1. 程式人生 > >JDK 1.8 完整日期時間Api (文末附示例)

JDK 1.8 完整日期時間Api (文末附示例)

一、背景

jdk 1.8 之前, Java 時間使用java.util.Datejava.util.Calendar 類。

Date today = new Date();
System.out.println(today);
    
 // 轉為字串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String todayStr = sdf.format(today);
System.out.println(todayStr);

Date 的幾個問題:

  1. 如果不格式化,Date打印出的日期可讀性差;
  2. 可以使用 SimpleDateFormat
    對時間進行格式化,但 SimpleDateFormat 是執行緒不安全的(阿里巴巴開發手冊中禁用static修飾SimpleDateFormat);
  3. Date對時間處理比較麻煩,比如想獲取某年、某月、某星期,以及 n 天以後的時間,如果用Date來處理的話真是太難了,並且 Date 類的 getYear()getMonth() 這些方法都被棄用了;

二、JDK 1.8 新的日期時間型別

Java8引入的新的一系列API,對時間日期的處理提供了更好的支援,清楚的定義了時間日期的一些概念,比如說,瞬時時間(Instant),持續時間(duration),日期(date),時間(time),時區(time-zone

)以及時間段(Period)。

  1. LocalDate:不包含時間的日期,比如2019-10-14。可以用來儲存生日,週年紀念日,入職日期等。
  2. LocalTime:與LocalDate想對照,它是不包含日期的時間。
  3. LocalDateTime:包含了日期及時間,沒有偏移資訊(時區)。
  4. ZonedDateTime:包含時區的完整的日期時間,偏移量是以UTC/格林威治時間為基準的。
  5. Instant:時間戳,與System.currentTimeMillis()類似。
  6. Duration:表示一個時間段。
  7. Period:用來表示以年月日來衡量一個時間段。
  8. DateTimeFormatter:新的日期解析格式化類。

2.1 LocalDate

LocalDate類內只包含日期,不包含具體時間。只需要表示日期而不包含時間,就可以使用它。

public static void localDate() {
    //獲取當前年月日
    LocalDate today = LocalDate.now();
    System.out.println("當前年月日:" + today);

    // 獲取年的兩種方式
    int thisYear = today.getYear();
    int thisYearAnother = today.get(ChronoField.YEAR);
    System.out.println("今年是" + thisYear + "年");
    System.out.println("今年是" + thisYearAnother + "年");

    // 獲取月
    Month thisMonth = today.getMonth();
    System.out.println(thisMonth.toString());
    // 這是今年的第幾個月(兩種寫法)
    int monthOfYear = today.getMonthValue();
    // int monthOfYear = today.get(ChronoField.MONTH_OF_YEAR);
    System.out.println("這個月是今年的第" + monthOfYear + "個月");
    // 月份的天數
    int length = today.lengthOfMonth();
    System.out.println("這個月有" + length + "天");

    // 獲取日的兩種方式
    int thisDay = today.getDayOfMonth();
    int thisDayAnother = today.get(ChronoField.DAY_OF_MONTH);
    System.out.println("今天是這個月的第" + thisDay + "天");
    System.out.println("今天是這個月的第" + thisDayAnother + "天");

    // 獲取星期
    DayOfWeek thisDayOfWeek = today.getDayOfWeek();
    System.out.println(thisDayOfWeek.toString());
    // 今天是這周的第幾天
    int dayOfWeek = today.get(ChronoField.DAY_OF_WEEK);
    System.out.println("今天是這周的第" + dayOfWeek + "天");

    // 是否為閏年
    boolean leapYear = today.isLeapYear();
    System.out.println("今年是閏年:" + leapYear);

    //構造指定的年月日
    LocalDate anotherDay = LocalDate.of(2008, 8, 8);
    System.out.println("指定年月日:" + anotherDay);
}

2.2 LocalTime

LocalTime只會獲取時間,不獲取日期。LocalTimeLocalDate類似,區別在於LocalDate不包含具體時間,而LocalTime不包含具體日期。

public static void localTime() {
    // 獲取當前時間
    LocalTime nowTime = LocalTime.now();
    System.out.println("當前時間:" + nowTime);

    //獲取小時的兩種方式
    int hour = nowTime.getHour();
    int thisHour = nowTime.get(ChronoField.HOUR_OF_DAY);
    System.out.println("當前時:" + hour);
    System.out.println("當前時:" + thisHour);


    //獲取分的兩種方式
    int minute = nowTime.getMinute();
    int thisMinute = nowTime.get(ChronoField.MINUTE_OF_HOUR);
    System.out.println("當前分:" + minute);
    System.out.println("當前分:" + thisMinute);

    //獲取秒的兩種方式
    int second = nowTime.getSecond();
    int thisSecond = nowTime.get(ChronoField.SECOND_OF_MINUTE);
    System.out.println("當前秒:" + second);
    System.out.println("當前秒:" + thisSecond);

    // 構造指定時間(最多可到納秒)
    LocalTime anotherTime = LocalTime.of(20, 8, 8);
    System.out.println("構造指定時間:" + anotherTime);
}

