JavaSE原理-日期類API

學習目標

掌握Java8中的提供的java。time包中的常用日期類與相關方法

可以從java。util包的下的日期類相關類過渡到java。time包下的日期類

掌握Java8中的日期與字串之間的相互轉換

1。為什麼會出現新的日期類API

將java。util。Date類束之高閣才是正確之道 -> Tim Yates

在Java面世之初,標準庫就引入了兩種用於處理日期和時間的類,它們是

java。util。Date和java。util。Calendar,而前者堪稱類糟糕設計的典範,瀏覽

API可以發現,從Java1。1開始,Date類中的所有方法就已經被棄用,Java1。1推

薦採用Calendar類處理日期和時間,但是這個類同樣存在不少問題。

對於日期的計算困難問題。

毫秒值與日期直接轉換比較繁瑣,其次透過毫秒值來計算時間的差額步驟較多。

package com。itheima。time;

import java。util。Calendar;

import java。util。Date;

/**

* 計算當前時間距離2000年6月1日相差了多少天。

*

* 透過距離1970年1月1日的毫秒差值,可以計算出兩個日期之間相隔的天數。

*/

public class JavaUtilTimeDemo01 {

public static void main(String[] args) {

//1。初始化Date物件,無參構造(無參構造預設代表的就是當前時間)

Date dateNow = new Date();

//2。獲取當前時間的距離1970年1月1日過了多少毫秒。

long dateTimeNow = dateNow。getTime();

//3。初始化Calendar物件並設時間為2006年6月1日並且將Calendar物件轉換

為Date物件。

Calendar paramterTime = Calendar。getInstance();

paramterTime。set(2000, Calendar。JUNE, 1);

執行緒安全問題

SimpleDateFormat類是執行緒不安全的,在多執行緒的情況下,全域性共享一個

SimpleDateFormat類中的Calendar物件有可能會出現異常。

Date paramterDateTime = paramterTime。getTime();

//4。計算paramterDateTime與dateTimeNow之間的毫秒差額。

Long intervalTime = dateTimeNow -

paramterDateTime。getTime();

//5。對intervalTime進行計算獲取差額,毫秒值/1000->/60->/60->/24

long intervalDay = intervalTime / 1000 / 60 / 60 / 24;

System。out。println(“當前時間距離2000年6月1日已經過了” +

intervalDay+“天。”);

}

}

package com。itheima。time;

import java。text。ParseException;

import java。text。SimpleDateFormat;

public class JavaUtilTimeDemo02 {

//建立SimpleDateFormat的物件(單例)

static SimpleDateFormat SIMPLEDATEFORMAT = new

SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);

public static void main(String[] args) {

//建立10個執行緒並啟動。

for (int i = 0; i < 10; i++) {

new Thread(() -> {

try {

System。out。println(SIMPLEDATEFORMAT。parse(“2018-12-12 12:12:12”));

} catch (ParseException e) {

e。printStackTrace();

}

})。start();

}

}

}

另外的一個問題就是在java。util。Date和java。util。Calendar類之前,列舉

型別(ENUM)還沒有出現,所以在欄位中使用整數常量導致整數常量都是可變的,

而不是執行緒安全的。為了處理實際開發中遇到的問題,標準庫隨後引入了

java。sql。Date作為java。util。Date的子類,但是還是沒能徹底解決問題。

最終JavaSE 8中引入了java。time包,這種全新的包從根本上解決了長久以來的

存在的諸多弊端,java。time包基於Joda-Time庫構件,是一種免費的開源解決

方案,實際上在Java 8沒有出現之前,公司中已經廣泛使用Joda-Time來解決

Java中的日期與時間問題,Joda-Time的設計團隊也參與了java。time包的開

發。

2。Date-Time API中的基本類使用

常用類的概述與功能介紹

Instant類

Instant類對時間軸上的單一瞬時點建模,可以用於記錄應用程式中的事件時間戳,在之

後學習的型別轉換中,均可以使用Instant類作為中間類完成轉換。

Duration類

Duration類表示秒或納秒時間間隔,適合處理較短的時間,需要更高的精確性。

Period類

Period類表示一段時間的年、月、日。

LocalDate類

LocalDate是一個不可變的日期時間物件,表示日期,通常被視為年月日。

LocalTime類

LocalTime是一個不可變的日期時間物件,代表一個時間,通常被看作是小時-秒,時間表

示為納秒精度。

LocalDateTime類

LocalDateTime是一個不可變的日期時間物件,代表日期時間,通常被視為年-月-日-

時-分-秒。

ZonedDateTime類

ZonedDateTime是具有時區的日期時間的不可變表示,此類儲存所有日期和時間欄位,精

度為納秒,時區為區域偏移量,用於處理模糊的本地日期時間。

now方法在日期/時間類的使用

Date-Time API中的所有類均生成不可變例項,它們是執行緒安全的,並且這些類

不提供公共建構函式,也就是說沒辦法透過new的方式直接建立,需要採用工廠方

法加以例項化。

now方法可以根據當前日期或時間建立例項。

package com。itheima。time;

import java。time。*;

各個類封裝時間所表示的特點

Instant封裝的時間為祖魯時間並非當前時間。

祖魯時間也是格林尼治時間,也就是國際標準時間。

LocalDate封裝的只有年月日,沒有時分秒,格式為yyyy-MM-dd。

LocalTime封裝的只有時分秒,沒有年月日,格式為hh:mm:ss。sss,最

後的sss是納秒。

LocalDateTime將LocalDate和LocalTime合二為一,在年月日與時分

秒中間使用T作為分隔。

ZonedDateTime中封裝了年月日時分秒,以及UTC(祖魯時間)偏移量,並

且還有一個地區名。

+8:00代表中國是東八區,時間比國際標準時間快八小時。

不僅僅是剛才提供的幾個類可以使用now方法,Java8的Time包中還提供了其他的幾個類可以更精

準的獲取某些資訊。

