Java 8爲Date和Time引入了新的API,以解決舊java.util.Date
和java.util.Calendar
的缺點。做爲本文的一部分,讓咱們從現有Date和Calendar API存在的一些問題入手,來探討新的Java 8 Date和Time API如何解決這些問題。咱們還將搞一搞Java 8時間類庫中的核心類,好比LocalDate
, LocalTime
, LocalDateTime
, ZonedDateTime
, Period
, Duration
以及它們的api。java
Date
和Calendar
類不是線程安全的,使開發者難以調試這些api的併發問題,須要編寫額外的代碼來處理線程安全。Java 8中引入的新的Date和Time API是不可變的和線程安全的,使得這些痛點得以解決。 最經常使用的類是LocalDate
,LocalTime
和LocalDateTime
。正如他們的名字所示,它們表明與上下文相結合的本地日期/時間。這些類主要用於不須要在上下文中明確指定時區的狀況。做爲本節的一部分,咱們將介紹最經常使用的API。數據庫
LocalDate
表示在ISO格式(YYYY-MM-DD
)下的不帶具體時間的日期。經常使用於表示生日或者咱們最關心的發工資的的日期。獲取當前系統時鐘下的日期,以下所示:LocalDate localDate = LocalDate.now();
表示特定日,月和年的LocalDate可使用「 of 」方法或使用「 parse 」方法得到。例如,如下代碼段表明2015年2月20日的LocalDate:LocalDate.of(2015, 02, 20); LocalDate.parse("2015-02-20");
是否是很是直觀並且方便呢!LocalDate提供各類實用方法,以得到各類日期信息。讓咱們快速瀏覽一下這些API方法。如下代碼段獲取當前本地日期並添加一天:LocalDate tomorrow = LocalDate.now().plusDays(1)
;此示例獲取當前日期並減去一個月。請注意它是如何接受枚舉做爲時間單位的:LocalDate previousMonthSameDay = LocalDate.now().minus(1, ChronoUnit.MONTHS);
在如下兩個代碼示例中,咱們分析日期「2016-06-12」並分別獲取星期幾和月中的某天。注意返回值,第一個是表示DayOfWeek的對象,而第二個是表示月份的序數值的int:api
DayOfWeek sunday = LocalDate.parse("2019-06-12").getDayOfWeek();
int twelve = LocalDate.parse("2016-09-12").getDayOfMonth();複製代碼
咱們還能夠測試一個日期是否發生在閏年,若是用老方法怕不是要上天:boolean leapYear = LocalDate.now().isLeapYear();
判斷日期的前後:安全
boolean notBefore = LocalDate.parse("2019-06-12").isBefore(LocalDate.parse("2019-06-11"));
boolean isAfter = LocalDate.parse("2019-06-12") .isAfter(LocalDate.parse("2019-06-11"));複製代碼
日期邊界能夠從給定日期得到。在如下兩個示例中,咱們獲得LocalDateTime,它表明給定日期的一天的開始(2016-06-12T00:00)和表明月初的LocalDate(2019-06-01):併發
LocalDateTime beginningOfDay = LocalDate.parse("2019-06-12").atStartOfDay();
LocalDate firstDayOfMonth = LocalDate.parse("2019-09-12").with(TemporalAdjusters.firstDayOfMonth());複製代碼
如今讓咱們來看看咱們如何使用當地時間LocalTime
。測試
在本地時間表示不帶日期的時間。與LocalDate
相似,能夠從系統時鐘或使用「parse」和「of」方法建立LocalTime實例。快速瀏覽下面的一些經常使用API。能夠從系統時鐘建立當前LocalTime的實例,以下所示:LocalTime now = LocalTime.now();
在下面的代碼示例中,咱們經過解析字符串表示建立表示06:30 AM 的LocalTime
:LocalTime sixThirty = LocalTime.parse("06:30");
方法「of」可用於建立LocalTime
。例如,下面的代碼使用「of」方法建立表示06:30 AM的LocalTime
:LocalTime sixThirty = LocalTime.of(6, 30);
下面的示例經過解析字符串來建立LocalTime
,並使用「plus」API爲其添加一小時。結果將是表明07:30 AM的LocalTime
:LocalTime sevenThirty = LocalTime.parse("06:30").plus(1, ChronoUnit.HOURS);
各類getter方法可用於獲取特定的時間單位,如小時,分鐘和秒,以下所示獲取小時:int six = LocalTime.parse("06:30").getHour();
同LocalDate
同樣檢查特定時間是否在另外一特定時間以前或以後。下面的代碼示例比較結果爲true
的兩個LocalTime
:boolean isbefore = LocalTime.parse("06:30").isBefore(LocalTime.parse("07:30"));
一天中的最大,最小和中午時間能夠經過LocalTime類中的常量得到。在執行數據庫查詢以查找給定時間範圍內的記錄時,這很是有用。例如,下面的代碼表明23:59:59.99
:LocalTime maxTime = LocalTime.MAX;
如今讓咱們深刻了解LocalDateTime
。spa
所述LocalDateTime用於表示日期和時間的組合。當咱們須要結合日期和時間時,這是最經常使用的類。該類提供了各類API,咱們將介紹一些最經常使用的API。相似於LocalDate
和LocalTime
從系統時鐘獲取LocalDateTime
的實例:LocalDateTime.now();
下面的代碼示例解釋瞭如何使用工廠「of」和「parse」方法建立實例。結果將是表明2019年2月20日06:30 AM
的LocalDateTime
實例:線程
LocalDateTime.of(2019, Month.FEBRUARY, 20, 06, 30);
LocalDateTime.parse("2019-02-20T06:30:00");複製代碼
有一些實用的API能夠支持特定時間單位的時間運算,例如天,月,年和分鐘。如下代碼示例演示了「加」和「減」方法的用法。這些API的行爲與LocalDate
和LocalTime
中的 API徹底相同:設計
localDateTime.plusDays(1);
localDateTime.minusHours(2);複製代碼
Getter方法可用於提取相似於日期和時間類的特定單位。鑑於上面的LocalDateTime
實例,下面的代碼示例將返回2月份的月份:localDateTime.getMonth();
調試
當咱們須要處理時區特定的日期和時間時,Java 8提供了ZonedDateTime
類。ZoneID是用於表示不一樣區域的標識符。大約有40個不一樣的時區,使用ZoneID
表示它們,以下所示下面的代碼咱們來獲取下「亞洲/上海」
時區:ZoneId zoneId = ZoneId.of("Aisa/Shanghai");
獲取全部的時區:Set
LocalDateTime
轉化爲特定的時區中的時間:ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);
ZonedDateTime提供解析方法來獲取時區的特定日期時間:ZonedDateTime.parse("2019-06-03T10:15:30+01:00[Aisa/Shanghai]");
使用時區的另外一種方法是使用OffsetDateTime
。OffsetDateTime
是具備偏移量的日期時間的不可變表示形式。此類存儲全部日期和時間字段,精確到納秒,以及從UTC/格林威治的偏移量。可使用ZoneOffset
建立OffsetDateTime
實例。這裏咱們建立一個LocalDateTime
來表示2015年2月20日上午6:30
:LocalDateTime localDateTime = LocalDateTime.of(2019, Month.FEBRUARY, 20, 06, 30);
而後咱們經過建立ZoneOffset
併爲LocalDateTime
實例設置來增長兩個小時:
ZoneOffset offset = ZoneOffset.of("+02:00");
OffsetDateTime offSetByTwo = OffsetDateTime.of(localDateTime, offset);複製代碼
咱們如今假定本地日期時間爲2019-02-20 06:30 +02:00
。如今讓咱們繼續討論如何使用Period
和Duration
類修改日期和時間值。
Period
類被普遍地用於修改給定的日期的值或者獲取兩個日期之間的差值:
LocalDate initialDate = LocalDate.parse("2007-05-10");
LocalDate finalDate = initialDate.plus(Period.ofDays(5));複製代碼
Period
類有各類getter方法,如getYears
,getMonths
和getDays
從獲取值週期對象。下面的代碼示例返回一個int
值爲5,是基於上面示例的逆序操做:int five = Period.between(finalDate, initialDate).getDays();
該Period
能夠在特定的單元得到兩個日期之間的如天或月或數年,使用ChronoUnit.between
:int five = ChronoUnit.DAYS.between(finalDate , initialDate);
此代碼示例返回五天。讓咱們繼續看看Duration
類。
相似Period
,Duration
類是用來處理時間。在下面的代碼中,咱們建立一個本地時間上午6:30,而後加30秒的持續時間,以使本地時間上午6時30分30秒的:
LocalTime initialTime = LocalTime.of(6, 30, 0);
LocalTime finalTime = initialTime.plus(Duration.ofSeconds(30));複製代碼
兩個時刻之間的持續時間能夠做爲持續時間或做爲特定單位得到。在第一個代碼片斷中,咱們使用Duration
類的between()
方法來查找finalTime
和initialTime
之間的時間差,並以秒爲單位返回差別:int thirty = Duration.between(finalTime, initialTime).getSeconds();
在第二個例子中,咱們使用ChronoUnit
類的between()
方法來執行相同的操做:int thirty = ChronoUnit.SECONDS.between(finalTime, initialTime);
如今咱們來看看如何將舊的Date
和Calendar
轉換爲新的Date
和Time
。
Java 8添加了toInstant()
方法,該方法有助於將舊API中的Date和Calendar實例轉換爲新的Date Time API,以下面的代碼片斷所示:
LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault());複製代碼
所述LocalDateTime
能夠從以下「ofEpochSecond"方法來構造。如下代碼的結果將是表明2019-06-13T11:34:50
的LocalDateTime
:LocalDateTime.ofEpochSecond(1465817690, 0, ZoneOffset.UTC);
如今讓咱們繼續進行日期和時間格式化。
Java 8提供了用於輕鬆格式化日期和時間的 API :LocalDateTime localDateTime = LocalDateTime.of(2019, Month.JANUARY, 25, 6, 30);
如下代碼傳遞ISO日期格式以格式化本地日期。結果將是2019-01-25
:String localDateString = localDateTime.format(DateTimeFormatter.ISO_DATE);
該DateTimeFormatter
提供多種標準格式選項。也能夠提供自定義模式來格式化方法,以下所示對上面的例子進行自定義格式化,它將返回LocalDate
爲2019/01/25
:localDateTime.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))
;咱們能夠將格式樣式傳遞爲SHORT
,LONG
或MEDIUM
做爲格式化選項的一部分。下面的代碼示例將在2019年1月25日06:30:00
給出表示LocalDateTime
的輸出:localDateTime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.UK);
最後讓咱們看看Java 8 Core Date / Time API 可用的替代方案。並非全部的項目都使用java 8。
對於從Java 7或Java 6這些老項目來講可使用Threeten ,而後能夠像在上面java 8同樣使用相同的功能,一旦你遷移到java 8 只須要修改你的包路徑代碼而無需變動。經過在項目中引用如下pom依賴項就能夠當即使用:
<dependency>
<groupId>org.threeten</groupId>
<artifactId>threetenbp</artifactId>
<version>LATEST</version>
</dependency>複製代碼
Java 8 日期和時間庫的另外一種替代方案是老牌時間處理類庫Joda-Time。事實上,Java 8 Date Time API 吸取了大量的Joda-Time庫。該庫提供了Java 8 Date Time項目中支持的幾乎全部功能。經過在項目中引用如下pom依賴項就能夠當即使用:
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>LATEST</version>
</dependency>複製代碼
關注公衆號:碼農小胖哥,獲取更多資訊