Java學習86:LocalDateTime

在這裏插入圖片描述
從Java8開始,java.time包提供了新的API,主要涉及到的類型:

  • 本地的日期和時間:LocalDateTime,LocalDate,LocalTime;
  • 帶時區的日期和時間:ZonedDateTime;
  • 時刻:Instant;
  • 時區:ZoneId,ZoneOffset;
  • 時間間隔:Duration;

以及一套新的用於取代SimpleDateFormat的格式化類型DateTimeFormatter

和舊的API相比,新API嚴格區分了時刻、本地日期、本地時間和帶時區的日期時間,並且,對日期和時間進行運算更加方便。

此外,新API修正了舊API不合理的常量設計:

Month的範圍用1~12表示1月到12月;
Week的範圍用1~7表示週一到週日。
最後,新API的類型幾乎全部是不變類型(和String類似),可以放心使用不必擔心被修改。

LocalDateTime
我們首先來看最常用的LocalDateTime,它表示一個本地日期和時間:

import java.time.*;


public class Demo02 {
    public static void main(String[] args) {
        LocalDate d=LocalDate.now();// 當前日期
        LocalTime t=LocalTime.now();// 當前時間
        LocalDateTime dt=LocalDateTime.now();// 當前日期和時間
        System.out.println(d);
        System.out.println(t);
        System.out.println(dt);
    }
}

本地日期和時間通過now()獲取到的總是以當前默認時區返回的,和舊API不同,LocalDateTime、LocalDate和LocalTime默認嚴格按照ISO 8601規定的日期和時間格式進行打印。

上述代碼其實有一個小問題,在獲取3個類型的時候,由於執行一行代碼總會消耗一點時間,因此,3個類型的日期和時間很可能對不上(時間的毫秒數基本上不同)。爲了保證獲取到同一時刻的日期和時間,可以改寫如下:

LocalDateTime dt = LocalDateTime.now(); // 當前日期和時間
LocalDate d = dt.toLocalDate(); // 轉換到當前日期
LocalTime t = dt.toLocalTime(); // 轉換到當前時間

反過來,通過指定的日期和時間創建LocalDateTime可以通過of()方法:

// 指定日期和時間:
LocalDate d2 = LocalDate.of(2019, 11, 30); // 2019-11-30, 注意11=11月
LocalTime t2 = LocalTime.of(15, 16, 17); // 15:16:17
LocalDateTime dt2 = LocalDateTime.of(2019, 11, 30, 15, 16, 17);
LocalDateTime dt3 = LocalDateTime.of(d2, t2);

因爲嚴格按照ISO 8601的格式,因此,將字符串轉換爲LocalDateTime就可以傳入標準格式:

LocalDateTime dt = LocalDateTime.parse("2019-11-19T15:16:17");
LocalDate d = LocalDate.parse("2019-11-19");
LocalTime t = LocalTime.parse("15:16:17");

注意ISO 8601規定的日期和時間分隔符是T。標準格式如下:

  • 日期:yyyy-MM-dd
  • 時間:HH:mm:ss
  • 帶毫秒的時間:HH:mm:ss.SSS
  • 日期和時間:yyyy-MM-dd’T’HH:mm:ss
  • 帶毫秒的日期和時間:yyyy-MM-dd’T’HH:mm:ss.SSS

DateTimeFormatter
如果要自定義輸出的格式,或者要把一個非ISO 8601格式的字符串解析成LocalDateTime,可以使用新的DateTimeFormatter:

import java.time.*;
import java.time.format.DateTimeFormatter;


public class Demo02 {
    public static void main(String[] args) {
        // 自定義格式化
        DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        System.out.println(dtf.format(LocalDateTime.now()));
        // 自定義格式解析
        LocalDateTime dt2=LocalDateTime.parse("2020/03/04 20:57:33",dtf);
        System.out.println(dt2);
    }
}

LocalDateTime提供了對日期和時間進行加減的非常簡單的鏈式調用:

import java.time.*;
import java.time.format.DateTimeFormatter;


public class Demo02 {
    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
        System.out.println(dt);
        // 加5天減3小時:
        LocalDateTime dt2 = dt.plusDays(5).minusHours(3);
        System.out.println(dt2); // 2019-10-31T17:30:59
        // 減1月:
        LocalDateTime dt3 = dt2.minusMonths(1);
        System.out.println(dt3); // 2019-09-30T17:30:59
    }
}