Year類(表示年)

YearMonth類(表示年月)

MonthDay類(表示月日)

public class Java8TimeClassMethodDemo1 {

public static void main(String[] args) {

//使用now方法建立Instant的例項物件。

Instant instantNow = Instant。now();

//使用now方法建立LocalDate的例項物件。

LocalDate localDateNow = LocalDate。now();

//使用now方法建立LocalTime的例項物件。

LocalTime localTimeNow = LocalTime。now();

//使用now方法建立LocalDateTime的例項物件。

LocalDateTime localDateTimeNow = LocalDateTime。now();

//使用now方法建立ZonedDateTime的例項物件。

ZonedDateTime zonedDateTimeNow = ZonedDateTime。now();

//將例項物件列印到控制檯。

System。out。println(“Instant:”+instantNow);

System。out。println(“LocalDate:”+localDateNow);

System。out。println(“LocalTime:”+localTimeNow);

System。out。println(“LocalDateTime:”+localDateTimeNow);

System。out。println(“ZonedDateTime:”+zonedDateTimeNow);

}

}

package com。itheima。time;

of方法在日期/時間類的應用

of方法可以根據給定的引數生成對應的日期/時間物件,基本上每個基本類都有

of方法用於生成的對應的物件,而且過載形式多變,可以根據不同的引數生成對

應的資料。

import java。time。*;

public class Java8TimeClassMethodDemo2 {

public static void main(String[] args) {

//初始化Year的例項化物件。

Year year = Year。now();

//初始化YearMonth的例項化物件

YearMonth month = YearMonth。now();

//初始化MonthDay的例項化物件。

MonthDay day = MonthDay。now();

}

}

package com。itheima。time;

import java。time。LocalDate;

import java。time。LocalDateTime;

import java。time。LocalTime;

public class Java8TimeClassMethodDemo3 {

public static void main(String[] args) {

//初始化2018年8月8日的LocalDate物件。

LocalDate date = LocalDate。of(2018, 8, 8);

System。out。println(“LocalDate:” + date);

/*

初始化晚上7點0分0秒的LocalTime物件。

LocalTime。of方法的過載形式有以下幾種,可以根據實際情況自行使用。

LocalTime of(int hour, int minute) -> 根據小時/分鐘生成物件。

LocalTime of(int hour, int minute, int second) -> 根據小時/分鐘/秒生成

物件。

LocalTime of(int hour, int minute, int second, int nanoOfSecond) ->

根據小時/分鐘/毫秒/納秒生成物件。

注意:如果秒和納秒為0的話,那麼預設不會封裝這些資料,只顯示小時和分鐘。

*/

LocalTime time = LocalTime。of(19, 0, 0, 0);

System。out。println(“LocalTime:” + time);

/*

為LocalDateTime新增時區資訊(拓展)

在學習ZonedDateTime的時候,發現了這個物件裡面封裝的不僅有時間日期,並且還有偏移量+時

區,那麼時區如何在Java中獲取呢,透過提供的一個類ZoneId的getAvailableZoneIds方法可以

獲取到一個Set集合,集合中封裝了600個時區。

初始化2018年8月8日下午7點0分的LocalDateTime物件。

LocalDateTime。of方法的過載形式有以下幾種,可以根據事情自行使用。

LocalDateTime of(int year, int month, int dayOfMonth, int hour, int

minute, int second, int nanoOfSecond) -> 根據年/月/日/時/分/秒生成物件。

LocalDateTime of(int year, int month, int dayOfMonth, int hour, int

minute) -> 根據年/月/日/時/分生成物件。

注意:LocalDateTime of(LocalDate date, LocalTime time)方法可以將一個

LocalDate物件和一個LocalTime物件合併封裝為一個LocalDateTime物件。

*/

LocalDateTime。of(2018, 8, 8, 19, 0, 0, 0);

LocalDateTime localDateTime = LocalDateTime。of(date, time);

System。out。println(“LocalDateTime:” + localDateTime);

}

}

//獲取所有的時區資訊

Set availableZoneIds = ZoneId。getAvailableZoneIds();

for (String zoneId : availableZoneIds) {

System。out。println(zoneId);

}

同樣也提供了獲取當前系統預設的時區的方式systemDefault()方法。

//獲取當前系統預設的時區資訊

ZoneId zoneId = ZoneId。systemDefault();

System。out。println(zoneId);

我們可以透過給LocalDateTime新增時區資訊來檢視到不同時區的時間,比如說LocalDateTime中

當前封裝的是上海時間,那麼想知道在此時此刻,紐約的時間是什麼,就可以將紐約的時區Id新增進

去,就可以檢視到了,方式如下。

封裝時間LocalDateTime並新增時區資訊。

更改時區資訊檢視對應時間。

Month列舉類的使用

java。time包中引入了Month的列舉,Month中包含標準日曆中的12個月份的常

量(從JANURAY到DECEMEBER)也提供了一些方便的方法供我們使用。

推薦在初始化LocalDate和LocalDateTime物件的時候,月份的引數使用列舉的

方式傳入,這樣更簡單易懂而且不易出錯,因為如果是老的思維,Calendar傳入0

的話,那麼會出現異常。

package com。itheima。time;

import java。time。LocalDateTime;

import java。time。ZoneId;

import java。time。ZonedDateTime;

/**

* 為LocalDateTime新增時區資訊。

*/

public class Java8TimeClassMethodDemo5 {

public static void main(String[] args) {

//1。封裝LocalDateTime物件,引數自定義 -> 2018年11月11日 8點54分38秒

LocalDateTime time = LocalDateTime。of(2018, 11, 11, 8, 54, 38);

//2。封裝完成後的time物件只是封裝的是一個時間,並沒有時區相關的資料,所以新增時區到

物件中,使用atZone方法。

ZonedDateTime zonedDateTime =

time。atZone(ZoneId。of(“Asia/Shanghai”));

System。out。println(“Asia/Shanghai的時間是:” + zonedDateTime);

//3。更改時區檢視其它時區的當前時間,透過withZoneSameInstant方法即可更改。

ZonedDateTime otherZonedTime =

zonedDateTime。withZoneSameInstant(ZoneId。of(“Asia/Tokyo”));

System。out。println(“在同一時刻,Asia/Tokyo的時間是:” + otherZonedTime);

}

}

