20 個案例教你在 Java 8 中如何處理日期和時間?

来源:https://www.cnblogs.com/springforall/archive/2019/08/16/11361439.html
-Advertisement-
Play Games

前言 前面一篇文章寫了 "《SimpleDateFormat 如何安全的使用?》" , 裡面介紹了 SimpleDateFormat 如何處理日期/時間,以及如何保證線程安全,及其介紹了在 Java 8 中的處理時間/日期預設就線程安全的 DateTimeFormatter 類。那麼 Java 8 ...


前言

前面一篇文章寫了《SimpleDateFormat 如何安全的使用?》, 裡面介紹了 SimpleDateFormat 如何處理日期/時間,以及如何保證線程安全,及其介紹了在 Java 8 中的處理時間/日期預設就線程安全的 DateTimeFormatter 類。那麼 Java 8 中該怎麼樣處理生活中常見的一些日期/時間呢?比如:計算一周後的日期;計算一年前或一年後的日期;檢查閏年等。

接下來創建了 20 個基於任務的實例來學習 Java 8 的新特性。從最簡單創建當天的日期開始,然後創建時間及時區,接著模擬一個日期提醒應用中的任務——計算重要日期的到期天數,例如生日、紀念日、賬單日、保費到期日、信用卡過期日等。

示例 1、在 Java 8 中獲取今天的日期

Java 8 中的 LocalDate 用於表示當天日期。和 java.util.Date 不同,它只有日期,不包含時間。當你僅需要表示日期時就用這個類。

1LocalDate now = LocalDate.now();
2System.out.println(now);

結果是:

12018-06-20

上面的代碼創建了當天的日期,不含時間信息。列印出的日期格式非常友好,不像老的 Date 類列印出一堆沒有格式化的信息。

示例 2、在 Java 8 中獲取年、月、日信息

LocalDate 類提供了獲取年、月、日的快捷方法,其實例還包含很多其它的日期屬性。通過調用這些方法就可以很方便的得到需要的日期信息,不用像以前一樣需要依賴 java.util.Calendar 類了

1LocalDate now = LocalDate.now();
2int year = now.getYear();
3int monthValue = now.getMonthValue();
4int dayOfMonth = now.getDayOfMonth();
5System.out.printf("year = %d, month = %d, day = %d", year, monthValue, dayOfMonth);

結果是:

1year = 2018, month = 6, day = 20

示例 3、在 Java 8 中處理特定日期

在第一個例子里,我們通過靜態工廠方法 now() 非常容易地創建了當天日期,你還可以調用另一個有用的工廠方法LocalDate.of() 創建任意日期, 該方法需要傳入年、月、日做參數,返回對應的 LocalDate 實例。這個方法的好處是沒再犯老 API 的設計錯誤,比如年度起始於 1900,月份是從 0 開始等等。日期所見即所得,就像下麵這個例子表示了 6 月 20 日,沒有任何隱藏機關。

1LocalDate date = LocalDate.of(2018, 06, 20);
2System.out.println(date);

可以看到創建的日期完全符合預期,與寫入的 2018 年 6 月 20 日完全一致。

示例 4、在 Java 8 中判斷兩個日期是否相等

現實生活中有一類時間處理就是判斷兩個日期是否相等。你常常會檢查今天是不是個特殊的日子,比如生日、紀念日或非交易日。這時就需要把指定的日期與某個特定日期做比較,例如判斷這一天是否是假期。下麵這個例子會幫助你用 Java 8 的方式去解決,你肯定已經想到了,LocalDate 重載了 equal 方法,請看下麵的例子:

1LocalDate now = LocalDate.now();
2LocalDate date = LocalDate.of(2018, 06, 20);
3if (date.equals(now)) {
4    System.out.println("同一天");
5}

這個例子中我們比較的兩個日期相同。註意,如果比較的日期是字元型的,需要先解析成日期對象再作判斷。

示例 5、在 Java 8 中檢查像生日這種周期性事件

