Java8中操作日期時間相關的API

從Java8開始,原生的Java API中已經能提供高質量的日期和時間支持,java.time包中整合了很多Joda-Time的特性。
1、LocalDate和LocalTime
首先LocalDate類,該類的實例是一個不可變對象,它只提供了簡單的日期,並不包含當天的時間信息,也不附帶任何與時區相關的信息。

一般通過靜態工廠方法of創建一個LocalDate實例。LocalDate實例提供了多種方法來讀取常用的值,比如年份、月份、星期幾等,如下所示:

import java.time.LocalDate;

public class DateTimeTest {

    public static void main(String[] a) {
        //獲取當前日期
        LocalDate now = LocalDate.now();
        
        //獲取指定年月日的localdate
        LocalDate date = LocalDate.of(2017, 5, 29);
        
        System.out.println("年:" + date.getYear());
        
        System.out.println("月:" + date.getMonthValue());
        
        System.out.println("幾號:" + date.getDayOfMonth());
        
        System.out.println("星期幾:" + date.getDayOfWeek().getValue());
        
        System.out.println("當年中的第幾天:" + date.getDayOfYear());
        
        System.out.println("當月的天數:"+date.lengthOfMonth());
        
        System.out.println("當年的天數:"+date.lengthOfYear());
        
    }
}

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

//表示11:30分
LocalTime time=LocalTime.of(11,30);
//11:30:50
LocalTime time2=LocalTime.of(11,30,50);
System.out.println("時:"+time2.getHour());
System.out.println("分:"+time2.getMinute());
System.out.println("秒:"+time2.getSecond());

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

LocalDate date = LocalDate.parse("2014-03-18");
LocalTime time = LocalTime.parse("13:45:20");

你可以向parse方法傳遞一個DateTimeFormatter。該類的實例定義瞭如何格式化一個日期或者時間對象。

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

//2017-05-29 11:30:50
LocalDateTime dateTime=LocalDateTime.of(2017,5,29,11,30,50);
LocalDateTime dateTime2=LocalDateTime.of(date,time2);

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

LocalDateTime ldt=time2.atDate(date);
LocalDateTime dateTime3=date.atTime(time2);
LocalDate localDate = dateTime3.toLocalDate();
LocalTime localTime=dateTime3.toLocalTime();

Instant
作爲人,我們習慣於以星期幾、幾號、幾點、幾分這樣的方式理解日期和時間。毫無疑問, 這種方式對於計算機而言並不容易理解。新的java.time.Instant類是以Unix元年時間(傳統的設定爲UTC時區1970年1月1日午夜時分)開始所經歷的秒數來表示時間。
你可以通過向靜態工廠方法ofEpochSecond傳遞一個代表秒數的值創建一個該類的實例。靜態工廠方法ofEpochSecond還有一個增強的重載版本,它接收第二個以納秒爲單位的參數值,對傳入作爲秒數的參數進行調整。一旦得到Instant對象,你就可以像之前的LocalDate和LocalTime一樣獲取該時間的各個信息.

//獲取當前時間的instant
Instant now_instant=Instant.now();
//傳入秒得到instant
Instant instant=Instant.ofEpochSecond(100);
//傳入毫秒
Instant instant2=Instant.ofEpochMilli(100000);
now.getDayOfMonth();
now.getDayOfYear();
now.getMonthValue();
now.getYear();
now.lengthOfYear();

Duration
Duration類主要用於以秒和納秒衡量時長。Duration類的靜態工廠方法between用於創建兩個時間之間的時長。

你可以創建兩個LocalTime對象、兩個LocalDateTime或者兩個Instant對象之間的duration。

//創建兩個localTime之間的時長對象
Duration duration = Duration.between(LocalTime.of(19, 20), LocalTime.of(19, 30));
//獲取相差多少秒      
System.out.println(duration.getSeconds());
//創建兩個LocalDateTime之間的時長對象
Duration duration2 = Duration.between(LocalDateTime.of(2017, 5, 29, 19, 20), LocalDateTime.of(2017, 5, 29, 19, 30));
//獲取相差多少秒
System.out.println(duration2.getSeconds());
//獲取相差多少秒的絕對值
System.out.println(duration2.abs().getSeconds());
//相差多少天
System.out.println(duration2.toDays());
//相差多少時
System.out.println(duration2.toHours());
//相差多少分鐘
System.out.println(duration2.toMinutes());
//相差多少毫秒
System.out.println(duration2.toMillis());

由於LocalDateTime和Instant是爲不同的目的而設計的,一個是爲了便於人閱讀使用, 另一個是爲了便於機器處理,所以你不能將二者混用。如果你試圖在這兩類對象之間創建 duration,會觸發一個DateTimeException異常。此外,由於Duration類主要用於以秒和納秒衡量時間的長短,你不能僅向between方法傳遞一個LocalDate對象做參數。
如果你需要以年、月或者日的方式對多個時間單位建模,可以使用Period類。使用該類的 工廠方法between,你可以使用得到兩個LocalDate之間的時長,如下所示:

Period tenDays = Period.between(LocalDate.of(2017, 3, 8),   

Duration和Period類都提供了很多非常方便的工廠類,用於直接創建對應的實例。

//1天的時長轉成小時
System.out.println(Duration.ofDays(1).toHours());
//1小時的時長轉成分鐘
System.out.println(Duration.ofHours(1).toMinutes());
//10天的時長
System.out.println(Period.ofDays(10).getDays());

操縱和格式化日期
上述這些日期和時間對象都是不可修改的,這是爲了更好地支持函數式編程,確保線程安全,保持領域模式一致性而做出的重大設計決定。當然,新的日期和時間API也 提供了一些便利的方法來創建這些對象的可變版本。
如果你已經有一個LocalDate對象,想要創建它的一個修改版,最直接也最簡單的方法是使 用withXXX方法。withXXX方法會創建對象的一個副本,並按照需要修改它的屬性。注意,下面的這段代碼中所有的方法都返回一個修改了屬性的新對象。它們都不會修改原來的對象!

//2017-5-18
LocalDate date1 = LocalDate.of(2017, 5, 18);
//2018-5-18
LocalDate date2 = date1.withYear(2018);
//2018-5-25
LocalDate date3 = date2.withDayOfMonth(25);

也可以以聲明的方式操縱LocalDate對象。比如,你可以加上或者減去一段時間。

//加3天
System.out.println(date1.plusDays(5));
//減2天減2小時減1周
System.out.println(date1.minusDays(2).minusMonths(2).plusWeeks(1));

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

import static java.time.temporal.TemporalAdjusters.*;
LocalDate date1 = LocalDate.of(2019, 4, 18);
//2019-04-21
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY)); 
//2019-04-30
LocalDate date3 = date2.with(lastDayOfMonth());

TemporalAdjuster類中的常用工廠方法
dayOfWeekInMonth 創建一個新的日期,它的值爲同一個月中每一週的第幾天
firstDayOfMonth 創建一個新的日期,它的值爲當月的第一天
firstDayOfNextMonth 創建一個新的日期,它的值爲下月的第一天
firstDayOfNextYear 創建一個新的日期,它的值爲明年的第一天
firstDayOfYear 創建一個新的日期,它的值爲當年的第一天
firstInMonth 創建一個新的日期,它的值爲同一個月中,第一個符合星期幾要求的值
lastDayOfMonth 創建一個新的日期,它的值爲當月的最後一天
lastDayOfNextMonth 創建一個新的日期,它的值爲下月的最後一天
lastDayOfNextYear 創建一個新的日期,它的值爲明年的最後一天
lastDayOfYear 創建一個新的日期,它的值爲今年的最後一天
lastInMonth 創建一個新的日期,它的值爲同一個月中,最後一個符合星期幾要求的值
next/previous 創建一個新的日期,並將其值設定爲日期調整後或者調整前,第一個符合指定星 期幾要求的日期
nextOrSame/previousOrSame 創建一個新的日期,並將其值設定爲日期調整後或者調整前,第一個符合指定星 期幾要求的日期,如果該日期已經符合要求,直接返回該對象

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

//2019-03-18
LocalDate date = LocalDate.of(2019, 3, 18);
//20190318
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
//2019-03-18
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
//2019-03-18
LocalDate date1 = LocalDate.parse("20190318",
			DateTimeFormatter.BASIC_ISO_DATE);
//2019-03-18
LocalDate date2 = LocalDate.parse("2019-03-18",
			DateTimeFormatter.ISO_LOCAL_DATE);

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

LocalDate date1 = LocalDate.parse("20190318",
                                 DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2019-03-18",
                                 DateTimeFormatter.ISO_LOCAL_DATE);

和老的java.util.DateFormat相比較,所有的DateTimeFormatter實例都是線程安全的。所以,你能夠以單例模式創建格式器實例,就像DateTimeFormatter所定義的那些常量,並能在多個線程間共享這些實例。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);

處理不同的時區
上述的日期和時間的種類都不包含時區信息。時區的處理是新版日期和時間API新增加的重要功能,使用新版日期和時間API時區的處理被極大地簡化了。新的java.time.ZoneId 類是老版java.util.TimeZone的替代品。它的設計目標就是要讓你無需爲時區處理的複雜和繁瑣而操心,比如處理日光時這種問題。跟其他日期和時間類一樣,ZoneId類也是無法修改的。
時區是按照一定的規則將區域劃分成的標準時間相同的區間。在ZoneRules這個類中包含了40個這樣的實例。你可以簡單地通過調用ZoneId的getRules()得到指定時區的規則。每個特定的ZoneId對象都由一個地區ID標識,比如:

ZoneId romeZone = ZoneId.of("Europe/Rome");

地區ID都爲“{區域}/{城市}”的格式,這些地區集合的設定都由英特網編號分配機構(IANA) 的時區數據庫提供。

也可以利用當前時區和UTC/格林尼治的固定偏差創建時區。

//東九區
ZoneId zoneId= ZoneId.of("+09:00");

你可以通過Java 8的新方法toZoneId將一個老的時區對象轉換爲ZoneId:

ZoneId zoneId = TimeZone.getDefault().toZoneId();

一旦得到一個ZoneId對象整合起來,構造爲一個ZonedDateTime實例,它代表了相對於指定時區的時間點.

//不包含任何時區的時間
LocalDateTime localDateTime=LocalDateTime.now();
//東九區
ZoneId zoneId= ZoneId.of("+09:00");
//東八區
ZoneId bj=ZoneId.of("+08:00");
//將東八區的當期時間轉化成東九區的時間
System.out.println(LocalDateTime.ofInstant(localDateTime.atZone(bj)
								.toInstant(),zoneId)
								.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章