/**

* Month列舉類的使用。

練習

按照要求建立對應的時間日期物件並且列印到控制檯輸出。

建立當前時間(不帶時區)

建立當前時間(只包含年月日) 月份使用列舉表示

建立當前時間(包含年月日時分秒並且帶有時區) 月份使用列舉表示

建立2012年12年31日7時38分46秒的日期物件 月份使用列舉表示

建立2012年12年31日的日期物件 月份使用列舉表示

建立7時38分46秒的時間物件

透過鍵盤錄入的方式建立2014年11月11日 11時11分11秒的日期物件列印到控制檯輸出。

3。根據現有例項建立日期與時間物件

想要修改某個日期/時間物件的現有例項時,我們可以使用plus和minus方法來完成操作。

Java8中日期時間相關的API中的所有例項都是不可改變的,一旦建立

LocalDate,LocalTime,LocalDateTime就無法修改他們(類似於String),這對於

執行緒安全非常有利。

plus方法在LocalDate與LocalTime中的使用

LocalDate中定義了多種對日期進行增減操作的方法

LocalDate plusDays(long days) 增加天數

LocalDate plusWeeks(long weeks) 增加週數

LocalDate plusMonths(long months) 增加月數

LocalDate plusYears(long years) 增加年數

LocalTime中定義了多種對時間進行增減操作的方法

LocalTime plusNanos(long nanos) 增迦納秒

LocalTime plusSeconds(long seconds) 增加秒

LocalTime plusMinutes(long minutes) 增加分鐘

*/

public class Java8TimeClassMethodDemo6 {

public static void main(String[] args) {

//在初始化LocalDate和LocalDateTime的時候,月份的引數傳入列舉類(2011年5月15日

11時11分11秒)

LocalDateTime。of(2011, Month。JUNE,15,11,11,11);

//of方法可以根據傳入的數字返回對應的月份。

Month month = Month。of(12);

System。out。println(month);

}

}

LocalTime plusHours(long hours) 增加小時

package com。itheima。time。plus;

import org。junit。Test;

import java。time。LocalDate;

import java。time。Month;

public class Java8TimeMethodPlusDemo1 {

public static void main(String[] args) {

//封裝LocalDate物件引數為2016年2月13日。

LocalDate date = LocalDate。of(2016, Month。FEBRUARY, 13);

//計算當前時間的4天后的時間。

LocalDate plusDaysTime = date。plusDays(4);

//計算當前時間的周後的時間。

LocalDate plusWeeksTime = date。plusWeeks(3);

//計算當前時間的5個月後的時間。

LocalDate plusMonthsTime = date。plusMonths(5);

//計算當前時間的2年後的時間。

LocalDate plusYearsTime = date。plusYears(2);

System。out。println(“當前的時間是:”+date);

System。out。println(“4天后的時間是:”+plusDaysTime);

System。out。println(“3周後的時間是:”+plusWeeksTime);

System。out。println(“5個月後的時間是:”+plusMonthsTime);

System。out。println(“2年後的時間是:”+plusYearsTime);

}

}

package com。itheima。time。plus;

import java。time。LocalTime;

public class Java8TimeMethodPlusDemo2 {

public static void main(String[] args) {

//封裝LocalTime物件引數為8時14分39秒218納秒。

LocalTime time = LocalTime。of(8, 14, 39, 218);

//計算當前時間500納秒後的時間。

LocalTime plusNanosTime = time。plusNanos(500);

//計算當前時間45秒後的時間。

LocalTime plusSecondsTime = time。plusSeconds(45);

//計算當前時間19分鐘後的時間。

LocalTime plusMinutesTime = time。plusMinutes(19);

//計算當前時間3小時後的時間。

LocalTime plusHoursTime = time。plusHours(3);

System。out。println(“當前的時間是:” + time);

System。out。println(“45秒後的時間是:” + plusSecondsTime);

System。out。println(“19分鐘後的時間是:” + plusMinutesTime);

System。out。println(“500納秒後的時間是:” + plusNanosTime);

System。out。println(“3小時後的時間是:” + plusHoursTime);

}

}

本文中都是使用plusXXX的方法進行演示,實際上也有對應的減少

方法,以minus開頭的方法對應的即為減少,實際上minus方法呼叫

的也是plus方法,只不過傳入的引數是負數。

plus和minus方法的應用

剛才學習到的plusXXX相關的方法都是添加了數值到具體的某一項上,根據觀察

還有兩個單獨的plus方法,接下來我們來學習這兩個單獨的plus方法。

plus(TemporaAmount amountToAdd)

TemporaAmount是一個介面,當介面作為方法的引數的時候,實際上傳入的是介面的實現

類物件,根據檢視這個介面的體系,可以看到這個介面有一個實現類,名字叫做Period,

在學習第一節的時候,說明了這個類表示一段時間。

如何使用Period來表示一段時間呢?這個類本身提供了of(int year,int month,int

day)來表示,例: Period。of(1,2,3)返回的物件表示的即為1年2個月3天

這麼一個時間段,我們可以使用plus方法新增這個時間段。

plus(long l,TemporaUnit unit)

在實際開發過程中,可能還會更精準的去操作日期或者說增加一些特殊的時間,比如說1

個世紀,1個半天,1千年,10年等,Java8提供了這些日期的表示方式而不需要去單獨進行

計算了。

TemporaUnit是一個介面,透過檢視體系介面發現,可以使用子類ChronoUnit來表

示,ChronoUnit封裝了很多時間段供我們使用。

package com。itheima。time。plus;

