最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java新舊時間日期API的使用和避坑指南

 更新時間:2025年05月24日 10:19:46   作者:litbluehandy  
這篇文章主要介紹了Java新舊時間日期API的使用和避坑指南,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

在 Java 8 之前,我們處理日期時間需求時,使用 Date、Calender 和 SimpleDateFormat,來聲明時間戳、使用日歷處理日期和格式化解析日期時間。但是,這些類的 API 的缺點比較明顯,比如可讀性差、易用性差、使用起來冗余繁瑣,還有線程安全問題。因此,Java 8 推出了新的日期時間類。

每一個類功能明確清晰、類之間協(xié)作簡單、API 定義清晰不踩坑,API 功能強大無需借助外部工具類即可完成操作,并且線程安全。

Java 8引入了三個新的日期時間類,分別是LocalDateLocalTimeLocalDateTime,分別處理日期、時間和日期時間。

一、新的時間和日期API

1.1 獲取當(dāng)前時間

LocalDateTime localDateTime = LocalDateTime.now();

System.out.println("當(dāng)前時刻:" + localDateTime );
System.out.println("當(dāng)前年:" + localDateTime.getYear() +
                   "\n當(dāng)前月:" + localDateTime.getMonth() +
                   "\n當(dāng)前日:" + localDateTime.getDayOfMonth());

System.out.println("當(dāng)前時/分/秒:" +
                   localDateTime.getHour() +" / " +
                   localDateTime.getMinute() + "/" +
                   localDateTime.getSecond());


/* 
 * 打印結(jié)果
 * 
 * 當(dāng)前時刻:2020-09-04T22:11:27.505361600
 * 當(dāng)前年:2020
 * 當(dāng)前月:SEPTEMBER
 * 當(dāng)前日:4
 * 當(dāng)前時/分/秒:  22/13/48
 */

1.2 構(gòu)造一個指定年月日的時間

比如構(gòu)造:2019年8月30日18時26分30秒,大約是我對小方表白的時刻。

LocalDateTime specifiedTime = LocalDateTime.of(2019, Month.AUGUST, 30, 18, 26, 30);
System.out.println("構(gòu)造時間:" + specifiedTime );

/**
 * 打印結(jié)果
 * 
 * 構(gòu)造時間:2019-08-30T18:26:30
 */

1.3 修改日期

LocalDateTime updateTime = LocalDateTime.now();
// 增加1個月
updateTime.plusMonths(1);
// 減少2天
updateTime.minusDays(2);
// 直接修改到2028年
updateTime.withYear(2028);
// 直接修改到本月的第28天
updateTime.withDayOfMonth(28);
// 組合條件修改
updateTime.withDayOfMonth(12).withYear(2060).minusDays(1);

1.4 格式化日期

LocalDateTime formatTime = LocalDateTime.now();
String type1 = formatTime.format(DateTimeFormatter.BASIC_ISO_DATE);
String type2 = formatTime.format(DateTimeFormatter.ISO_DATE);
String type3 = formatTime.format(DateTimeFormatter.ofPattern("yyyy-/-MM-/-dd"));

System.out.println("formatTime1:" + type1 +
                   "\nformatTime2: " + type2 +
                   "\nformatTime3: " + type3);

/**
 * 輸出:
 * formatTime1:20200904
 * formatTime2: 2020-09-04
 * formatTime3: 2020-/-09-/-04
 */

1.5 計算時間差

Java 8 中有一個專門的類 Period 定義了日期間隔,通過Period.between 得到了兩個LocalDate 的差,返回的是兩個日期差幾年零幾月零幾天。如果希望得知兩個日期之間差幾天,直接調(diào)用 PeriodgetDays(). 方法得到的只是最后的“零幾天”,而不是算總的間隔天數(shù)。

LocalDate today = LocalDate.of(2020, 9, 5);
LocalDate specifyDate = LocalDate.of(2019, 8, 30);
System.out.println(Period.between(specifyDate, today).getDays());
System.out.println(Period.between(specifyDate, today));
System.out.println(ChronoUnit.DAYS.between(specifyDate, today));

