介紹 Java 8 Date/Time API
1.概述
java8 引入新的日期時間API,為了解決原來
java.util.Date , java.util.Calendar
的一些缺陷。
本文首先介紹原Date,Calendar API的問題,然後來說明java8 Date , Time API是如何解決的。
同時,我們也了解java8中java.time包中一些常用類及其API,如
.
LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Period, Duration
2.原Date/Time APIs問題
- 線程安全 —— Date和Calendar類不是線程安全的,在調試并發程式時讓人頭痛,開發者必須自己寫額外的代碼處理線程安全。相反,java8中引入新的Date和Time API是不可變且線程安全的,是以開發者無需擔心并發問題。
- 簡單易懂 —— Date和Calendar類
- 時區和時間 —— 使用原來的API開發者不得不寫額外邏輯處理時區邏輯,是以新的API可以使用Local、ZoneDate/TimeAPI處理時區問題。
3.使用LocalDate,LocalTime,LocalDateTime類API
最常用的類是
LocalDate, LocalTime, LocalDateTime
,見名思意,它們分别表示目前上下文的本地日期/時間。
這些類主要用在時區無需顯示指定的場景。本節讓介紹其最常用的API.
3.1.使用LocalDate
LocalDate 表示ISO格式日期 (yyyy-MM-dd),沒有時間。
可以用來存儲日期,如生日,付款日期等。目前日期可以用系統時鐘建立:
LocalDate localDate = LocalDate.now();
LocalDate表示特定年,月,日,可以使用of方法或parse方法建立。舉例:
LocalDate.of(2015, 02, 20);
LocalDate.parse("2015-02-20");
LocalDate提供多個工具方法可以獲得不同資訊。讓我們快速了解下。
下面代碼片段獲得本地日期,然後加一天:
LocalDate tomorrow = LocalDate.now().plusDays(1);LocalDate tomorrow = LocalDate.now().plusDays(1);
下面代碼獲得目前日期,然後減去一個月。注意其枚舉時間機關參數:
LocalDate previousMonthSameDay = LocalDate.now().minus(1, ChronoUnit.MONTHS);
下面兩行代碼解析“2016-06-12”,然後擷取星期幾和月中那一天。注意其傳回值,第一個為DayOfWeek,第二個是int。
DayOfWeek sunday = LocalDate.parse("2016-06-12").getDayOfWeek();
int twelve = LocalDate.parse("2016-06-12").getDayOfMonth();
也可以輕松測試閏年,示例代碼如下:
boolean leapYear = LocalDate.now().isLeapYear();
兩個日期比較,before和after很便捷:
boolean notBefore = LocalDate.parse("2016-06-12").isBefore(LocalDate.parse("2016-06-11"));
boolean isAfter = LocalDate.parse("2016-06-12").isAfter(LocalDate.parse("2016-06-11"));
可以從日期中擷取邊界。下面示例分别擷取當天的開始及當月的第一天。
LocalDateTime beginningOfDay = LocalDate.parse("2016-06-12").atStartOfDay();
LocalDate firstDayOfMonth = LocalDate.parse("2016-06-12").with(TemporalAdjusters.firstDayOfMonth());
下面我們看看LocalTime類。
3.2 使用LocalTime
LocalTime表示時間,沒有日期。與LocalDate類似,LocalTime能通過of和parse方法從系統時鐘建立。
下面通過示例了解下常用API.
LocalTime now = LocalTime.now();
下面代碼示例,我們通過解析字元串建立LocalTime表示06:30AM。
LocalTime sixThirty = LocalTime.parse("06:30");
也可以使用工廠方法建立LocalTime。示例如下:
LocalTime sixThirty = LocalTime.of(6, 30);
通過plus方法增加一小時。servenThirth為07:30AM.
LocalTime sevenThirty = LocalTime.parse("06:30").plus(1, ChronoUnit.HOURS);
提供便捷的方法擷取時間單元。
int six = LocalTime.parse("06:30").getHour();
比較日期,是否晚于或早于特定時間。示例代碼:
boolean isbefore = LocalTime.parse("06:30").isBefore(LocalTime.parse("07:30"));
LocalTime類中通過常量可以獲得max,min,noon時間。在執行資料庫時間範圍查詢時非常有用。下面代碼表示23:59:59.99.
LocalTime maxTime = LocalTime.MAX
下面看看LocalDateTime類。
3.3使用LocalDateTime
LocalDateTime表示日期和時間組合。這時最常用的類,提供了豐富的API.
通過從系統時鐘建立:
LocalDateTime.now();
通過工廠方法of或parse建立:
LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);
LocalDateTime.parse("2015-02-20T06:30:00");
提供了工具API實作增加或減少單元日期或時間。如年,月,日,分鐘等。
localDateTime.plusDays(1);
localDateTime.minusHours(2);
通過get方法擷取時間單元。
localDateTime.getMonth();
4.使用ZonedDateTime
java8提供了ZonedDateTime類,用于處理帶時區的日期和時間。ZoneId表示不同的時區。大約有40不同的時區。
下面代碼建立一個時區:
ZoneId zoneId = ZoneId.of("Europe/Paris");
擷取所有時區集合:
Set allZoneIds = ZoneId.getAvailableZoneIds();
把LocalDateTime轉換成特定的時區:
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);
ZonedDateTime提供parse方法獲得特定時區的dateTime:
ZonedDateTime.parse("2015-05-03T10:15:30+01:00[Europe/Paris]");
另外和時區一起使用的類是OffsetDateTime類,OffsetDateTime是不變的,表示date-time偏移,存儲所有日期和時間字段,精确至納秒,從UTC/Greenwich計算偏移。
可以通過
OffSetDateTime
執行個體結合
ZoneOffset
建立LocalDateTime。
LocalDateTime localDateTime = LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);
我們建立ZoneOffset,增加兩個小時:
ZoneOffset offset = ZoneOffset.of("+02:00");
OffsetDateTime offSetByTwo = OffsetDateTime.of(localDateTime, offset);
先localDateTime的值為 2015-02-20 06:30 +02:00.
下面我們看如何通過Period和Duration類修改日期和時間值。
5.使用Period 和 Duration
Period類代表年,月,日的時間量,Duration類代表秒和納秒的數量。
5.1使用Period
Period類主要用來修改給定日期值,或擷取兩日期之間的內插補點:
LocalDate initialDate = LocalDate.parse("2007-05-10");
通過Period可以操作日期:
LocalDate finalDate = initialDate.plus(Period.ofDays(5));
Period類有不同的get方法,如getYears,getMonths,getDays可以獲得不同周期值。下面代碼傳回5,反應日期間隔:
int five = Period.between(finalDate, initialDate).getDays();
也可以通過ChronoUnit實作通用功能:
int five = ChronoUnit.DAYS.between(initialDate , initialDate);
使用Duration
與Period類似, Duration類用于處理時間, 下面代碼建立LocalTime然後增加30秒。
LocalTime initialTime = LocalTime.of(6, 30, 0);
LocalTime finalTime = initialTime.plus(Duration.ofSeconds(30));
兩個時間之間間隔通過Duration類獲得時間單元。
int thirty = Duration.between(finalTime, initialTime).getSeconds();
通用可以通過ChronoUnit 類獲得。
int thirty = ChronoUnit.SECONDS.between(finalTime, initialTime);
相容原Date and Calendar
java8已經提供了toInstant方法,可以轉換Date和Calendar執行個體至新的DateTime。
下面是原Date類中新增的方法:
public static Date from(Instant instant) {
try {
return new Date(instant.toEpochMilli());
} catch (ArithmeticException ex) {
throw new IllegalArgumentException(ex);
}
}
public Instant toInstant() {
return Instant.ofEpochMilli(getTime());
}
示例轉換:
LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault());
也可以通過Epoch時間建構LocalDateTime,Epoch指的是一個特定的時間:1970-01-01 00:00:00 UTC。
結果為2016-06-13T11:34:50:
LocalDateTime.ofEpochSecond(1465817690, 0, ZoneOffset.UTC);
7.Date and Time 格式化
java8提供了非常簡單的方式格式化日期和時間:
LocalDateTime localDateTime = LocalDateTime.of(2015, Month.JANUARY, 25, 6, 30);
下面代碼傳入一個ISO日期格式去格式化一個本地時間,結果為2015-01-25:
LocalDate localDate = localDateTime.format(DateTimeFormatter.ISO_DATE);
DateTimeFormatter 類提供了多個标準格式選項。也支援自定義的格式。下面示例代碼結果為2015/01/25:
localDateTime.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
也可以通過格式樣式格式化,如SHORT, LONG or MEDIUM 。下面代碼輸出為25-Jan-2015 06:30:00:
localDateTime
.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
.withLocale(Locale.UK);
總結
java8提供了豐富的API操作日期時間,相比較原來的API,使用更容易更便捷。如果使用jdk較低版本,可以使用joda庫實作,本來java8API就是由joda提供。joda可以通過maven引入:
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.4</version>
</dependency>