Java 中另一個日期時間的處理就是檢查類似每月賬單、結婚紀念日、EMI日或保險繳費日這些周期性事件。如果你在電子商務網站工作,那麼一定會有一個模塊用來在聖誕節、感恩節這種節日時向客戶發送問候郵件。Java 中如何檢查這些節日或其它周期性事件呢?答案就是 MonthDay 類。這個類組合了月份和日,去掉了年,這意味著你可以用它判斷每年都會發生事件。和這個類相似的還有一個 YearMonth 類。這些類也都是不可變並且線程安全的值類型。下麵我們通過 MonthDay 來檢查周期性事件:

1LocalDate now = LocalDate.now();
2LocalDate dateOfBirth = LocalDate.of(2018, 06, 20);
3MonthDay birthday = MonthDay.of(dateOfBirth.getMonth(), dateOfBirth.getDayOfMonth());
4MonthDay currentMonthDay = MonthDay.from(now);
5if (currentMonthDay.equals(birthday)) {
6    System.out.println("Happy Birthday");
7} else {
8    System.out.println("Sorry, today is not your birthday");
9}

結果:(註意:獲取當前時間可能與你看的時候不對,所以這個結果可能和你看的時候運行結果不一樣)

1Happy Birthday

只要當天的日期和生日匹配,無論是哪一年都會列印出祝賀信息。你可以把程式整合進系統時鐘,看看生日時是否會受到提醒,或者寫一個單元測試來檢測代碼是否運行正確。

示例 6、在 Java 8 中獲取當前時間

與 Java 8 獲取日期的例子很像,獲取時間使用的是 LocalTime 類,一個只有時間沒有日期的 LocalDate 近親。可以調用靜態工廠方法 now() 來獲取當前時間。預設的格式是 hh:mm:ss:nnn。

1LocalTime localTime = LocalTime.now();
2System.out.println(localTime);

結果:

113:35:56.155

可以看到當前時間就只包含時間信息,沒有日期。

示例 7、如何在現有的時間上增加小時

通過增加小時、分、秒來計算將來的時間很常見。Java 8 除了不變類型和線程安全的好處之外,還提供了更好的plusHours() 方法替換 add(),並且是相容的。註意,這些方法返回一個全新的 LocalTime 實例,由於其不可變性,返回後一定要用變數賦值。

1LocalTime localTime = LocalTime.now();
2System.out.println(localTime);
3LocalTime localTime1 = localTime.plusHours(2);//增加2小時
4System.out.println(localTime1);

結果:

113:41:20.721
215:41:20.721

可以看到,新的時間在當前時間 13:41:20.721 的基礎上增加了 2 個小時。

示例 8、如何計算一周後的日期

和上個例子計算兩小時以後的時間類似,這個例子會計算一周後的日期。LocalDate 日期不包含時間信息,它的 plus()方法用來增加天、周、月,ChronoUnit 類聲明瞭這些時間單位。由於 LocalDate 也是不變類型,返回後一定要用變數賦值。

1LocalDate now = LocalDate.now();
2LocalDate plusDate = now.plus(1, ChronoUnit.WEEKS);
3System.out.println(now);
4System.out.println(plusDate);

結果:

12018-06-20
22018-06-27

可以看到新日期離當天日期是 7 天,也就是一周。你可以用同樣的方法增加 1 個月、1 年、1 小時、1 分鐘甚至一個世紀,更多選項可以查看 Java 8 API 中的 ChronoUnit 類。

示例 9、計算一年前或一年後的日期

繼續上面的例子,上個例子中我們通過 LocalDate 的 plus() 方法增加天數、周數或月數,這個例子我們利用 minus() 方法計算一年前的日期。

1LocalDate now = LocalDate.now();
2LocalDate minusDate = now.minus(1, ChronoUnit.YEARS);
3LocalDate plusDate1 = now.plus(1, ChronoUnit.YEARS);
4System.out.println(minusDate);
5System.out.println(plusDate1);

結果:

12017-06-20
22019-06-20

示例 10、使用 Java 8 的 Clock 時鐘類

