Java8日期時間:LocalDateTime / LocalDate / LocalTime

一、簡介

Java 8通過發佈新的Date-Time API (JSR 310)來進一步加強對日期與時間的處理。

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

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

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

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

Java8引入了一套全新的日期時間處理API,新的API基於ISO標準日曆系統。
Java8 在 java.time 包下提供了很多新的 API。以下爲兩個比較重要的 API:

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

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

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

二、日期/時間初識

示例1:獲取當前日期/時間
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class DateTimeAPI {
    public static void main(String[] args) {
        LocalDate.now();       // 獲取當前日期 format: yyyy-MM-dd
        LocalTime.now();       // 獲取當前時間 format: HH:mm:ss
        LocalDateTime.now();   // 獲取當前日期時間 format: yyyy-MM-dd HH:mm:ss
    }
}

以上是對當前日期的獲取,比較三者的輸出(print console:)有什麼區別:

2020-04-20
11:27:01.570
2020-04-20T11:27:01.570
示例2: 構造指定日期/時間

調用工廠方法LocalDate.of()創建任意日期, 該方法需要傳入年、月、日做參數,返回對應的LocalDate實例。這個方法的好處是沒再犯老API的設計錯誤,比如年度起始於1900,月份是從0開始等等.

    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2000, 1, 1);
        LocalTime time = LocalTime.of(0,0,0);
        LocalDateTime dateTime = LocalDateTime.of(date, time);
        System.out.println("千禧年=" + dateTime);

        LocalDateTime dateTime1 = LocalDateTime.of(2000,1,1,0,0,0);
        System.out.println("千禧年=" + dateTime1);
    }

輸出結果如下:

千禧年=2000-01-01T00:00
千禧年=2000-01-01T00:00
示例3: 獲取年月日信息
    public static void main(String[] args) {
        LocalDate date = LocalDate.now();
        System.out.printf("年=%d, 月=%d, 日=%d", date.getYear(), date.getMonthValue(), date.getDayOfMonth());
        System.out.println();

        LocalTime time = LocalTime.now();
        System.out.printf("時=%d, 分=%d, 秒=%d, 毫秒=%d", time.getHour(), time.getMinute(), time.getSecond(), time.getNano());
        System.out.println();

        LocalDateTime dateTime = LocalDateTime.now();
        System.out.printf("年=%d, 月=%d, 日=%d, 時=%d, 分=%d, 秒=%d, 毫秒=%d", dateTime.getYear(), dateTime.getMonthValue(),
                dateTime.getDayOfMonth(), dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), dateTime.getNano());
    }

輸出結果如下:

=2020, 月=4, 日=20=11,=58,=1, 毫秒=800000000=2020, 月=4, 日=20,=11,=58,=1, 毫秒=801000000
示例4: 獲取當前日期時間戳
    public static void main(String[] args) {

        // 獲取秒數
        Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
        System.out.println("時間戳 秒數: " + second);

        // 獲取毫秒
        Long millisecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        System.out.println("時間戳 毫秒: " + millisecond);
    }

輸出結果如下:

時間戳 秒數: 1587367802
時間戳 毫秒: 1587367802085

三、比較與計算

示例1: 日期時間計算

Java8提供了新的plusXxx() 、minsXxx()方法用於計算日期時間增量值 / 減量值,替代了原來的add()方法。新的API將返回一個全新的日期時間示例,需要使用新的對象進行接收。

    public static void main(String[] args) {

        // 時間增量
        LocalTime time = LocalTime.now();
        System.out.println("當前時間 time: " + time);
        System.out.println("增加 2小時: " + time.plusHours(2));
        System.out.println("增加 2分鐘: " + time.plusMinutes(2));
        System.out.println("增加 2秒鐘: " + time.plusSeconds(2));
        System.out.println("增加 2毫秒: " + time.plusNanos(2));

        // 日期增量
        LocalDate date = LocalDate.now();
        System.out.println("當前日期 date: " + date);
        System.out.println("增加 2年: " + date.plusYears(2));
        System.out.println("增加 2月: " + date.plusMonths(2));
        System.out.println("增加 2周: " + date.plusWeeks(2));
        System.out.println("增加 2天: " + date.plusDays(2));
    }

輸出結果如下:

當前時間 time: 14:31:54.785
增加 2小時: 16:31:54.785
增加 2分鐘: 14:33:54.785
增加 2秒鐘: 14:31:56.785
增加 2毫秒: 14:31:54.785000002

當前日期 date: 2020-04-20
增加 2: 2022-04-20
增加 2: 2020-06-20
增加 2: 2020-05-04
增加 2: 2020-04-22
示例2: 日期時間比較

