JodaTime開源時間/日期庫

此前Java處理時間日期所使用的 Date 和 Calendar 被詬病不已,Calendar 的主要問題對象可變,而像時間和日期這樣的類應該是不可變的,另外其概念模型也有不明確的地方,月份計算從0開始等等。java

JodaTime開源時間/日期庫是很好的替代,另外Java8中也推出了新的java.time庫,設計理念與JodaTime類似。git

Joda-Time 令時間和日期值變得易於管理、操做和理解。易於使用是 Joda 的主要設計目標。Joda-Time主類 DateTime 和JDK舊有類 Date 和 Calendar之間能夠互相轉換。從而保證了與JDK框架的兼容。安全

1.JodaTime中的時間日期概念


1.1 Instant

"蓋將自其變者而觀之,則天地曾不能以一瞬",Instant 就表明時間軸上的"一瞬",爲保持和JDK一致,時間軸起點亦在1970年,單位爲ms。app

Instant類的做用就圍繞着時間軸上的絕對時間(long類型),提供了構造,修改,加減等方法。另外它也是DateTime類的構建方式之一框架

DateTime dateTime = new DateTime(new Instant());
DateTime dateTime = new Instant().toDateTime();

1.2 Interval

Interval表明一個Instant到下一個Instant的時間間隔,這個間隔是半開閉集合。即包括起始的一瞬,但並不包含結束的一瞬。ui

Instant表示時間軸上的一點,Interval則表示時間軸上一段區間。插件

1.3 Duration

Duration指用ms計量的一段持續時間。Duration雖然與Interval看似相似,但Duration的概念相對孤立,僅表示時間區間長度,與時間軸上的位置沒有關係。線程

Duration 能夠參與兩個Instant之間的運算。設計

$$instant + duration = instant$$code

1.4 Period

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

1.5 Chronology

Chronology表明曆法,負責具體時間日期的計算,雖然做用上居於核心位置,但在Api上卻容易被忽視。
使用者每每不須要指定具體的歷法,感覺不到其存在。曆法類是單例實現,默認實現是 ISOChronology。

1.6 TimeZone

表明時區。能夠用來構建曆法類。

DateTimeZone zone = DateTimeZone.forID("Europe/London");

1.7 Partial

Partial表示日期時間的一部分,是本地化時間,與時區無關。
例如一個TimeDate指定爲2015年11月9日11時11分11秒,則在時間軸上爲肯定一點;若省略掉年份時間信息,只取11月9日,則在時間軸上則對應多點,表示歷年來11月9日這一天的任意時間點。其實現類有下列幾種:

  • LocalDate

  • LocalTime

  • LocalDateTime

  • YearMonth

  • MonthDay

由概念可知 爲Partial指定其缺失域和時區信息,能夠將其在時間軸上的位置肯定下來。
$$partial + missing fields + time zone = instant$$

1.8 格式化

一個日期時間的具體域包括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();

1.9 不可變性

Joda 類具備不可變性,所以它們的實例沒法被修改。不可變類的一個優勢就是它們是線程安全的。

2 DateTime使用

2.1 構造方法

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的兼容.

2.2.獲取具體信息和屬性使用

有了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

2.3.日期計算和不可變性(immutable)

DateTime對時間日期的計算主要針對7種域提供 with/plus/minus 三種方法。

DateTime dt = dateTime.plusYears(1);

陷阱:由於不可變性,DateTime修改以後獲得的是一個新DateTime對象,這一點能夠經過hashcode來驗證,所以必須給這個新對象賦一個引用。

2.4曆法和時區

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()能夠獲取所有時區名稱.

2.5 本地時間

LocalDate能夠經過DateTime獲取,亦能夠自行構建。

LocalDate localDate= dateTime.toLocalDate();

LocalDate localDate = new LocalDate(2009, 9, 6);

3. 時間處理示例

3.1 獲取當前日期和年月日

LocalDate now = new LocalDate();
//DateTime now = new DateTime();

now.toString();
int year = now.getYear();
int month = now.getMonthOfYear();
int day = now.getDayOfWeek();

3.2 獲取某個特定的日期

LocalDate now = new LocalDate(2015,11,9);
DateTime now = new DateTime(2015,11,9,7,15);

3.3 判斷兩個日期的關係

LocalDate now = new LocalDate(2015,11,9);
LocalDate then = new LocalDate(2015,11,9);
now.isEqual(then);
now.isBefore(then);
now.isAfter(then);

3.4 修改/添加/減小日期

LocalDate now = new LocalDate();
then = now.plusYears(1);
then = now.minusYears(1);
then = now.withYear(2016);

3.5 檢查重複日期,如生日

MonthDay birth = new MonthDay(11,9);

LocalDate now = new LocalDate();
MonthDay today = new MonthDay(now);

birth.isEqual(today);

3.6 獲取1周/月/往後的日期

then = now.plusWeeks(1);
then = now.plusMonths(1);
then = now.plusDays(1);

3.7 兩個日期之間包含多少天,多少個月

Period period = new Period(now,then);
System.out.println(period.getDays());
System.out.println(period.getYears());
System.out.println(period.getMonths());

3.8 得到上個月最後一天

LocalDate now = new LocalDate();
LocalDate lastDayOfPreviousMonth = now.minusMonths(1).
            dayOfMonth().withMaximumValue();

dayOfMonth方法返回了屬性(property)。

3.9 計算 11 月中第一個星期一

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。

3.10 計算五年後的第二個月的最後一天:

DateTime now = new DateTime();
DateTime then = now.plusYears(5)
        .monthOfYear()
        .setCopy(2)
        .dayOfMonth()
        .withMaximumValue();
相關文章
相關標籤/搜索