import java。time。LocalDate;

import java。time。Period;

/*

今天程式設計師小郝在檢視自己的車輛保險記錄的時候檢視到還有2年3個月零8天保險就到期了,

計算2年3個月零8天后的時間是多少。

*/

public class Java8TimeMethodPlusDemo4 {

public static void main(String[] args) {

LocalDate date = LocalDate。now(); //date表示當前時間。

//固然可以使用對於年月日依次+2,+3,+8的方式來操作,但是有些繁瑣,首先我

們先將2年3月8天封裝為一段時間,也就是封裝為一個Period物件。

Period time = Period。of(2, 3, 8);

//使用plus方法對於date物件直接進行增加的操作。

LocalDate endDate = date。plus(time);

System。out。println(“今天是” + date + “,保險到期的時間是” +

endDate + “。”);

}

}

package com。itheima。time。plus;

import java。time。LocalDate;

import java。time。LocalDateTime;

import java。time。Month;

import java。time。Period;

import java。time。temporal。ChronoUnit;

/*

結婚10年稱為錫婚,2020年2月2日11點11分11秒稱為對稱日,很多情侶準備在那天結婚,如果在那

天結婚了,那麼錫婚會發生在什麼時候。

*/

public class Java8TimeMethodPlusDemo5 {

public static void main(String[] args) {

LocalDateTime marryTime = LocalDateTime。of(2020,

Month。FEBRUARY, 2, 11, 11, 11);

//使用plus方法進行計算,新增1個,ChronoUnit。DECADES(十年)。

LocalDateTime time = marryTime。plus(1, ChronoUnit。DECADES);

System。out。println(“如果在” + marryTime + “結婚,那麼錫婚是” +

time);

注意第一個引數為單位,第二個引數為時間長度。

例:plus(1, ChronoUnit。DECADES)加1個10年。

plus(1, ChronoUnit。CENTURIES)加1個100年。

with方法在LocalDateTime類的應用

如果不需要對日期進行加減而是要直接修改日期的話,那麼可以使用with方法,with方法提供了很

多種修改時間的方式。

LocalDateTime withNano(int i) 修改納秒

LocalDateTime withSecond(int i) 修改秒

LocalDateTime withMinute(int i) 修改分鐘

LocalDateTime withHour(int i) 修改小時

LocalDateTime withDayOfMonth(int i) 修改日

LocalDateTime withMonth(int i) 修改月

LocalDateTime withYear(int i) 修改年

//如果錫婚後的半天準備要請所有親戚朋友吃飯,那麼吃飯的時間是。

LocalDateTime eatTime = time。plus(1, ChronoUnit。HALF_DAYS);

System。out。println(“半天后吃飯,吃飯的時候是:” + eatTime);

}

}

package com。itheima。time。with;

import java。time。LocalDateTime;

public class Java8TimeMethodWithDemo1 {

public static void main(String[] args) {

LocalDateTime time = LocalDateTime。now();

//經過使用發現time中的時間有錯誤,應該是1日,在不知道原有時間的基礎上,無法進

行增減操作,所以可以直接使用with方法進行修改。

LocalDateTime endTime = time。withDayOfMonth(1);

System。out。println(“修改前錯誤的時間是:” + time);

System。out。println(“修改完成之後的時間是:” + endTime);

}

}

with(TemporalField field, long newValue)

TemporalField是一個介面,透過檢視體系結構,可以使用它的子類

ChronoField,ChronoField中封裝了一些日期時間中的組成部分,可以直接選擇之後傳

入第二個引數進行修改。

例:with(ChronoField。DAY_OF_MONTH,1); 將日期中的月份中的天數改為1。

例:with(ChronoField。YEAR,2021); 將日期中的年份改為2021。

練習

使用三種方式計算2019年7月19日14時38分34秒後的3年7個月18天后是什麼時候?

4。調節器TemporalAdjuster與查詢TemporalQuery

在上一節學習的with方法中學習了可以透過with方法修改日期時間物件中封裝的數

據,但是有一些時候可能會做一些複雜的操作,比如說將時間調整到下個周的週日,下

一個工作日,或者本月中的某一天,這個時候可以使用調節器TemporalAdjuster來

更方便的處理日期。

with方法有一個過載形式,需要傳入一個TemporalAdjuster物件,透過檢視發現TemporalAdjuster是

一個介面,方法的引數是一個介面,那麼實際上傳入的是這個介面的實現類物件。

package com。itheima。time。with;

import java。time。LocalDateTime;

import java。time。temporal。ChronoField;

public class Java8TimeMethodWithDemo2 {

public static void main(String[] args) {

LocalDateTime time = LocalDateTime。now();

//經過使用發現time中的時間有錯誤,應該是1日,在不知道原有時間的基礎上,

無法進行增減操作,所以可以直接使用with方法進行修改。

LocalDateTime endTime =

time。with(ChronoField。DAY_OF_MONTH,1);

System。out。println(“修改前錯誤的時間是:” + time);

System。out。println(“修改完成之後的時間是:” + endTime);

}

}

在以上的描述中,發現了一個叫做TemporalAdjusters的類可以給我們提供一些常用的方法,方法如下。

TemporalAdjusters類中常用靜態方法的使用

static TemporalAdjuster firsyDayOfNextMonth()

下個月的第一天

static TemporalAdjuster firstDayOfNextYear()

下一年的第一天

static TemporalAdjuster firstDayOfYear()

當年的第一天

static TemporaAdjuster firstInMonth(DayOfWeek dayOfWeek)

當月的第一個周x(透過引數確定)

static TemporaAdjuster lastDayOfMonth()

當月的最後一天

static TemporaAdjuster lastDayOfYear()

當年的最後一天

static TemporaAdjuste lastInMonth(DayOfWeek dayOfWeek)

當月的最後一個周x(透過引數確定)

static TemporaAdjuster next(DayOfWeek dayOfWeek)