Java8提供了isAfter()、isBefore()用於判斷當前日期時間和指定日期時間的比較。

    public static void main(String[] args) {

        LocalDateTime dateTime = LocalDateTime.now();

        LocalDateTime dateTime1 = LocalDateTime.of(2000,1,1,0,0,0,0);
        if (dateTime.isAfter(dateTime1)) {
            System.out.println("千禧年已經過去了。。。。");
        }

        LocalDateTime dateTime2 = LocalDateTime.of(2050, 1,1,0,0,0,0);
        if (dateTime.isBefore(dateTime2)) {
            System.out.println("2050年還未到來!!!!!");
        }
    }

輸出結果如下:

千禧年已經過去了。。。。

2050年還未到來!!!!!
示例3: 比較兩個日期是否相等
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        LocalDate date = LocalDate.of(2018, 9, 24);
        System.out.println("日期是否相等=" + now.equals(date));
    }

輸出結果如下:

日期是否相等=false
示例4: 計算時間點之間的間隔
    public static void main(String[] args) {

        // 計算時間點之間的間隔
        LocalDateTime startTime = LocalDateTime.now();
        LocalDateTime endTime = startTime.plusHours(1).plusMinutes(30);
        Duration duration = Duration.between(startTime, endTime);

        Long day = duration.toDays();
        System.out.println("間隔天數: " + day);

        Long hour = duration.toHours();
        System.out.println("間隔小時: " + hour);

        Long minute = duration.toMinutes();
        System.out.println("間隔分鐘: " + minute);

        Long millis = duration.toMillis();
        System.out.println("間隔毫秒: " + millis);
    }

輸出結果如下:

間隔天數: 0
間隔小時: 1
間隔分鐘: 90
間隔毫秒: 5400000
示例5: 計算日期之間的間隔
    public static void main(String[] args) {

        // 計算日期之間的間隔
        LocalDate startDate = LocalDate.now();
        LocalDate endDate = startDate.plusMonths(1).plusDays(5);
        Period period = Period.between(startDate, endDate);

        int year = period.getYears();
        System.out.println("間隔年數: " + year);

        int month = period.getMonths();
        System.out.println("間隔月數: " + month);

        int day = period.getDays();
        System.out.println("間隔天數: " + day);
    }

輸出結果如下:

間隔年數: 0
間隔月數: 1
間隔天數: 5

四、時區

示例1: 創建帶有時區的日期時間(ZoneId)

Java 8不僅分離了日期和時間,也把時區分離出來了。現在有一系列單獨的類如ZoneId來處理特定時區,ZoneDateTime類來表示某時區下的時間。

    public static void main(String[] args) {

        // 上海時間
        ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai");
        ZonedDateTime shanghaiZonedDateTime = ZonedDateTime.now(shanghaiZoneId);

        // 東京時間
        ZoneId tokyoZoneId = ZoneId.of("Asia/Tokyo");
        ZonedDateTime tokyoZonedDateTime = ZonedDateTime.now(tokyoZoneId);

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println("上海時間: " + shanghaiZonedDateTime.format(formatter));
        System.out.println("東京時間: " + tokyoZonedDateTime.format(formatter));
    }

輸出結果如下:

上海時間: 2020-04-20 15:03:18
東京時間: 2020-04-20 16:03:18
示例2: 創建帶有時區的日期時間(UTC)
    public static void main(String[] args) {

        // 北京時間
        ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai"));
        ZonedDateTime zonedDateTime1 = LocalDateTime.now().atZone(ZoneId.of("UTC+8"));
        System.out.println("上海時間    : " + zonedDateTime);
        System.out.println("上海時間 UTC: " + zonedDateTime1);

        // 東京時間
        ZonedDateTime zonedDateTime2 = LocalDateTime.now().atZone(ZoneId.of("Asia/Tokyo"));
        ZonedDateTime zonedDateTime3 = LocalDateTime.now().atZone(ZoneId.of("UTC+9"));
        System.out.println("東京時間    : " + zonedDateTime2);
        System.out.println("東京時間 UTC: " + zonedDateTime3);
    }

輸出結果如下:

上海時間    : 2020-04-20T15:15:58.378+08:00[Asia/Shanghai]
上海時間 UTC: 2020-04-20T15:15:58.380+08:00[UTC+08:00]
東京時間    : 2020-04-20T15:15:58.380+09:00[Asia/Tokyo]
東京時間 UTC: 2020-04-20T15:15:58.381+09:00[UTC+09:00]

五、格式化