2.3 LocalDateTime

LocalDateTime表示日期和時間組合。可以通過of()方法直接建立,也可以呼叫LocalDateatTime()方法或LocalTimeatDate()方法將LocalDateLocalTime合併成一個LocalDateTime

public static void localDateTime() {
    // 當前日期和時間
    LocalDateTime today = LocalDateTime.now();
    System.out.println("現在是:" + today);

    // 建立指定日期和時間
    LocalDateTime anotherDay = LocalDateTime.of(2008, Month.AUGUST, 8, 8, 8, 8);
    System.out.println("建立的指定時間是:" + anotherDay);

    // 拼接日期和時間
    // 使用當前日期,指定時間生成的 LocalDateTime
    LocalDateTime thisTime = LocalTime.now().atDate(LocalDate.of(2008, 8, 8));
    System.out.println("拼接的日期是:" + thisTime);
    // 使用當前日期,指定時間生成的 LocalDateTime
    LocalDateTime thisDay = LocalDate.now().atTime(LocalTime.of(12, 24, 12));
    System.out.println("拼接的日期是:" + thisDay);
    // 指定日期和時間生成 LocalDateTime
    LocalDateTime thisDayAndTime = LocalDateTime.of(LocalDate.of(2008, 8, 8), LocalTime.of(12, 24, 12));
    System.out.println("拼接的日期是:" + thisDayAndTime);

    // 獲取LocalDate
    LocalDate todayDate = today.toLocalDate();
    System.out.println("今天日期是:" + todayDate);

    // 獲取LocalTime
    LocalTime todayTime = today.toLocalTime();
    System.out.println("現在時間是:" + todayTime);
}

2.4 Instant

Instant用於一個獲取時間戳,與System.currentTimeMillis()類似,但Instant可以精確到納秒。

public class InstantDemo {

    public static void main(String[] args) {

        // 建立Instant物件
        Instant instant = Instant.now();
        // 通過ofEpochSecond方法建立(第一個引數表示秒,第二個引數表示納秒)
        Instant another = Instant.ofEpochSecond(365 * 24 * 60, 100);

        // 獲取到秒數
        long currentSecond = instant.getEpochSecond();
        System.out.println("獲取到秒數:" + currentSecond);

        // 獲取到毫秒數
        long currentMilli = instant.toEpochMilli();
        System.out.println("獲取到毫秒數:" + currentMilli);
    }
}

2.5 Duration

Duration的內部實現與Instant類似,但Duration表示時間段,通過between方法建立,還可以通過of()方法建立。

public static void duration() {
    LocalDateTime from = LocalDateTime.now();
    LocalDateTime to = LocalDateTime.now().plusDays(1);
    // 通過between()方法建立
    Duration duration = Duration.between(from, to);
    // 通過of()方法建立,該方法引數為時間段長度和時間單位。
    // 7天
    Duration duration1 = Duration.of(7, ChronoUnit.DAYS);
    // 60秒
    Duration duration2 = Duration.of(60, ChronoUnit.SECONDS);
}

2.5 Period

PeriodDuration類似,獲取一個時間段,只不過單位為年月日,也可以通過of方法和between方法建立,between方法接收的引數為LocalDate

private static void period() {
    // 通過of方法
    Period period = Period.of(2012, 12, 24);
    // 通過between方法
    Period period1 = Period.between(LocalDate.now(), LocalDate.of(2020,12,31));
}

三、時間操作

3.1 時間比較

isBefore()isAfter()判斷給定的時間或日期是在另一個時間/日期之前還是之後。
LocalDate為例,LocalDateTime/LocalTime同理。

public static void compare() {
    LocalDate thisDay = LocalDate.of(2008, 8, 8);
    LocalDate otherDay = LocalDate.of(2018, 8, 8);

    // 晚於
    boolean isAfter = thisDay.isAfter(otherDay);
    System.out.println(isAfter);

    // 早於
    boolean isBefore = thisDay.isBefore(otherDay);
    System.out.println(isBefore);
}

3.2 增加/減少年數、月數、天數

LocalDateTime為例,LocalDate/LocalTime同理。

