此前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();