示例1: 使用預定義格式解析與格式化日期
    public static void main(String[] args) {

        // 解析日期
        String dateText = "20200420";
        LocalDate date = LocalDate.parse(dateText, DateTimeFormatter.BASIC_ISO_DATE);
        System.out.println("格式化後的日期: " + date);

        // 格式化日期
        dateText = date.format(DateTimeFormatter.BASIC_ISO_DATE);
        System.out.println("dateText: " + dateText);
    }

輸出結果如下:

格式化後的日期: 2020-04-20
dateText: 20200420
示例2: 日期和字符串的相互轉換
    public static void main(String[] args) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 日期時間轉字符串
        LocalDateTime now = LocalDateTime.now();
        String dateToStr = now.format(formatter);
        System.out.println("日期轉換成字符串: " + dateToStr);

        // 字符串轉日期時間
        String datetimeText = "1999-12-31 23:59:59";
        LocalDateTime strToDate = LocalDateTime.parse(datetimeText, formatter);
        System.out.println("字符串轉換成日期: " + strToDate);
    }

輸出結果如下:

日期轉換成字符串: 2020-04-20 15:50:45
字符串轉換成日期: 1999-12-31T23:59:59
示例3: 日期和時間戳的相互轉換
    public static void main(String[] args) {

        // 日期轉時間戳
        System.out.println(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
        System.out.println(LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        System.out.println(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());

        // 時間戳轉成日期
        Long instantL = 1587367830000L;
        Instant instant = Instant.ofEpochMilli(instantL);
        LocalDateTime instantToDate = LocalDateTime.ofInstant(instant, ZoneId.of("+8"));
        System.out.println("時間戳轉換成日期: " + instantToDate);

    }

輸出結果如下:

1587371801887
1587371801888
1587371801888
時間戳轉換成日期: 2020-04-20T15:30:30
示例4: 字符串和時間戳的相互轉換
    public static void main(String[] args) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 字符串轉時間戳
        String datetimeText = "2020-04-20 15:30:30";
        LocalDateTime strToDate = LocalDateTime.parse(datetimeText, formatter);
        System.out.println("字符串轉換成日期: " + strToDate);
        Long dateToInstant = strToDate.toInstant(ZoneOffset.of("+8")).toEpochMilli();
        System.out.println("日期轉換成毫秒時間戳: " + dateToInstant);

        // 時間戳轉換成字符串
        Long instant = 1587367830000L;
        LocalDateTime instantToDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(instant), ZoneId.of("+8"));
        System.out.println("時間戳轉換成日期: " + instantToDate);
        String dateToStr = formatter.format(instantToDate);
        System.out.println("日期轉換成字符串: " + dateToStr);

    }

輸出結果如下:

字符串轉換成日期: 2020-04-20T15:30:30
日期轉換成毫秒時間戳: 1587367830000

時間戳轉換成日期: 2020-04-20T15:30:30
日期轉換成字符串: 2020-04-20 15:30:30

六、時間矯正器

TemporalAdjuster : 時間校正器。它是一個函數式接口,只有一個方法 adjustInto。有時我們可能需要獲取例如:將日期調整到“下個週日”等操作。
TemporalAdjusters : 該類通過靜態方法提供了大量的常用TemporalAdjuster 的實現。

Temporal 是一個接口,LocalDateTime 、LocalDate 、 LocalTime 都是它的實現類,有實現方法 with(TemporalAdjuster adjuster)。

@FunctionalInterface
public interface TemporalAdjuster {
    Temporal adjustInto(Temporal temporal);
}
示例1: 時間矯正
    public static void main(String[] args) {

        // 接下來第一個週五
        LocalDateTime nextFriday = LocalDateTime.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
        System.out.println("接下來第一個週五: " + nextFriday);

        // 明年的感恩節(明年11月第四個星期四)
        LocalDate date = LocalDate.now().plusYears(1).withMonth(11)
                .with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY));
        System.out.println("明年感恩節: " + date);
    }

輸出結果如下:

接下來第一個週五: 2020-04-24T18:55:50.841
明年感恩節: 2021-11-25

注意: TemporalAdjusters.next(DayOfWeek day) 方法返回的是 接下來第一個週五,並不是我們一般理解的 下週五,比如說:今天 2020-01-13(週一),那麼返回的就是 2020-01-17 四天後的週五。

七、相關類說明

Instant 時間戳
Duration 持續時間、時間差
LocalDate 只包含日期,比如:2018-09-24
LocalTime 只包含時間,比如:10:32:10
LocalDateTime 包含日期和時間,比如:2018-09-24 10:32:10
Peroid 時間段
ZoneOffset 時區偏移量,比如:+8:00
ZonedDateTime 帶時區的日期時間
Clock 時鐘,可用於獲取當前時間戳
java.time.format.DateTimeFormatter 時間格式化類

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章