JDK1.8-新的日期和時間API

愛西考的王同學發表於2019-05-21

Java的API提供了很多有用的元件,能幫助你構建複雜的應用。不過,Java API也不總是完美的。我們相信大多數有經驗的程式設計師都會贊同Java 8之前的庫對日期和時間的支援就非常不理想。然而,你也不用太擔心:Java 8中引入全新的日期和時間API就是要解決這一問題。

1 LocalDate 、 LocalTime 、 Instant 、 Duration 以及 Period

讓我們從探索如何建立簡單的日期和時間間隔入手。 java.time 包中提供了很多新的類可以幫你解決問題,它們是 LocalDate 、 LocalTime 、 Instant 、 Duration 和 Period 。

1.1 LocalDate 和 LocalTime

LocalDate 類,該類的例項是一個不可變物件,它只提供了簡單的日期,並不含當天的時間資訊。另外,它也不附帶任何與時區相關的資訊。

你可以通過靜態工廠方法 of 建立一個 LocalDate 例項。 LocalDate 例項提供了多種方法來讀取常用的值,比如年份、月份、星期幾等。

public static void main(String[] args) {
        //2014-3-18
        LocalDate date = LocalDate.of(2014, 3, 18);
        //2014
        int year = date.getYear();
        //march
        Month month = date.getMonth();
        //18
        int day = date.getDayOfMonth();
        DayOfWeek dow = date.getDayOfWeek();
        //這個月的天數
        int len = date.lengthOfMonth();
        //是否事閏年
        boolean leap = date.isLeapYear();
        System.out.println("year : " + year + " month : " + month + " day : " + day +
                " dow : " + dow + " len : " + len + " leap : " + leap);
    }

複製程式碼

似地,一天中的時間,比如13:45:20,可以使用 LocalTime 類表示。你可以使用 of 過載的兩個工廠方法建立 LocalTime 的例項。第一個過載函式接收小時和分鐘,第二個過載函式同時還接收秒。同 LocalDate 一樣, LocalTime 類也提供了一些 getter 方法訪問這些變數的值。

LocalTime time = LocalTime.of(13, 45, 20);
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();
複製程式碼

LocalDate 和 LocalTime 都可以通過解析代表它們的字串建立。使用靜態方法 parse ,你可以實現這一目的:

LocalDate date = LocalDate.parse("2014-03-18");
LocalTime time = LocalTime.parse("13:45:20");
複製程式碼

1.2 合併日期和時間

這個複合類名叫 LocalDateTime ,是 LocalDate 和 LocalTime 的合體。它同時表示了日期和時間,但不帶有時區資訊,你可以直接建立,也可以通過合併日期和時間物件構造。

// 2014-03-18T13:45:20
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(13, 45, 20);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date);
複製程式碼

通過它們各自的 atTime 或者 atDate 方法,向 LocalDate 傳遞一個時間物件,或者向LocalTime 傳遞一個日期物件的方式,你可以建立一個 LocalDateTime 物件。你也可以使用toLocalDate 或者 toLocalTime 方法,從 LocalDateTime 中提取 LocalDate 或者 LocalTime元件:

LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();
複製程式碼

1.3 Duration 和 Period

Duration 類的靜態工廠方法 between 就是為比較兩個時間而設計的。你可以建立兩個 LocalTimes 物件、兩個 LocalDateTimes物件,或者兩個 Instant 物件之間的 duration:

Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
複製程式碼

如果你需要以年、月或者日的方式對多個時間單位建模,可以使用 Period 類。使用該類的工廠方法 between ,你可以使用得到兩個 LocalDate 之間的時長:

Period tenDays = Period.between(LocalDate.of(2014, 3, 8),
								LocalDate.of(2014, 3, 18));
複製程式碼

最後, Duration 和 Period 類都提供了很多非常方便的工廠類,直接建立對應的例項;換句話說,就像下面這段程式碼那樣,不再是隻能以兩個temporal物件的差值的方式來定義它們的物件。

Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
複製程式碼

Duration 類和 Period 類共享方法

JDK1.8-新的日期和時間API
JDK1.8-新的日期和時間API

2 操縱、解析和格式化日期

2.1 比較直觀的方式操縱 LocalDate 的屬性

		//2019-3-20
        LocalDate of = LocalDate.of(2019, 3, 20);
        //2018-3-20
        LocalDate localDate = of.withYear(2018);
        //2018-4-20
        LocalDate localDate1 = localDate.withMonth(4);
        //2018-9-20
        LocalDate with = localDate1.with(ChronoField.MONTH_OF_YEAR, 9);
複製程式碼

最後這一行中使用的 with 方法和get方法有些類似,它們都宣告於 Temporal 介面,所有的日期和時間API類都實現這兩個方法,它們定義了單點的時間,比如 LocalDate 、 LocalTime 、 LocalDateTime 以及 Instant 。更確切 地說,使用 get 和 with 方法,我們可以將 Temporal 物件值的讀取和修改區分開。