/**
 * 輸出:
 * 6
 * P1Y6D
 * 372
 */

1.6 時間反解析

LocalDate inverseAnalysisTime = LocalDate.parse("2020-/-09-/-04" ,
                DateTimeFormatter.ofPattern("yyyy-/-MM-/-dd"));
System.out.println("反解析后時間為:" + inverseAnalysisTime);

/**
 * 輸出:
 * 反解析后時間為:2020-09-04
 */

LocalDateTime inverseAnalysisTime = LocalDateTime.parse("2020-/-09-/-04 22:42" ,
                DateTimeFormatter.ofPattern("yyyy-/-MM-/-dd HH:mm"));
System.out.println("反解析后時間為:" + inverseAnalysisTime);
/**
 * 輸出:
 * 反解析后時間為:2020-09-04T22:42
 */

注意:

  • 這里的LocalDate、LocalTimeLocalDateTime的使用要區(qū)別好,不然解析過程會出現(xiàn)錯誤。

1.7 Instant類

Instant對象和時間戳是一一對應(yīng)的,它是精確到納秒的(而不是象舊版本的Date精確到毫秒)。

Instant instant = Instant.now();
System.out.println(instant);

// 輸出, ISO-8601 標(biāo)準(zhǔn)
// 2020-09-04T15:13:50.152933300Z

Instant 類返回的值計算從 1970 年 1 月 1 日(1970-01-01T00:00:00Z)第一秒開始的時間, 也稱為 EPOCH。 發(fā)生在時期之前的瞬間具有負(fù)值,并且發(fā)生在時期后的瞬間具有正值 (1970-01-01T00:00:00Z 中的 Z 其實就是偏移量為 0)。Instant 類提供的其他常量是 MIN, 表示最小可能(遠(yuǎn)遠(yuǎn))的瞬間,MAX表示最大(遠(yuǎn)期)瞬間。

  • 該類還提供了多種方法操作 Instant。加和減的增加或減少時間的方法。以下代碼將 1 小時添加到當(dāng)前時間:
Instant oneHourLater = Instant.now().plusHours(1);
  • 比較時間的方法
long secondsFromEpoch = Instant.ofEpochSecond(0L).until(Instant.now(),ChronoUnit.SECONDS);
// 1599233977

LocalDateTime start = LocalDateTime.of(2020, 9, 4, 0, 0, 0);
LocalDateTime end = LocalDateTime.of(2020, 9, 8, 0, 0, 0);
// 兩個時間之間相差了4天
System.out.println(start.until(end, ChronoUnit.DAYS));

// 4
  • Instant 不包含年,月,日等單位。但是可以轉(zhuǎn)換成 LocalDateTime 或 ZonedDateTime, 如下 把一個 Instant + 默認(rèn)時區(qū)轉(zhuǎn)換成一個 LocalDateTime。
LocalDateTime ldt = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());

System.out.printf("%s %d %d at %d:%d%n", ldt.getMonth(), ldt.getDayOfMonth(),
                                                        ldt.getYear(), ldt.getHour(), ldt.getMinute());
// SEPTEMBER 4 2020 at 23:40

無論是 ZonedDateTime 或 OffsetTimeZone 對象可被轉(zhuǎn)換為 Instant 對象,因為都映射到時間軸上的確切時刻。 但是,相反情況并非如此。要將 Instant 對象轉(zhuǎn)換為 ZonedDateTime 或 OffsetDateTime 對象,需要提供時區(qū)或時區(qū)偏移信息。

二、線程安全性問題

放兩張圖就一目了然:

三、數(shù)據(jù)庫中時間存儲

3.1 區(qū)別

int

  • 占用4個字節(jié)
  • 建立索引之后,查詢速度快
  • 條件范圍搜索可以使用使用between
  • 不能使用mysql提供的時間函數(shù)