下一個周x(透過引數確定)

static TemporaAdjuster previous(DayOfWeek dayOfWeek)

上一個周x(透過引數確定)

TemporalAdjuster是一個函式式介面,裡面有一個抽象方法叫做Temporal

adjustInto(Temporal temporal);傳入一個Temporal物件透過實現邏輯返回

一個Temporal物件,Temporal是LocalDate,LocalTime相關日期類的父介面,

實際上傳入的就是一個時間日期物件返回一個時間日期物件。

package com。itheima。time;

import java。time。LocalDate;

import java。time。Month;

import java。time。temporal。TemporalAdjuster;

import java。time。temporal。TemporalAdjusters;

public class Java8TimeTemporalAdjusterDemo1 {

public static void main(String[] args) {

//封裝日期時間物件為當前時間,LocalDate。

LocalDate time = LocalDate。now();

/*

with方法可以修改time物件中封裝的資料,需要傳入一個TemporalAdjuster物件,

透過檢視發現TemporalAdjuster是一個介面,方法的引數是一個介面,那麼實際上傳入的是

這個介面的實現類物件。

TemporalAdjusters的類可以給我們提供一些常用的方法。

*/

//with方法傳入了TemporalAdjuster類的實現物件,是由TemporalAdjusters類的方

法實現了adjustInto方法,當前的邏輯是:將時間修改為當月的第一天。

LocalDate firstDayOfMonth =

time。with(TemporalAdjusters。firstDayOfMonth());

//將時間修改為下個月的第一天。

DayOfWeek的使用

DayOfWeek是一週中星期幾的列舉類,其中封裝了從週一到週日。

LocalDate firstDayOfNextMonth =

time。with(TemporalAdjusters。firstDayOfNextMonth());

//將時間修改為下一年的第一天。

LocalDate firstDayOfNextYear =

time。with(TemporalAdjusters。firstDayOfNextYear());

//將時間修改為本年的第一天。

LocalDate firstDayOfYear =

time。with(TemporalAdjusters。firstDayOfYear());

//將時間修改為本月的最後一天。

LocalDate lastDayOfMonth =

time。with(TemporalAdjusters。lastDayOfMonth());

//將時間修改為本年的最後一天。

LocalDate lastDayOfYear =

time。with(TemporalAdjusters。lastDayOfYear());

System。out。println(“當月的第一天是:” + firstDayOfMonth);

System。out。println(“下個月的第一天是:” + firstDayOfNextMonth);

System。out。println(“下一年的第一天是:” + firstDayOfNextYear);

System。out。println(“本年的第一天是:” + firstDayOfYear);

System。out。println(“本月的最後一天是:” + lastDayOfMonth);

System。out。println(“本年的最後一天是:” + lastDayOfYear);

}

}

package com。itheima。time;

import java。time。DayOfWeek;

import java。time。LocalDate;

import java。time。temporal。TemporalAdjusters;

public class Java8TimeTemporalAdjusterDemo2 {

public static void main(String[] args) {

//封裝日期時間物件為當前時間,LocalDate。

LocalDate time = LocalDate。now();

/*

DayOfWeek是一週中星期幾的列舉類,其中封裝了從週一到週日。

*/

//將當前時間修改為下一個週日

LocalDate nextSunday =

time。with(TemporalAdjusters。next(DayOfWeek。SUNDAY));

//將當前時間修改為上一個週三

自定義TemporalAdjuster調節器

透過Java8本身提供的TemporalAdjusters中的方法可以完成一些常用的操作,如果要自定義日期

時間的更改邏輯,可以透過實現TemporalAdjuster類介面中的方式來完成。

建立類實現TemporalAdjuster介面

實現TemporalAdjuster中的adjustInto方法,傳入一個日期時間物件,完成邏輯之後返回日

期時間物件。

透過with方法傳入自定義調節器物件完成更改。

例:假如員工一個月中領取工資,發薪日是每個月的15號,如果發薪日是週末,則調整為週五。

LocalDate previousWednesday =

time。with(TemporalAdjusters。previous(DayOfWeek。WEDNESDAY));

System。out。println(“下一個週日是:”+nextSunday);

System。out。println(“上一個週三是:”+previousWednesday);

}

}

package com。itheima。time。impl;

import java。time。DayOfWeek;

import java。time。LocalDate;

import java。time。temporal。*;

/**

* 假如員工一個月中領取工資,發薪日是每個月的15號,如果發薪日是週末,則調整為週五。

*/

public class PayDayAdjuster implements TemporalAdjuster {

@Override

public Temporal adjustInto(Temporal temporal) {

//1。將temporal轉換為子類物件LocalDate,from方法可以將任何時態物件轉換為

LocalDate。

LocalDate payDay = LocalDate。from(temporal);

//2。判斷當前封裝的時間中的日期是不是當月15日,如果不是,則更改為15日。

int day;

if (payDay。getDayOfMonth() != 15) {

day = 15;

} else {

day = payDay。getDayOfMonth();

}

LocalDate realPayDay = payDay。withDayOfMonth(day);

//3。判斷realPayDay物件中封裝的星期數是不是週六或者是週日,如果是週末或者是週日則

更改為週五。

if (realPayDay。getDayOfWeek() == DayOfWeek。SUNDAY ||

realPayDay。getDayOfWeek() == DayOfWeek。SATURDAY) {

//說明發薪日是週末,則更改為週五。

realPayDay =

realPayDay。with(TemporalAdjusters。previous(DayOfWeek。FRIDAY));

}

return realPayDay;

TemporalQuery的應用

學習的時態類物件(LocalDate,LocalTime)都有一個方法叫做query,可以針對日期進行查詢。

R query(TemporalQuery query)這個方法是一個泛型方法,返回的資料就是傳入的泛型類的類

型,TemporalQuery是一個泛型介面,裡面有一個抽象方法是

R queryFrom(TemporalAccessor temporal),TemporalAccessor是Temporal的父介面,實際

上也就是LocalDate,LocalDateTime相關類的頂級父介面,這個queryFrom的方法的實現邏輯就

是,傳入一個日期/時間物件透過自定義邏輯返回資料。

}

}