Java 8 增加了一個 Clock 時鐘類用於獲取當時的時間戳,或當前時區下的日期時間信息。以前用到System.currentTimeInMillis() 和 TimeZone.getDefault() 的地方都可用 Clock 替換。

1Clock clock = Clock.systemUTC();
2Clock clock1 = Clock.systemDefaultZone();
3System.out.println(clock);
4System.out.println(clock1);

結果:

1SystemClock[Z]
2SystemClock[Asia/Shanghai]

示例 11、如何用 Java 判斷日期是早於還是晚於另一個日期

另一個工作中常見的操作就是如何判斷給定的一個日期是大於某天還是小於某天?在 Java 8 中,LocalDate 類有兩類方法 isBefore() 和 isAfter() 用於比較日期。調用 isBefore() 方法時,如果給定日期小於當前日期則返回 true。

1 LocalDate tomorrow = LocalDate.of(2018,6,20);
2 if(tomorrow.isAfter(now)){
3     System.out.println("Tomorrow comes after today");
4 }
5 LocalDate yesterday = now.minus(1, ChronoUnit.DAYS);
6 if(yesterday.isBefore(now)){
7     System.out.println("Yesterday is day before today");
8 }

在 Java 8 中比較日期非常方便,不需要使用額外的 Calendar 類來做這些基礎工作了。

示例 12、在 Java 8 中處理時區

Java 8 不僅分離了日期和時間,也把時區分離出來了。現在有一系列單獨的類如 ZoneId 來處理特定時區,ZoneDateTime 類來表示某時區下的時間。這在 Java 8 以前都是 GregorianCalendar 類來做的。

1ZoneId america = ZoneId.of("America/New_York");
2LocalDateTime localtDateAndTime = LocalDateTime.now();
3ZonedDateTime dateAndTimeInNewYork  = ZonedDateTime.of(localtDateAndTime, america );
4System.out.println(dateAndTimeInNewYork);

示例 13、如何表示信用卡到期這類固定日期,答案就在 YearMonth

與 MonthDay 檢查重覆事件的例子相似,YearMonth 是另一個組合類,用於表示信用卡到期日、FD 到期日、期貨期權到期日等。還可以用這個類得到 當月共有多少天,YearMonth 實例的 lengthOfMonth() 方法可以返回當月的天數,在判斷 2 月有 28 天還是 29 天時非常有用。

1YearMonth currentYearMonth = YearMonth.now();
2System.out.printf("Days in month year %s: %d%n", currentYearMonth, currentYearMonth.lengthOfMonth());
3YearMonth creditCardExpiry = YearMonth.of(2018, Month.FEBRUARY);
4System.out.printf("Your credit card expires on %s %n", creditCardExpiry);

結果:

1Days in month year 2018-06: 30
2Your credit card expires on 2018-02

示例 14、如何在 Java 8 中檢查閏年

LocalDate 類有一個很實用的方法 isLeapYear() 判斷該實例是否是一個閏年。

示例 15、計算兩個日期之間的天數和月數

有一個常見日期操作是計算兩個日期之間的天數、周數或月數。在 Java 8 中可以用 java.time.Period 類來做計算。下麵這個例子中,我們計算了當天和將來某一天之間的月數。

1LocalDate date = LocalDate.of(2019, Month.MARCH, 20);
2Period period = Period.between(now, date);
3System.out.println("離下個時間還有" + period.getMonths() + " 個月");

示例 16、包含時差信息的日期和時間

在 Java 8 中,ZoneOffset 類用來表示時區,舉例來說印度與 GMT 或 UTC 標準時區相差 +05:30,可以通過ZoneOffset.of() 靜態方法來獲取對應的時區。一旦得到了時差就可以通過傳入 LocalDateTime 和 ZoneOffset 來創建一個 OffSetDateTime 對象。

1LocalDateTime datetime = LocalDateTime.of(2014, Month.JANUARY, 14,19,30);
2ZoneOffset offset = ZoneOffset.of("+05:30");
3OffsetDateTime date = OffsetDateTime.of(datetime, offset);  
4System.out.println("Date and Time with timezone offset in Java : " + date);