datetime

  • 占用8個字節(jié),允許為空值,可以自定義值
  • 系統(tǒng)不會自動修改其值
  • 與時區(qū)無關(guān),存什么拿到的就是什么。
  • 可以在指定datetime字段的值的時候使用now()變量來自動插入系統(tǒng)的當(dāng)前時間。

timestamp

  • 類型在默認(rèn)情況下,insert、update 數(shù)據(jù)時,timestamp列會自動以當(dāng)前時間(CURRENT_TIMESTAMP)填充/更新。
  • 受時區(qū)timezone的影響以及MYSQL版本和服務(wù)器的SQL MODE的影響 ,存儲時對當(dāng)前的時區(qū)進行轉(zhuǎn)換,檢索時再轉(zhuǎn)換回當(dāng)前的時區(qū)。

3.2 使用建議

  • int適合需要進行大量時間范圍查詢的數(shù)據(jù)表。
  • datetime適合用來記錄數(shù)據(jù)的原始的創(chuàng)建時間,因為無論你怎么更改記錄中其他字段的值,datetime字段的值都不會改變,除非你手動更改它。
  • timestamp適合用來記錄數(shù)據(jù)的最后修改時間,因為只要你更改了記錄中其他字段的值,timestamp字段的值都會被自動更新。(如果需要可以設(shè)置timestamp不自動更新)。

四、“老三樣”的坑

老三樣指:Date、CalenderSimpleDateFormat。

4.1 初始化日期時間

如果要初始化一個 2020 年 9 月 5 日 11 點 12 分 13 秒這樣的時間:

 Date date = new Date(2020, 9, 5, 11, 12, 13);

// 輸出:
// Tue Oct 05 11:12:13 CST 3920

這里就要注意:年應(yīng)該是和 1900 的差值,月應(yīng)該是從 0 到 11 而不是從 1 到 12。

我們也可以直接使用Calander

Calendar calendar = Calendar.getInstance();
// 月份依舊是 0-11
calendar.set(2020,8,5,11,16,25);

System.out.println(calendar.getTime());

// 輸出:
// Sat Sep 05 11:16:25 CST 2020

4.2 時區(qū)問題

關(guān)于 Date 類,我們要有兩點認(rèn)識:

  • Date 并無時區(qū)問題,世界上任何一臺計算機使用 new Date() 初始化得到的時間都一樣。因為,Date 中保存的是 UTC 時間,UTC 是以原子鐘為基礎(chǔ)的統(tǒng)一時間,不以太陽參照計時,并無時區(qū)劃分。
  • Date 中保存的是一個時間戳,代表的是從 1970 年 1 月 1 日 0 點(Epoch 時間)到現(xiàn)在的毫秒數(shù)。嘗試輸出 Date(0):
System.out.println(new Date(0));
System.out.println(TimeZone.getDefault().getID() + ":" + TimeZone.getDefault().getRawOffset()/3600000);

// 輸出:
// Thu Jan 01 08:00:00 CST 1970
// 因為我機器當(dāng)前的時區(qū)是中國上海,相比 UTC 時差 +8 小時。

對于國際化的項目,處理好時間和時區(qū)問題首先就是要正確保存日期時間。這里有兩種保存方式:

  • 方式一,以 UTC 保存,保存的時間沒有時區(qū)屬性,是不涉及時區(qū)時間差問題的世界統(tǒng)一時間。我們通常說的時間戳,或 Java 中的 Date 類就是用的這種方式,這也是推薦的方式。
  • 方式二,以字面量保存,比如年 / 月 / 日 時: 分: 秒,一定要同時保存時區(qū)信息。只有有了時區(qū)信息,我們才能知道這個字面量時間真正的時間點,否則它只是一個給人看的時間表示,只在當(dāng)前時區(qū)有意義。Calendar 是有時區(qū)概念的,所以我們通過不同的時區(qū)初始化 Calendar,得到了不同的時間。正確保存日期時間之后,就是正確展示,即我們要使用正確的時區(qū),把時間點展示為符合當(dāng)前時區(qū)的時間表示。

4.3 日期時間格式化和解析