package com。itheima。time;

import com。itheima。time。impl。PayDayAdjuster;

import java。time。DayOfWeek;

import java。time。LocalDate;

import java。time。temporal。TemporalAdjusters;

public class Java8TimeTemporalAdjusterTest1 {

public static void main(String[] args) {

//封裝LocalDate物件為2018年12月1日。

LocalDate payDay = LocalDate。of(2019, 12, 1);

//2018年12月15日為週末,所以要提前到週五發放工資,透過自定義調節器完成對時間的修

改。

LocalDate realPayDay = LocalDate。from(new

PayDayAdjuster()。adjustInto(payDay));

System。out。println(“預計的發薪日是2018年12月15日,實際的發薪日為:” +

realPayDay);

}

}

如果要計劃日期距離某一個特定天數差距多少天,可以自定義類實現TemporalQuery介面並且作為

引數傳入到query方法中。

例:計算當前時間距離下一個勞動節還有多少天?

package com。itheima。time。impl;

import java。time。LocalDate;

import java。time。Month;

import java。time。temporal。ChronoField;

import java。time。temporal。ChronoUnit;

import java。time。temporal。TemporalAccessor;

import java。time。temporal。TemporalQuery;

/**

* 獲取某一天距離下一個勞動節的相隔天數的實現類。

*/

public class UntilDayQueryImpl implements TemporalQuery {

@Override

public Long queryFrom(TemporalAccessor temporal) {

//獲取當前的年/月/日資訊。

int year = temporal。get(ChronoField。YEAR);

int month = temporal。get(ChronoField。MONTH_OF_YEAR);

int day = temporal。get(ChronoField。DAY_OF_MONTH);

//將獲取到的資料封裝為一個LocalDate物件。

LocalDate time = LocalDate。of(year, month, day);

//封裝勞動節的時間,年引數傳遞year,month和day是5和1。

LocalDate laborDay = LocalDate。of(year, Month。MAY,1);

//判斷當前時間是否已經超過了當年的勞動節,如果超過了,則laborDay+1年。

if (time。isAfter(laborDay)){

laborDay = laborDay。plusYears(1);

}

//透過ChronoUnit的between方法計算兩個時間點的差額。

long l = ChronoUnit。DAYS。between(time, laborDay);

return l;

}

}

package com。itheima。time;

import com。itheima。time。impl。UntilDayQueryImpl;

import java。time。LocalDate;

public class Java8TimeTemporalQueryDemo1 {

public static void main(String[] args) {

//封裝LocalDate物件為當前時間。

LocalDate time = LocalDate。now();

//呼叫time物件的query方法查詢距離下一個五一勞動節還有多少天。

Long l = time。query(new UntilDayQueryImpl());

System。out。println(“距離下一個五一勞動節還有:” + l + “天。”);

}

}

練習

計算2018年5月30號與下一個的聖誕節/兒童節/勞動節各相差多少天?

假如員工一個月中領取工資,發薪日是每個月的15號,如果發薪日是週六,如果發薪日是週日,

則調整為下週五,如何實現?

5。java。util。Date與java。time。LocalDate的轉換

對於老專案的改造,需要將Date或者Calendar轉換為java。time包中相應的類的,可

以根據本小節中提供的方法進行改造。

Java8中的java。time包中並沒有提供太多的內建方式來轉換java。util包中用預處

理標準日期和時間的類,我們可以使用Instant類作為中介,也可以使用

java。sql。Date和java。sql。Timestamp類提供的方法進行轉換。

使用Instant類將java。util。Date轉換為java。time。LocalDate

java。time包中並沒有提供很多的方式來進行直接轉換,但是給之前的Date類,Calendar類在

Java1。8都提供了一個新的方法,叫做toInstant,可以將當前物件轉換為Instant物件,透過給

Instant新增時區資訊之後就可以轉換為LocalDate物件。

package com。itheima。time。convert;

import java。time。Instant;

import java。time。LocalDate;

import java。time。ZoneId;

import java。time。ZonedDateTime;

import java。util。Date;

public class Java8TimeDateToLocalDateDemo1 {

public static void main(String[] args) {

//初始化Date物件。

Date d = new Date();

//將Date類物件轉換為Instant類物件。

Instant i = d。toInstant();

//Date類包含日期和時間資訊,但是並不提供時區資訊,和Instant類一樣,可以透過

Instant類的atZone方法新增時區資訊之後進行轉換。

ZonedDateTime zonedDateTime = i。atZone(ZoneId。systemDefault());

//將ZonedDateTime透過toLocalDate方法轉換為LocalDate物件。

LocalDate localDate = zonedDateTime。toLocalDate();

System。out。println(“轉換之前的Date物件是:” + d);

System。out。println(“轉換之後的LocalDate物件是:” + localDate);

}

}

java。sql。Date類中的轉換方法使用

java。sql。Date類中提供直接轉換為LocalDate的方法,toLocalDate。

java。sql。Timestamp類中的轉換方法使用

TimeStamp是時間戳物件,透過傳入一個毫秒值物件進行初始化。

package com。itheima。time。convert;

import java。time。*;

import java。sql。Date;

public class Java8TimeDateToLocalDateDemo2 {

public static void main(String[] args) {

//初始化java。sql。Date物件。

Date d = new Date(System。currentTimeMillis());

//將java。sql。Date物件透過toLocalDate方法轉換為LocalDate物件。

LocalDate localDate = d。toLocalDate();

System。out。println(“轉換前的java。sql。Date物件是:” + d);

System。out。println(“轉換後的LocalDate物件是:” + localDate);

}

}

package com。itheima。time。convert;

import java。sql。Date;

import java。sql。Time;

import java。sql。Timestamp;