注意到月份加減會自動調整日期,例如從2019-10-31減去1個月得到的結果是2019-09-30,因爲9月沒有31日。

對日期和時間進行調整則使用withXxx()方法,例如:withHour(15)會把10:11:12變爲15:11:12:

  • 調整年:withYear()
  • 調整月:withMonth()
  • 調整日:withDayOfMonth()
  • 調整時:withHour()
  • 調整分:withMinute()
  • 調整秒:withSecond()

示例代碼如下:

import java.time.*;
import java.time.format.DateTimeFormatter;


public class Demo02 {
    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
        System.out.println(dt);
        // 日期變爲31日:
        LocalDateTime dt2 = dt.withDayOfMonth(31);
        System.out.println(dt2); // 2019-10-31T20:30:59
        // 月份變爲9:
        LocalDateTime dt3 = dt2.withMonth(9);
        System.out.println(dt3); // 2019-09-30T20:30:59
    }
}

同樣注意到調整月份時,會相應地調整日期,即把2019-10-31的月份調整爲9時,日期也自動變爲30。

實際上,LocalDateTime還有一個通用的with()方法允許我們做更復雜的運算。例如:

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;


public class Demo02 {
    public static void main(String[] args) {
        // 本月第一天0:00時刻:
        LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
        System.out.println(firstDay);

        // 本月最後1天:
        LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
        System.out.println(lastDay);

        // 下月第1天:
        LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println(nextMonthFirstDay);

        // 本月第1個週一:
        LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
        System.out.println(firstWeekday);
    }
}

對於計算某個月第1個週日這樣的問題,新的API可以輕鬆完成。

要判斷兩個LocalDateTime的先後,可以使用isBefore()、isAfter()方法,對於LocalDate和LocalTime類似:

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;


public class Demo02 {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime target = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
        System.out.println(now.isBefore(target));
        System.out.println(LocalDate.now().isBefore(LocalDate.of(2019, 11, 19)));
        System.out.println(LocalTime.now().isAfter(LocalTime.parse("08:15:00")));
    }
}

注意到LocalDateTime無法與時間戳進行轉換,因爲LocalDateTime沒有時區,無法確定某一時刻。後面我們要介紹的ZonedDateTime相當於LocalDateTime加時區的組合,它具有時區,可以與long表示的時間戳進行轉換。

Duration和Period
Duration表示兩個時刻之間的時間間隔。另一個類似的Period表示兩個日期之間的天數:

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;


public class Demo02 {
    public static void main(String[] args) {
        LocalDateTime start = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
        LocalDateTime end = LocalDateTime.of(2020, 1, 9, 19, 25, 30);
        Duration d = Duration.between(start, end);
        System.out.println(d); // PT1235H10M30S

        Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
        System.out.println(p); // P1M21D
    }
}

注意到兩個LocalDateTime之間的差值使用Duration表示,類似PT1235H10M30S,表示1235小時10分鐘30秒。而兩個LocalDate之間的差值用Period表示,類似P1M21D,表示1個月21天。

Duration和Period的表示方法也符合ISO 8601的格式,它以P…T…的形式表示,P…T之間表示日期間隔,T後面表示時間間隔。如果是PT…的格式表示僅有時間間隔。利用ofXxx()或者parse()方法也可以直接創建Duration:

Duration d1 = Duration.ofHours(10); // 10 hours
Duration d2 = Duration.parse("P1DT2H3M"); // 1 day, 2 hours, 3 minutes

有的童鞋可能發現Java 8引入的java.timeAPI。怎麼和一個開源的Joda Time很像?難道JDK也開始抄襲開源了?其實正是因爲開源的Joda Time設計很好,應用廣泛,所以JDK團隊邀請Joda Time的作者Stephen Colebourne共同設計了java.timeAPI。

小結
Java 8引入了新的日期和時間API,它們是不變類,默認按ISO 8601標準格式化和解析;

使用LocalDateTime可以非常方便地對日期和時間進行加減,或者調整日期和時間,它總是返回新對象;

使用isBefore()和isAfter()可以判斷日期和時間的先後;

使用Duration和Period可以表示兩個日期和時間的“區間間隔”。

謝謝觀看

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