查询两个日期间隔天数怎么算_大厂都是怎么用Java8代替SimpleDateFormat?
1 SimpleDateFormat 之坑
1.1 格式化
1.1.1 案例
- 初始化一個Calendar,設置日期2020年12月29日
?
- 日志
?
這是由于混淆SimpleDateFormat的各種格式化模式:
- 小寫y是年
- 大寫Y是week year,即所在的周屬于哪一年
一年第一周的判斷方式
從getFirstDayOfWeek()開始,完整的7天,并且包含那一年至少getMinimalDaysInFirstWeek()天。
該計算方式和區(qū)域相關(guān),對zh_CN區(qū)域,2020年第一周條件:從周日開始的完整7天,2020年包含1天即 可。顯然,2019年12月27日周日到2020年1月2日周六是2020年第一周,得出的week year就是2021年。
若把區(qū)域改為法國
Locale.setDefault(Locale.FRANCE);
則week yeay就還是2020年,因為一周的第一天從周一開始算,2020年的第一周是2019年12月28日周一開始,27日還是屬于去年:
?
小結(jié)
無特殊需求,針對年份的日期格式化,應該一律使用 “y” 而非 “Y”。
線程安全問題
使用一個100線程的線程池,循環(huán)20次把時間格式化任務提交到線程池處理,每個任務中又循環(huán)10次解析2020-01-01 11:12:13這樣一個時間表示:
- 運行程序后大量報錯,即使沒有報錯的輸出結(jié)果也不正常,比如2020年解析成57728年
?
SimpleDateFormat 用于定義解析和格式化日期時間的模式。看起來是一次性工作,應該復用,但它的解析和格式化操作都非線程安全。
分析源碼
?
SimpleDateFormat繼承自DateFormat,DateFormat有字段Calendar;
- SimpleDateFormat#parse調(diào)用CalendarBuilder#establish構(gòu)建Calendar
?
establish方法內(nèi)部先清空Calendar再構(gòu)建Calendar,整個操作沒有加鎖。
?
顯然,若使用線程池調(diào)用parse,即多線程并發(fā)操作一個Calendar,就可能會產(chǎn)生一個線程還沒來得及處理Calendar就被另一個線程清空。format方法同理,不再贅述。因此只能在同一個線程復用SimpleDateFormat,
解決方案
通過ThreadLocal來存放SimpleDateFormat:
- 日志輸出全部正確
?
1.2 當需要解析的字符串和格式不匹配,SimpleDateFormat還是能得到結(jié)果
案例
使用yyyyMM解析20160901字符串:
?
- 居然輸出2112年,這是因為把 1111當成月份
?
對于SimpleDateFormat的這些坑,使用Java 8中的DateTimeFormatter即可避免。
2 Java 8中的DateTimeFormatter
2.1 格式化字符串
首先,使用DateTimeFormatterBuilder定義格式化字符串,無需死記大寫Y還是小寫y,大寫M還是小寫m:
?
2.2 線程安全
可定義為static使用
2.3 待解析字符串和格式不匹配時就報錯
?
- 日志
2020/11/11 11:11:11.789 Exception in thread "main" java.time.format.DateTimeParseException: Text '20201111' could not be parsed at index 0 at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1777) at org.javaedge.time.commonmistakes.datetime.dateformat.CommonMistakesApplication.better(CommonMistakesApplication.java:96) at org.javaedge.time.commonmistakes.datetime.dateformat.CommonMistakesApplication.main(CommonMistakesApplication.java:47)
3 Java8計算日期時間
- 有人喜歡使用時間戳進行計算,比如希望得到當前時間后30天:把new Date().getTime得到的時間戳加30天對應毫秒數(shù)
?
- 得到的日期居然比當前日期還要早,根本不是后30天
?
因為int發(fā)生了溢出!。
- 應將30改為30L,使其為long:
?
- 正確輸出
?
- Java 8前代碼,建議使用Calendar:
?
- 使用Java 8的日期時間類型,可以直接進行各種計算,更加簡潔和方便:
?
對日期時間做計算操作,日期時間API會比Calendar功能強大很多。
3.1 minus/plus直接對日期加減
?
3.2 with快捷時間調(diào)節(jié)
- TemporalAdjusters.firstDayOfMonth得到當前月的第一天
- TemporalAdjusters.firstDayOfYear()得到當前年的第一天
- TemporalAdjusters.previous(DayOfWeek.SATURDAY)得到上一個周六
- TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)得到本月最后一個周五
?
3.3 使用lambda自定義的時間調(diào)整
- 為當前時間增加100天以內(nèi)的隨機天數(shù):
?
判斷日期是否符合某個條件
?
query查詢是否匹配條件
?
使用Java 8操作和計算日期時間雖然方便,但計算兩個日期差時可能會踩坑:Java 8中有一個專門的類Period定義了日期間隔,通過Period.between得到了兩個LocalDate的差,返回的是兩個日期差幾年零幾月零幾天。
如果希望得知兩個日期之間差幾天,直接調(diào)用Period的getDays()方法得到的只是最后的“零幾天”,而不是算總的間隔天數(shù)。
比如,計算2020年12月12日和2020年10月1日的日期間隔,很明顯日期差是2個月零11天,但獲取getDays方法得到的結(jié)果只是11天,而不是72天:
?
可使用ChronoUnit.DAYS.between解決這個問題:
?
4 總結(jié)
也許你認為java.util.Date類似于新API中的LocalDateTime。其實不是,雖然它們都沒時區(qū)概念
- java.util.Date類是因為使用UTC表示,所以沒有時區(qū)概念,其本質(zhì)是時間戳
- LocalDateTime,嚴格上可以認為是一個日期時間的表示,而不是一個時間點
因此,在把Date轉(zhuǎn)換為LocalDateTime的時候,需要通過Date的toInstant方法得到一個UTC時間戳進行轉(zhuǎn)換,并需要提供當前的時區(qū),這樣才能把UTC時間轉(zhuǎn)換為本地日期時間(的表示)。反過來,把LocalDateTime的時間表示轉(zhuǎn)換為Date時,也需要提供時區(qū),用于指定是哪個時區(qū)的時間表示,也就是先通過atZone方法把LocalDateTime轉(zhuǎn)換為ZonedDateTime,然后才能獲得UTC時間戳:
Date in = new Date(); LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault()); Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
有人說新API很麻煩,還需要考慮時區(qū),真麻煩。但并非因為API強行設計繁瑣,而是UTC時間要變?yōu)楫數(shù)貢r間,必須考慮時區(qū)!
---------------------
為什么阿里巴巴的程序員成長速度這么快?
霸榜GitHub的Offer來了原理篇+框架篇,開放分享;
50W年薪程序員需要的技術(shù)棧分析
看完三件事??
如果你覺得這篇內(nèi)容對你還蠻有幫助,我想邀請你幫我三個小忙:
點贊,轉(zhuǎn)發(fā),有你們的 『點贊和評論』,才是我創(chuàng)造的動力。
關(guān)注公眾號 『 Java斗帝 』,不定期分享原創(chuàng)知識。
同時可以期待后續(xù)文章ing
總結(jié)
以上是生活随笔為你收集整理的查询两个日期间隔天数怎么算_大厂都是怎么用Java8代替SimpleDateFormat?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 惊喜连连!Redmi K50首批搭载天玑
- 下一篇: 报复性发布16款电动车 丰田章男回应:我