每到年底,就有很多踩時間格式化的坑,比如“這明明是一個 2019 年的日期,怎么使用 SimpleDateFormat 格式化后就提前跨年了”。我們來重現(xiàn)一個這個問題。

初始化一個 Calendar,設(shè)置日期時間為 2019 年 12 月 29 日,使用大寫的 YYYY 來初始化 SimpleDateFormat:

Locale.setDefault(Locale.SIMPLIFIED_CHINESE);
System.out.println("defaultLocale:" + Locale.getDefault());
Calendar calendar = Calendar.getInstance();
calendar.set(2019, Calendar.DECEMBER, 29,0,0,0);
SimpleDateFormat YYYY = new SimpleDateFormat("YYYY-MM-dd");
System.out.println("格式化: " + YYYY.format(calendar.getTime()));
System.out.println("weekYear:" + calendar.getWeekYear());
System.out.println("firstDayOfWeek:" + calendar.getFirstDayOfWeek());
System.out.println("minimalDaysInFirstWeek:" + calendar.getMinimalDaysInFirstWeek());

/**
 * 輸出:
 *  
 * defaultLocale:zh_CN
 * 格式化: 2020-12-29
 * weekYear:2020
 * firstDayOfWeek:1
 * minimalDaysInFirstWeek:1
 */

更改時區(qū)試試:

Locale.setDefault(Locale.FRANCE);

//        格式化: 2019-12-29
//        weekYear:2019
//        firstDayOfWeek:2
//        minimalDaysInFirstWeek:4

那么 week year 就還是 2019 年,因為一周的第一天從周一開始算,2020 年的第一周是 2019 年 12 月 30 日周一開始,29 日還是屬于去年。JDK 的文檔中有說明:小寫 y 是年,而大寫 Y 是 week year,也就是所在的周屬于哪一年,所以沒有特殊需求,針對年份的日期格式化,應(yīng)該一律使用 “y” 而非 “Y”。

另一個是:當(dāng)需要解析的字符串和格式不匹配的時候,SimpleDateFormat 表現(xiàn)得很寬容,還是能得到結(jié)果

String dateString = "20200905";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMM");
try {
    System.out.println("result:" + dateFormat.parse(dateString));
} catch (ParseException e) {
    e.printStackTrace();
}

// 輸出:
// result:Sun May 01 00:00:00 CST 2095

這里把0905當(dāng)初月份,往后推遲了905個月,但是并沒有爆出任何警告或錯誤。

我們可以用Java8中的DateTimeFormatter代替:

String dateString = "20200905";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM");
System.out.println("result:" + dateTimeFormatter.parse(dateString));

// 控制臺報錯:
//        Exception in thread "main" java.time.format.DateTimeParseException:Text '20200905' could not be parsed at index 0
//        at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
//        at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1874)
//        at cn.litblue.datedemo.DateDemo.main(DateDemo.java:56)

4.4 線程安全問題

我們寫一個案例:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