import java。time。LocalDate;

import java。time。LocalDateTime;

public class Java8TimeDateToLocalDateDemo3 {

public static void main(String[] args) {

//初始化java。sql。Timestamp物件。

Timestamp t = new Timestamp(System。currentTimeMillis());

//將java。sql。Timestamp物件透過toLocalDateTime方法轉換為LocalDateTime對

象。

LocalDateTime localDateTime = t。toLocalDateTime();

System。out。println(“轉換之前的Timestamp物件是:” + t);

System。out。println(“轉換之後的LocalDateTime物件是:” + localDateTime);

}

}

將java。util包中的類轉換為java。time包中的相應類

透過編寫轉換工具類達到傳入Date物件直接進行轉換的轉換或者將新的日期時間類轉換為Date對

象。

package com。itheima。time。convert;

import java。time。LocalDate;

import java。time。LocalDateTime;

import java。time。ZoneId;

import java。time。ZonedDateTime;

import java。util。Date;

/**

* 編寫工具類傳入不同的物件可以轉換為對應的物件。

*/

public class Java8TimeConvertTool {

/**

* 將java。sql。Date轉換為LocalDate

*

* @param date

* @return

*/

public static LocalDate convertFromSqlDateToLocalDate(java。sql。Date

date) {

return date。toLocalDate();

}

/**

* 將LocalDate轉換為java。sql。Date

* @param date

* @return

*/

public static java。sql。Date convertFromLocalDateToSqlDate(LocalDate

date) {

return java。sql。Date。valueOf(date);

}

/**

* 將java。util。Date轉換為LocalDate

* @param date

* @return

*/

public static LocalDate convertFromUtilDateToLocalDate(java。util。Date

date) {

return

date。toInstant()。atZone(ZoneId。systemDefault())。toLocalDate();

}

/**

將java。util。Date類轉換為java。time。LocalDate類的第二種方法

java。sql。Date類提供了轉換為LocalDate的方法,那麼可以將java。util。Date先轉換為

java。sql。Date。

透過java。sql。Date的構造方法直接傳入一個毫秒值可以構造一個java。sql。Date物件,毫秒值可

以透過java。util。Date物件的getTime方法獲取到。

* 將java。sql。Timestamp轉換為LocalDateTime

* @param timestamp

* @return

*/

public static LocalDateTime

convertFromTimestampToLocalDateTime(java。sql。Timestamp timestamp) {

return timestamp。toLocalDateTime();

}

/**

* 將LocalDateTime轉換為java。sql。Timestamp

* @param localDateTime

* @return

*/

public static java。sql。Timestamp

convertFromLocalDateTimeToTimestamp(LocalDateTime localDateTime) {

return java。sql。Timestamp。valueOf(localDateTime);

}

/**

* 將LocalDate轉換為java。util。Date

* @param date

* @return

*/

public static java。util。Date convertFromLocalDateToUtilDate(LocalDate

date){

ZonedDateTime zonedDateTime =

date。atStartOfDay(ZoneId。systemDefault());

return Date。from(zonedDateTime。toInstant());

}

}

package com。itheima。time。convert;

import java。time。Instant;

import java。time。LocalDate;

import java。time。ZoneId;

import java。time。ZonedDateTime;

import java。util。Date;

public class Java8TimeDateToLocalDateDemo4 {

public static void main(String[] args) {

//初始化Date物件。

Date d = new Date();

/*

java。sql。Date類提供了轉換為LocalDate的方法,那麼可以將java。util。Date先轉換

為java。sql。Date。

將java。util。Calendar類轉換為java。time。ZonedDateTime類

Calendar物件自Java1。1開始提供了一個方法獲取時區物件的方法,getTimeZone,要將Calendar

物件轉換為ZonedDateTime需要先獲取到時區物件。從Java1。8開始TimeZone類提供了一個方法可

以獲取到ZonedId。獲取到zoneId之後就可以初始化ZonedDateTime物件了,ZonedDateTime類有

一個ofInstant方法,可以將一個Instant物件和ZonedId物件作為引數傳入構造一個

ZonedDateTime物件。

透過java。sql。Date的構造方法直接傳入一個毫秒值可以構造一個java。sql。Date物件,

毫秒值可以透過java。util。Date物件的getTime方法獲取到。

*/

java。sql。Date date = new java。sql。Date(d。getTime());

//將java。sql。Date轉化為LocalDate。

LocalDate localDate = date。toLocalDate();

System。out。println(“轉換前的java。util。Date類物件是:” + d);

System。out。println(“轉換後的LocalDate類物件是:” + localDate);

}

}

package com。itheima。time。convert;

import java。time。LocalDate;

import java。time。ZoneId;

import java。time。ZonedDateTime;

import java。util。Calendar;

import java。util。Date;

import java。util。TimeZone;

public class Java8TimeCalendarToLocalDateDemo1 {

public static void main(String[] args) {

//初始化Canlendar物件。

Calendar cal = Calendar。getInstance();

//Calendar物件自Java1。1開始提供了一個方法獲取時區物件的方法,getTimeZone,要將

Calendar物件轉換為ZonedDateTime需要先獲取到時區物件。

TimeZone timeZone = cal。getTimeZone();

//從Java1。8開始TimeZone類提供了一個方法可以獲取到ZonedId。

ZoneId zoneId = timeZone。toZoneId();

//獲取到zoneId之後就可以初始化ZonedDateTime物件了,ZonedDateTime類有一個

ofInstant方法,可以將一個Instant物件和ZonedId物件作為引數傳入構造一個ZonedDateTime對

象。

ZonedDateTime zonedDateTime =

ZonedDateTime。ofInstant(cal。toInstant(), zoneId);

System。out。println(“轉換前的Calendar物件是:” + cal);

System。out。println(“轉換後的ZonedDateTime物件是:” + zonedDateTime);

}

}

將java。util。Calendar類轉換為java。time。LocalDateTime類

