Like Share Discussion Bookmark Smile

J.J. Huang   2020-04-10   Java   瀏覽次數:

Java 8 | Date/Time API(JSR 310)

Java 8引入了新的Date-Time API(JSR 310)來改進時間、日期的處理。時間和日期的管理一直是最令Java開發者痛苦的問題。java.util.Date和後來的java.util.Calendar一直沒有解決這個問題(甚至令開發者更加迷茫)。

因為上面這些原因,誕生了第三方庫Joda-Time,可以替代Java的時間管理APIJava 8中新的時間和日期管理API深受Joda-Time影響,並吸收了很多Joda-Time的精華。新的java.time包包含了所有關於日期、時間、時區、Instant(跟日期類似但是精確到納秒)、duration(持續時間)和時鐘操作的類。新設計的API認真考慮了這些類的不變性(從java.util.Calendar吸取的教訓),如果某個實例需要修改,則返回一個新的對象。


在舊版的Java中,日期時間 API存在諸多問題,其中有:

  • 非線程安全:java.util.Date是非線程安全的,所有的日期類都是可變的,這是Java日期類最大的問題之一。

  • 設計很差:Java的日期/時間類的定義並不一致,在java.utiljava.sql的包中都有日期類,此外用於格式化和解析的類在java.text包中定義。java.util.Date同時包含日期和時間,而java.sql.Date僅包含日期,將其納入java.sql包並不合理。另外這兩個類都有相同的名字,這本身就是一個非常糟糕的設計。

  • 時區處理麻煩:日期類並不提供國際化,沒有時區支持,因此Java引入了java.util.Calendarjava.util.TimeZone類,但他們同樣存在上述所有的問題。

Java 8java.time包下提供了很多新的API。以下為兩個比較重要的API

  • Local(本地):簡化了日期時間的處理,沒有時區的問題。

  • Zoned(時區):通過制定的時區處理日期時間。

新的java.time包涵蓋了所有處理日期,時間,日期/時間,時區,時刻(instants),過程(during)與時鐘(clock)的操作。

Clock

Clock類使用時區來返回當前的納秒時間和日期。
Clock可以替代System.currentTimeMillis()TimeZone.getDefault()

1
2
3
4
// Get the system clock as UTC offset 
final Clock clock = Clock.systemUTC();
System.out.println(clock.instant());
System.out.println(clock.millis());

輸出的結果為:(我目前時區為GMT+8[Asia/Taipei])

1
2
2020-04-10T13:38:58.906Z
1586612338962

LocalDate / LocalTime

LocalDate僅僅包含ISO-8601日曆系統中的日期部分;LocalTime則僅僅包含該日曆系統中的時間部分。這兩個類的對像都可以使用Clock對象構建得到。
LocalDate/LocalTimeLocalDateTime類可以在處理時區不是必須的情況。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Get the system clock as UTC offset 
final Clock clock = Clock.systemUTC();

// Get the local date and local time
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );

System.out.println( date );
System.out.println( dateFromClock );

// Get the local date and local time
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );

System.out.println( time );
System.out.println( timeFromClock );

輸出的結果為:(我目前時區為GMT+8[Asia/Taipei])

1
2
3
4
2020-04-10
2020-04-10
21:43:12.498
13:43:12.498

LocalDateTime

LocalDateTime類包含了LocalDateLocalTime的信息,但是不包含ISO-8601日曆系統中的時區信息。
關於於LocalDate和LocalTime的例子可以參考此篇文章

1
2
3
4
5
6
7
8
9
// Get the system clock as UTC offset 
final Clock clock = Clock.systemUTC();

// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );
System.out.println( datetimeFromClock );

輸出的結果為:(我目前時區為GMT+8[Asia/Taipei])

1
2
2020-04-10T21:45:35.622
2020-04-10T13:45:35.623

ZoneDateTime

如果你需要特定時區的data/time信息,則可以使用ZoneDateTime,它保存有ISO-8601日期系統的日期和時間,而且有時區信息。

1
2
3
4
5
6
7
8
9
10
11
// Get the system clock as UTC offset 
final Clock clock = Clock.systemUTC();

// Get duration between two dates// Get the zoned date/time
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "Asia/Taipei" ) );

System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );

輸出的結果為:(我目前時區為GMT+8[Asia/Taipei])

1
2
3
2020-04-10T21:48:27.269+08:00[Asia/Taipei]
2020-04-10T13:48:27.269Z
2020-04-10T21:48:27.269+08:00[Asia/Taipei]

Duration

Duration類,它持有的時間精確到秒和納秒。這使得我們可以很容易得計算兩個日期之間的不同,

1
2
3
4
5
6
7
// Get duration between two dates
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );

final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );

註:是import java.time.Duration;。

輸出的結果為:(我目前時區為GMT+8[Asia/Taipei])

1
2
Duration in days: 365
Duration in hours: 8783

註:以上參考了
Stream 與平行化
Java 8 Lambda新語法,簡化程式,增強效能
Java 8 新特性
Java 8的新特性—终极版
现代化 Java - Java8 指南