此前Java处理时间日期所使用的 Date 和 Calendar 被诟病不已,Calendar 的主要问题对象可变,而像时间和日期这样的类应该是不可变的,另外其概念模型也有不明确的地方,月份计算从0开始等等。java
JodaTime开源时间/日期库是很好的替代,另外Java8中也推出了新的java.time库,设计理念与JodaTime类似。git
Joda-Time 令时间和日期值变得易于管理、操做和理解。易于使用是 Joda 的主要设计目标。Joda-Time主类 DateTime 和JDK旧有类 Date 和 Calendar之间能够互相转换。从而保证了与JDK框架的兼容。安全
"盖将自其变者而观之,则天地曾不能以一瞬",Instant 就表明时间轴上的"一瞬",为保持和JDK一致,时间轴起点亦在1970年,单位为ms。app
Instant类的做用就围绕着时间轴上的绝对时间(long类型),提供了构造,修改,加减等方法。另外它也是DateTime类的构建方式之一框架
DateTime dateTime = new DateTime(new Instant()); DateTime dateTime = new Instant().toDateTime();
Interval表明一个Instant到下一个Instant的时间间隔,这个间隔是半开闭集合。即包括起始的一瞬,但并不包含结束的一瞬。ui
Instant表示时间轴上的一点,Interval则表示时间轴上一段区间。插件
Duration指用ms计量的一段持续时间。Duration虽然与Interval看似相似,但Duration的概念相对孤立,仅表示时间区间长度,与时间轴上的位置没有关系。线程
Duration 能够参与两个Instant之间的运算。设计
$$instant + duration = instant$$code
Period表示用具体域(如年/月/日/时/分/秒/毫秒/星期)计量的一段时间,如3天,2小时等。这亦是与时间轴无关的一个概念,与Duration的不一样只是在计量方式上。Duration与时区和历法无关,Period则与之相关。
Period概念之因此重要,能够想象在某年1月和7月的基础上分别加 数值为1月的Period,则两者所需的具体时间ms值是不相同的。Period 是描述时间间隔长度的另外一种方式。
由上可见Period是与Duration同级别的概念,亦能够参与Instant的运算。
$$instant + period = instant$$
另外也能够由 Interval 得到相应的Period和Duration。
\\DateTime now,then; Interval interval = new Interval(now,then); Period period = interval.toPeriod(); Duration duration = interval.toDuration();
Chronology表明历法,负责具体时间日期的计算,虽然做用上居于核心位置,但在Api上却容易被忽视。
使用者每每不须要指定具体的历法,感觉不到其存在。历法类是单例实现,默认实现是 ISOChronology。
表明时区。能够用来构建历法类。
DateTimeZone zone = DateTimeZone.forID("Europe/London");
Partial表示日期时间的一部分,是本地化时间,与时区无关。
例如一个TimeDate指定为2015年11月9日11时11分11秒,则在时间轴上为肯定一点;若省略掉年份时间信息,只取11月9日,则在时间轴上则对应多点,表示历年来11月9日这一天的任意时间点。其实现类有下列几种:
LocalDate
LocalTime
LocalDateTime
YearMonth
MonthDay
由概念可知 为Partial指定其缺失域和时区信息,能够将其在时间轴上的位置肯定下来。
$$partial + missing fields + time zone = instant$$
一个日期时间的具体域包括8个:年/月/日/时/分/秒/毫秒 + 星期,分别用不一样字母表示。
对于DateTime/LocalDate能够采用直接构造格式化
DateTime dt = new DateTime(); String a = dt.toString(); String b = dt.toString("dd:MM:yy"); String c = dt.toString("EEE", Locale.FRENCH);
固然这不过是个障眼法,真实的格式化工做由DateTimeFormatter完成,标准格式类由ISODateTimeFormat提供。
DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
若是要自定义格式化,须要建立DateTimeFormatter类
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyyMMddEE"); DateTimeFormatter germanFmt = fmt.withLocale(Locale.GERMAN); now.toString(fmt);
更详细的流式构造
DateTimeFormatter fmt = new DateTimeFormatterBuilder() .appendDayOfMonth(2) .appendLiteral('-') .appendMonthOfYearShortText() .appendLiteral('-') .appendTwoDigitYear(1956) // pivot = 1956 .toFormatter();
Joda 类具备不可变性,所以它们的实例没法被修改。不可变类的一个优势就是它们是线程安全的。
DateTime 是Joda-Time的核心类,表明时间日期值,其构造方法多样,便可以使用各类对象构造,亦可使用基本类型构造,核心在于可以肯定在时间轴上的位置。能够参与构造的对象包括:
Date - a JDK instant
Calendar - a JDK calendar
String - in ISO8601 format
Long - in milliseconds
any Joda-Time date-time class
int[]
Joda-Time支持Date/Calendar构造,保证了与JDK的兼容.
有了DateTime对象能够用get方法获取从年到毫秒数的具体信息。以年为示例以下:
int year = dateTime.getYear(); int yearincenture = dateTime.getYearOfCentury(); int yearofera = dateTime.getYearOfEra();
DateTime还分别提供了一个内部类 Property,Property的功能更增强大。DateTime的属性有多种类型,并支持修改。
Property p = now.year(); //年份 boolean isleap = p.isLeap(); //判断是不是闰年 String name = p.getAsText(); Property p = now.monthOfYear();//当年中的月份 p.setCopy(6); //将月份改成六月 Property p = now.dayOfMonth(); //当月中的天数 p.setCopy(9); //将天数改成当月9号 Property p = now.dayOfWeek(); //当星期的天数 p.setCopy(1); //将天数改成星期1
DateTime对时间日期的计算主要针对7种域提供 with/plus/minus 三种方法。
DateTime dt = dateTime.plusYears(1);
陷阱:由于不可变性,DateTime修改以后获得的是一个新DateTime对象,这一点能够经过hashcode来验证,所以必须给这个新对象赋一个引用。
Joda-Time支持多种历法和时区,其中默认历法是ISO标准历法,默认时区与JDK相同。Joda-Time使用插件化(pluggable)机制,其中时区类被设计成历法类的一个依赖。
Chronology类表示对历法抽象
DateTimeZone类表示对时区的抽象
//1.指定历法和时区 DateTimeZone zone = DateTimeZone.forID("Asia/Tokyo"); Chronology gregorianJuian =GJChronology.getInstance(zone); DateTime daTime = new DateTime(gregorianJuian);
经过
DateTimeZone.getAvailableIDs()
能够获取所有时区名称.
LocalDate能够经过DateTime获取,亦能够自行构建。
LocalDate localDate= dateTime.toLocalDate(); LocalDate localDate = new LocalDate(2009, 9, 6);
LocalDate now = new LocalDate(); //DateTime now = new DateTime(); now.toString(); int year = now.getYear(); int month = now.getMonthOfYear(); int day = now.getDayOfWeek();
LocalDate now = new LocalDate(2015,11,9); DateTime now = new DateTime(2015,11,9,7,15);
LocalDate now = new LocalDate(2015,11,9); LocalDate then = new LocalDate(2015,11,9); now.isEqual(then); now.isBefore(then); now.isAfter(then);
LocalDate now = new LocalDate(); then = now.plusYears(1); then = now.minusYears(1); then = now.withYear(2016);
MonthDay birth = new MonthDay(11,9); LocalDate now = new LocalDate(); MonthDay today = new MonthDay(now); birth.isEqual(today);
then = now.plusWeeks(1); then = now.plusMonths(1); then = now.plusDays(1);
Period period = new Period(now,then); System.out.println(period.getDays()); System.out.println(period.getYears()); System.out.println(period.getMonths());
LocalDate now = new LocalDate(); LocalDate lastDayOfPreviousMonth = now.minusMonths(1). dayOfMonth().withMaximumValue();
dayOfMonth方法返回了属性(property)。
DateTime now = new DateTime(); now = now.monthOfYear().setCopy(11) .dayOfMonth().withMinimumValue()//得到当月1号 .plusDays(6) .dayOfWeek().setCopy(1);//得到星期一
当获得本月1号后,使用dayOfWeek()将得到1号所在的星期,直接使用setCopy(1)指定有可能会回到上个月月末的星期1.
所以使用plusDays(6)做预处理,即便用1当月7号所在星期的星期1。
DateTime now = new DateTime(); DateTime then = now.plusYears(5) .monthOfYear() .setCopy(2) .dayOfMonth() .withMaximumValue();