ExecutorService threadPool = Executors.newFixedThreadPool(100);
for (int i = 0; i < 20; i++) {
    //提交20個并發(fā)解析時間的任務(wù)到線程池,模擬并發(fā)環(huán)境
    threadPool.execute(() -> {
        for (int j = 0; j < 10; j++) {
            try {
                System.out.println(simpleDateFormat.parse("2020-09-05 12:10:30"));
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    });
}
threadPool.shutdown();
threadPool.awaitTermination(1, TimeUnit.HOURS);

運行程序后大量報錯,且沒有報錯的輸出結(jié)果也不正常。

五、總結(jié)

老三樣還是不要用了,新的日期時間類不香么?

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • springboot項目啟動,但是訪問報404錯誤的問題

    springboot項目啟動,但是訪問報404錯誤的問題

    這篇文章主要介紹了springboot項目啟動,但是訪問報404錯誤的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • SpringMVC配置JSP視圖解析器的最佳實踐

    SpringMVC配置JSP視圖解析器的最佳實踐

    當(dāng)你在深夜調(diào)試SpringMVC項目時,有沒有遇到過明明控制器返回了"success"卻死活找不到對應(yīng)JSP文件的情況?別急!這很可能是因為視圖解析器配置出了問題,今天我們就來好好聊聊怎么用SpringMVC配置JSP視圖解析器這件事,需要的朋友可以參考下
    2025-05-05
  • 用Java實現(xiàn)24點游戲

    用Java實現(xiàn)24點游戲

    喜歡玩游戲的有福啦,文中有非常詳細(xì)的開發(fā)框架,按著框架來實現(xiàn)就好啦.而且24點游戲是經(jīng)典的紙牌益智游戲.,需要的朋友可以參考下
    2021-05-05
  • 深入理解Java設(shè)計模式之訪問者模式

    深入理解Java設(shè)計模式之訪問者模式

    這篇文章主要介紹了JAVA設(shè)計模式之訪問者模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解
    2021-11-11
  • IDEA項目的依賴(pom.xml文件)導(dǎo)入問題及解決

    IDEA項目的依賴(pom.xml文件)導(dǎo)入問題及解決

    這篇文章主要介紹了IDEA項目的依賴(pom.xml文件)導(dǎo)入問題及解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • mybatis如何通過接口查找對應(yīng)的mapper.xml及方法執(zhí)行詳解

    mybatis如何通過接口查找對應(yīng)的mapper.xml及方法執(zhí)行詳解

    這篇文章主要給大家介紹了利用mybatis如何通過接口查找對應(yīng)的mapper.xml及方法執(zhí)行的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。
    2017-06-06
  • Java基礎(chǔ)之SpringBoot整合knife4j

    Java基礎(chǔ)之SpringBoot整合knife4j

    Swagger現(xiàn)在已經(jīng)成了最流行的接口文檔生成與管理工具,但是你是否在用的時候也在吐槽,它是真的不好看,接口測試的json數(shù)據(jù)沒法格式化,測試地址如果更改了還要去改配置,接口測試時增加token驗證是真的麻煩…針對Swagger的種種缺點,Knife4j就呼之欲出了.需要的朋友可以參考下
    2021-05-05
  • 淺析java中ArrayList與Vector的區(qū)別以及HashMap與Hashtable的區(qū)別

    淺析java中ArrayList與Vector的區(qū)別以及HashMap與Hashtable的區(qū)別

    以下是對java中ArrayList與Vector的區(qū)別以及HashMap與Hashtable的區(qū)別進行了詳細(xì)的解析。需要的朋友可以過來參考下
    2013-08-08
  • Java利用Jsoup解析和操作HTML的技術(shù)指南

    Java利用Jsoup解析和操作HTML的技術(shù)指南

    在現(xiàn)代 Java 開發(fā)中,處理 HTML 數(shù)據(jù)是一項常見需求,無論是抓取網(wǎng)頁數(shù)據(jù)、解析 HTML 文檔,還是操作 DOM 樹,Jsoup 都是一個強大的工具,本文將介紹 Jsoup 的基本功能,并通過多個詳細(xì)的代碼示例展示如何使用它解析和操作 HTML,需要的朋友可以參考下
    2025-03-03
  • Mybatis讀取和存儲json類型數(shù)據(jù)的實現(xiàn)

    Mybatis讀取和存儲json類型數(shù)據(jù)的實現(xiàn)

    本文主要介紹了Mybatis讀取和存儲json類型數(shù)據(jù)的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06

最新評論

临汾市| 凉山| 城固县| 石狮市| 安泽县| 竹山县| 江孜县| 漳州市| 安仁县| 米林县| 常宁市| 涟源市| 阿坝县| 广河县| 明溪县| 遂平县| 西畴县| 文成县| 深圳市| 宜黄县| 文昌市| 东阿县| 孝感市| 汪清县| 翼城县| 潼南县| 永仁县| 佛坪县| 肥城市| 北京市| 义马市| 彰武县| 陇川县| 赣州市| 宣威市| 曲沃县| 夏邑县| 泽普县| 右玉县| 沂源县| 宁南县|