public static void plusAndMinus() {
    // 增加
    LocalDateTime today = LocalDateTime.now();
    LocalDateTime nextYearDay = today.plusYears(1);
    System.out.println("下一年的今天是:" + nextYearDay);
    LocalDateTime nextMonthDay = today.plus(1, ChronoUnit.MONTHS);
    System.out.println("下一個月的今天是:" + nextMonthDay);

    //減少
    LocalDateTime lastMonthDay = today.minusMonths(1);
    LocalDateTime lastYearDay = today.minus(1, ChronoUnit.YEARS);
    System.out.println("一個月前是:" + lastMonthDay);
    System.out.println("一年前是:" + lastYearDay);
}

3.3 時間修改

通過with修改時間

public static void edit() {
    LocalDateTime today = LocalDateTime.now();
    // 修改年為2012年
    LocalDateTime thisYearDay = today.withYear(2012);
    System.out.println("修改年後的時間為:" + thisYearDay);
    // 修改為12月
    LocalDateTime thisMonthDay = today.with(ChronoField.MONTH_OF_YEAR, 12);
    System.out.println("修改月後的時間為:" + thisMonthDay);
}

3.4 時間計算

通過 TemporalAdjusters 的靜態方法 和 Duration 計算時間

public static void compute() {
    // TemporalAdjusters 的靜態方法
    LocalDate today = LocalDate.now();
    // 獲取今年的第一天
    LocalDate date = today.with(firstDayOfYear());
    System.out.println("今年的第一天是:" + date);

    // Duration 計算
    LocalDateTime from = LocalDateTime.now();
    LocalDateTime to = LocalDateTime.now().plusMonths(1);
    Duration duration = Duration.between(from, to);

    // 區間統計換算
    // 總天數
    long days = duration.toDays();
    System.out.println("相隔" + days + "天");
    // 小時數
    long hours = duration.toHours();
    System.out.println("相隔" + hours + "小時");
    // 分鐘數
    long minutes = duration.toMinutes();
    System.out.println("相隔" + minutes + "分鐘");
}
  • TemporalAdjusters的更多方法
方法名稱 描述
dayOfWeekInMonth() 返回同一個月中每週的第幾天
firstDayOfMonth() 返回當月的第一天
firstDayOfNextMonth() 返回下月的第一天
firstDayOfNextYear() 返回下一年的第一天
firstDayOfYear() 返回本年的第一天
firstInMonth() 返回同一個月中第一個星期幾
lastDayOfMonth() 返回當月的最後一天
lastDayOfNextMonth() 返回下月的最後一天
lastDayOfNextYear() 返回下一年的最後一天
lastDayOfYear() 返回本年的最後一天
lastInMonth() 返回同一個月中最後一個星期幾
next() / previous() 返回後一個/前一個給定的星期幾
nextOrSame() / previousOrSame() 返回後一個/前一個給定的星期幾,如果這個值滿足條件,直接返回

四、時間日期格式化

4.1 格式化時間

DateTimeFormatter預設提供了多種格式化方式,如果預設提供的不能滿足要求,可以通過DateTimeFormatterofPattern方法建立自定義格式化方式。

public static void format() {
    LocalDate today = LocalDate.now();
    // 兩種預設格式化時間方式
    String todayStr1 = today.format(DateTimeFormatter.BASIC_ISO_DATE);
    String todayStr2 = today.format(DateTimeFormatter.ISO_LOCAL_DATE);
    System.out.println("格式化時間:" + todayStr1);
    System.out.println("格式化時間:" + todayStr2);
    //自定義格式化
    DateTimeFormatter dateTimeFormatter =   DateTimeFormatter.ofPattern("dd/MM/yyyy");
    String todayStr3 = today.format(dateTimeFormatter);
    System.out.println("自定義格式化時間:" + todayStr3);

}

4.2 解析時間

4.1 中以何種方式格式化,這裡需以同樣方式解析。

public static void parse() {
    LocalDate date1 = LocalDate.parse("20080808", DateTimeFormatter.BASIC_ISO_DATE);
    LocalDate date2 = LocalDate.parse("2008-08-08", DateTimeFormatter.ISO_LOCAL_DATE);
    System.out.println(date1);
    System.out.println(date2);
}

五、總結

相較於Date 的優勢

  1. Instant 的精確度更高,可以精確到納秒級;
  2. Duration 可以便捷得到時間段內的天數、小時數等;
  3. LocalDateTime 能夠快速地獲取年、月、日、下一月等;
  4. TemporalAdjusters 類中包含許多常用的靜態方法,避免自己編寫工具類;
  5. Date的格式化方式SimpleDateFormat相比,DateTimeFormatter是執行緒安全的。

5.1 示例程式碼

Github 示例程式碼

5.2 技術交流

  1. 風塵部落格
  2. 風塵部落格-掘金
  3. 風塵部落格-部落格園
  4. Github