示例 17、在 Java 8 中獲取當前的時間戳

如果你還記得 Java 8 以前是如何獲得當前時間戳,那麼現在你終於解脫了。Instant 類有一個靜態工廠方法 now() 會返回當前的時間戳,如下所示:

1Instant timestamp = Instant.now();
2System.out.println(timestamp);

結果:

12018-06-20T06:35:24.881Z

時間戳信息里同時包含了日期和時間,這和 java.util.Date 很像。實際上 Instant 類確實等同於 Java 8 之前的 Date類,你可以使用 Date 類和 Instant 類各自的轉換方法互相轉換,例如:Date.from(Instant) 將 Instant 轉換成java.util.Date,Date.toInstant() 則是將 Date 類轉換成 Instant 類。

示例 18、在 Java 8 中如何使用預定義的格式化工具去解析或格式化日期

在 Java 8 以前的世界里,日期和時間的格式化非常詭異,唯一的幫助類 SimpleDateFormat 也是非線程安全的,而且用作局部變數解析和格式化日期時顯得很笨重。幸好線程局部變數能使它在多線程環境中變得可用,不過這都是過去時了。Java 8 引入了全新的日期時間格式工具,線程安全而且使用方便。它自帶了一些常用的內置格式化工具。

參見我上一篇文章: 《SimpleDateFormat 如何安全的使用?》

示例 19、如何在 Java 中使用自定義格式化工具解析日期

儘管內置格式化工具很好用,有時還是需要定義特定的日期格式。可以調用 DateTimeFormatter 的 ofPattern() 靜態方法並傳入任意格式返回其實例,格式中的字元和以前代表的一樣,M 代表月,m 代表分。如果格式不規範會拋出 DateTimeParseException 異常,不過如果只是把 M 寫成 m 這種邏輯錯誤是不會拋異常的。

參見我上一篇文章: 《SimpleDateFormat 如何安全的使用?》

示例 20、在 Java 8 中如何把日期轉換成字元串

上兩個主要是從字元串解析日期。現在我們反過來,把 LocalDateTime 日期實例轉換成特定格式的字元串。這是迄今為止 Java 日期轉字元串最為簡單的方式了。下麵的例子將返回一個代表日期的格式化字元串。和前面類似,還是需要創建 DateTimeFormatter 實例並傳入格式,但這回調用的是 format() 方法,而非 parse() 方法。這個方法會把傳入的日期轉化成指定格式的字元串。

1LocalDateTime arrivalDate  = LocalDateTime.now();
2try {
3    DateTimeFormatter format = DateTimeFormatter.ofPattern("MMMdd yyyy  hh:mm a");
4    String landing = arrivalDate.format(format);
5    System.out.printf("Arriving at :  %s %n", landing);
6}catch (DateTimeException ex) {
7    System.out.printf("%s can't be formatted!%n", arrivalDate);
8    ex.printStackTrace();
9}

Java 8 日期時間 API 的重點

通過這些例子,你肯定已經掌握了 Java 8 日期時間 API 的新知識點。現在來回顧一下這個優雅 API 的使用要點:

1)提供了 javax.time.ZoneId 獲取時區。

2)提供了 LocalDate 和 LocalTime 類。

3)Java 8 的所有日期和時間 API 都是不可變類並且線程安全,而現有的 Date 和 Calendar API 中的 java.util.Date 和SimpleDateFormat 是非線程安全的。

4)主包是 java.time, 包含了表示日期、時間、時間間隔的一些類。裡面有兩個子包 java.time.format 用於格式化, java.time.temporal 用於更底層的操作。

5)時區代表了地球上某個區域內普遍使用的標準時間。每個時區都有一個代號,格式通常由區域/城市構成(Asia/Tokyo),在加上與格林威治或 UTC 的時差。例如:東京的時差是 +09:00。

6)OffsetDateTime 類實際上組合了 LocalDateTime 類和 ZoneOffset 類。用來表示包含和格林威治或 UTC 時差的完整日期(年、月、日)和時間(時、分、秒、納秒)信息。