Calendar物件可以獲取到年月日時分秒的資訊,這些資訊可以作為LocalDateTime構造方法的參

數。

注意Calendar獲取到的月份依舊是從0開始的,需要在原有的基礎上+1,如果不

加1,輕則月份少算了1個月,重則出現異常。

6。日期的解析與格式化DateTimeFormatter

SimpleDateFormat類在剛開始的講過了是執行緒不安全的,所以Java8提供了新的格

式化類 DateTimeFormatter。

DateTimeFormatter類提供了大量預定義格式化器,包括常量(如

ISO_LOCAL_DATE),模式字母(如yyyy-MM-dd)以及本地化樣式。

package com。itheima。time。convert;

import java。time。LocalDateTime;

import java。time。ZoneId;

import java。time。ZonedDateTime;

import java。util。Calendar;

import java。util。TimeZone;

public class Java8TimeCalendarToLocalDateTimeDemo1 {

public static void main(String[] args) {

//初始化Canlendar物件。

Calendar cal = Calendar。getInstance();

//透過Getter方法獲取到Calendar物件中封裝的資料。

int year = cal。get(Calendar。YEAR);

int month = cal。get(Calendar。MONTH);

int day = cal。get(Calendar。DAY_OF_MONTH);

int hour = cal。get(Calendar。HOUR);

int minute = cal。get(Calendar。MINUTE);

int second = cal。get(Calendar。SECOND);

//將以上獲取到的資料作為LocalDateTime的of方法的引數進行物件的封裝。

LocalDateTime localDateTime = LocalDateTime。of(year, month + 1, day,

hour, minute, second);

System。out。println(“轉換前的Calendar物件是:” + cal);

System。out。println(“轉換後的LocalDateTime物件是:” + localDateTime);

}

}

與SimpleDateFormat不同的是,新版本的日期/時間API的格式化與解析不需要在創

建轉換器物件再進行轉換了,透過時間日期物件的parse/format方法可以直接進行

轉換。

LocalDate類定義的parse和format方法

format方法需要傳入一個DateTimeFormatter物件,實際上檢視DateTimeFormatter類之後,指定

DateTimeFormatter中的常量即可指定格式化方法,常用的格式化方式有ISO_DATE_TIME/ISO_DATE。

package com。itheima。time;

import java。time。LocalDateTime;

import java。time。format。DateTimeFormatter;

public class Java8TimeFormatAndParseDemo1 {

public static void main(String[] args) {

//對LocalDateTime進行格式化與解析,初始化LocalDateTime物件。

LocalDateTime time = LocalDateTime。now();

//DateTimeFormatter類中定義了很多方式,透過常量名可以指定格式化方式。

String result = time。format(DateTimeFormatter。ISO_DATE_TIME);

System。out。println(“ISO_DATE_TIME格式化之後的String是:” + result);

String result1 = time。format(DateTimeFormatter。ISO_DATE);

System。out。println(“ISO_DATE格式化之後的String是:” + result1);

//解析字串的方式透過LocalDateTime類的靜態方法parse方法傳入需要解析的字串即可。

LocalDateTime localDateTime = LocalDateTime。parse(result);

System。out。println(“解析了字串之後的LocalDateTime是:” + localDateTime);

對日期進行格式化

透過DateTimeFormatter的ofLocalizedDate的方法也可以調整格式化的方

式。

此方法需要傳入一個FormatStyle類物件,檢視後發現FormatStyle物件是一個列舉類,其中有幾

種方式如下。

Full:全顯示(年月日+星期) Long:全顯示(年月日) Medium:縮略顯示(沒有年

月日漢字) SHORT:精簡顯示(精簡年+月日)

}

}

package com。itheima。time;

import java。time。LocalDateTime;

import java。time。format。DateTimeFormatter;

import java。time。format。FormatStyle;

public class Java8TimeFormatAndParseDemo2 {

public static void main(String[] args) {

//對LocalDateTime進行格式化與解析,初始化LocalDateTime物件。

LocalDateTime time = LocalDateTime。now();

//透過DateTimeFormatter的ofLocalizedDate指定解析格式也可以格式化日期

String r1 =

time。format(DateTimeFormatter。ofLocalizedDate(FormatStyle。FULL));

String r2 =

time。format(DateTimeFormatter。ofLocalizedDate(FormatStyle。LONG));

String r3 =

time。format(DateTimeFormatter。ofLocalizedDate(FormatStyle。MEDIUM));

String r4 =

time。format(DateTimeFormatter。ofLocalizedDate(FormatStyle。SHORT));

System。out。println(“FULL:”+r1);

System。out。println(“LONG:”+r2);

System。out。println(“MEDIUM:”+r3);

System。out。println(“SHORT:”+r4);

}

}

注意此種方式在不同時區的顯示方式不一樣,在其他時區不會顯示中文,會根據當前

系統的預設時區來進行區別顯示。

自定義格式化格式

除了系統的自帶的方式之外,也可以透過DateTimeFormatter類提供的

ofPattern方式建立自定時格式化器,格式化的寫法與之前使用的

SimpleDateFormat相同。

練習

將1998年3月18日17時19分28秒格式化為以下格式

package com。itheima。time;

import java。time。LocalDateTime;

import java。time。format。DateTimeFormatter;

import java。time。format。FormatStyle;

public class Java8TimeFormatAndParseDemo3 {

public static void main(String[] args) {

//對LocalDateTime進行格式化與解析,初始化LocalDateTime物件。

LocalDateTime time = LocalDateTime。now();

//透過透過DateTimeFormatter的ofPattern方法可以自定義格式化模式。

String result = time。format(DateTimeFormatter。ofPattern(“yyyy/MM/dd

HH:mm:ss:SSS”));

System。out。println(“LocalDateTime格式化前是:” + time);

System。out。println(“LocalDateTime格式化後是:” + result);

}

}

1998-3月-18——-17:19分28秒

JavaSE原理-日期類API