2.2 以相對方式修改 LocalDate 物件的屬性

//2014-3-18
LocalDate date1 = LocalDate.of(2014, 3, 18);
//2014-3-25
LocalDate date2 = date1.plusWeeks(1);

LocalDate date3 = date2.minusYears(3);
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);
複製程式碼

最後一行使用的 plus 方法也是通用方法,它和 minus 方法都宣告於 Temporal 介面中。通過這些方法,對 TemporalUnit 物件加上或者減去一個數字,我們能非常方便地將 Temporal 物件前溯或者回滾至某個時間段,通過ChronoUnit 列舉我們可以非常方便地實現 TemporalUnit 介面。

JDK1.8-新的日期和時間API

2.3 使用 TemporalAdjuster

有的時候,你需要進行一些更加複雜的操作,比如,將日期調整到下個週日、下個工作日,或者是本月的最後一天。這時,你可以使用過載版本的 with 方法,向其傳遞一個提供了更多定製化選擇的 TemporalAdjuster 物件,更加靈活地處理日期。對於最常見的用例,日期和時間API已經提供了大量預定義的TemporalAdjuster 。你可以通過 TemporalAdjuster 類的靜態工廠方法訪問它們。

//2014-03-18
LocalDate date1 = LocalDate.of(2014, 3, 18);
//2014-03-23
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY));
//2014-03-31
LocalDate date3 = date2.with(lastDayOfMonth());
複製程式碼

JDK1.8-新的日期和時間API

正如我們看到的,使用 TemporalAdjuster 我們可以進行更加複雜的日期操作,而且這些方 法的名稱也非常直觀,方法名基本就是問題陳述。此外,即使你沒有找到符合你要求的預定義的 TemporalAdjuster ,建立你自己的 TemporalAdjuster 也並非難事。實際上, Temporal- Adjuster 介面只宣告瞭單一的一個方法(這使得它成為了一個函式式介面)。

@FunctionalInterface
public interface TemporalAdjuster {
	Temporal adjustInto(Temporal temporal);
}
複製程式碼

這意味著 TemporalAdjuster 介面的實現需要定義如何將一個 Temporal 物件轉換為另一個 Temporal 物件。你可以把它看成一個 UnaryOperator

2.4 實現一個定製的 TemporalAdjuster

設計一個 NextWorkingDay 類,該類實現了 TemporalAdjuster 介面,能夠計算明天的日期,同時過濾掉週六和週日這些節假日。格式如下所示:

public class MyTemporalAdjuster {
    public static void main(String[] args) {
        LocalDate of = LocalDate.of(2019, 3, 20);
        LocalDate with = of.with(new NewTemporalAdjuster());
        System.out.println("with = " + with);
    }
}

//如果當天的星期介於週一至週五之間,日期向後移動一天;如果當天是週六或者週日,則返回下一個週一
class  NewTemporalAdjuster implements TemporalAdjuster {
    @Override
    public Temporal adjustInto(Temporal temporal) {
        DayOfWeek of = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
        int dayToAdd = 1;
        if (of == DayOfWeek.FRIDAY){
            dayToAdd = 3;
        }else if (of == DayOfWeek.SATURDAY){
            dayToAdd = 2;
        }
        return temporal.plus(dayToAdd, ChronoUnit.DAYS);
    }
}
複製程式碼

2.5 列印輸出及解析日期--時間物件

處理日期和時間物件時,格式化以及解析日期時間物件是另一個非常重要的功能。新的java.time.format 包就是特別為這個目的而設計的。這個包中,最重要的類是 DateTimeFormatter 。建立格式器最簡單的方法是通過它的靜態工廠方法以及常量。像 BASIC_ISO_DATE和 ISO_LOCAL_DATE 這 樣 的 常 量 是 DateTimeFormatter 類 的 預 定 義 實 例 。 所 有 的DateTimeFormatter 例項都能用於以一定的格式建立代表特定日期或時間的字串。

LocalDate date = LocalDate.of(2014, 3, 18);
//20140318
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
//2014-03-18
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
複製程式碼

你也可以通過解析代表日期或時間的字串重新建立該日期物件。所有的日期和時間API都提供了表示時間點或者時間段的工廠方法,你可以使用工廠方法 parse 達到重創該日期物件的目的:

LocalDate date1 = LocalDate.parse("20140318",DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2014-03-18",DateTimeFormatter.ISO_LOCAL_DATE);
複製程式碼

2.6 按照某個模式建立 DateTimeFormatter

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date1.format(formatter);
LocalDate date2 = LocalDate.parse(formattedDate, formatter);
複製程式碼

LocalDate 的 formate 方法使用指定的模式生成了一個代表該日期的字串。

相關文章