7)DateTimeFormatter 類用來格式化和解析時間。與 SimpleDateFormat 不同,這個類不可變並且線程安全,需要時可以給靜態常量賦值。DateTimeFormatter 類提供了大量的內置格式化工具,同時也允許你自定義。在轉換方面也提供了 parse() 將字元串解析成日期,如果解析出錯會拋出 DateTimeParseException。DateTimeFormatter 類同時還有format() 用來格式化日期,如果出錯會拋出 DateTimeException異常。

8)再補充一點,日期格式“MMM d yyyy”和“MMM dd yyyy”有一些微妙的不同,第一個格式可以解析“Jan 2 2014”和“Jan 14 2014”,而第二個在解析“Jan 2 2014”就會拋異常,因為第二個格式里要求日必須是兩位的。如果想修正,你必須在日期只有個位數時在前面補零,就是說“Jan 2 2014”應該寫成 “Jan 02 2014”。

推薦閱讀:

《深入理解 Java 記憶體模型》讀書筆記

面試-基礎篇

Spring Boot 2.0 遷移指南

SpringBoot使用Docker快速部署項目

為什麼選擇 Spring 作為 Java 框架?

SpringBoot RocketMQ 整合使用和監控

Spring Boot 面試的十個問題

使用 Spring Framework 時常犯的十大錯誤

SpringBoot Admin 使用指南

SpringBoot Kafka 整合使用

SpringBoot RabbitMQ 整合使用

Elasticsearch索引增量統計及定時郵件實現

Elasticsearch實戰 | 必要的時候,還得空間換時間!
乾貨 |《從Lucene到Elasticsearch全文檢索實戰》拆解實踐

上篇好文:

JVM面試問題系列:JVM 配置常用參數和常用 GC 調優策略


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有兩個Android項目,一個為pozhudl,一個為app,現在欲將pozhudl項目作為module導入到app中,並調用pozhudl項目中的類 先在pozhudl項目的build.gradle 中修改這句 apply plugin: 'com.android.application' 為 a ...
  • 1.wxml <input class="weui-input" type='number' bindinput="emailInput"/> 2.js (1) data: { verify:'' }, (2) emailInput: function(e){ this.setData({ veri ...
  • Android多渠道打包 Gradle打包 前言 由於App一般都會在多個應用市場上架,為了分析App在每個不同渠道的具體的數據,一般都會對不同渠道打包不同的App。多渠道打包有多種方式,這裡只介紹利用Gradle進行多渠道打包。 步驟 1、在AndroidManifest.xml中增加配置 其中, ...
  • 文章轉載自:http://www.pythonheidong.com/blog/article/3337/ 序言 招聘高峰期來了,大家都非常積極地準備著跳槽,那麼去一家公司面試就會有一堆新鮮的問題,可能不會,也可能會,但是瞭解不夠深。本篇文章為群里的小伙伴們去寶庫公司的筆試題,由筆者整理並提供筆者個 ...
  • 文章轉載自:http://www.pythonheidong.com/blog/article/3339/ 這些面試題是我在今年年初換工作的時候整理,沒有重點。包括java基礎,數據結構,網路,Android相關等等。適合中高級工程師。由於內容過多,將會分為上下兩部分。下部分跳轉鏈接:http:// ...
  • 文章轉載自:http://www.pythonheidong.com/blog/article/3329/ 本文為開發者奉獻了70道經典Android面試題加答案--重要知識點幾乎都涉及到了,你還等啥,趕緊收藏吧!! 1. 下列哪些語句關於記憶體回收的說明是正確的? (b) A、 程式員必須創建一個線 ...
  • 文章轉載自:http://www.pythonheidong.com/blog/article/3327/ iOS面試題 1.Difference between shallow copy and deep copy? 淺複製和深複製的區別? 淺層複製:指向對象的指針,而不複製引用對象本身。深層複製 ...
  • Data Structure Notes Chapter 1 Sorting Algorithm Insert Sorting: 對於近乎有序的數組可以降到$ O(n)$的時間複雜度。 Merge Sorting: Tips1 :Merge Sort Optimize in nearly order ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...