自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

大廠如何使用Java8日期時(shí)間?

開發(fā) 后端
我們生活在最好的時(shí)代,基本主流類庫(kù)都支持新日期時(shí)間類型,但還有項(xiàng)目因還是用祖?zhèn)魅掌跁r(shí)間類,出現(xiàn)很多古今交錯(cuò)的錯(cuò)誤實(shí)踐。

[[354895]]

 1 背景

Java8前,處理日期時(shí)間時(shí),使用的“三大件”

  • Date
  • Calender
  • SimpleDateFormat

以聲明時(shí)間戳、使用日歷處理日期和格式化解析日期時(shí)間。但這些類的API可讀性差、使用繁瑣,且非線程安全,如同設(shè)計(jì)的翔一樣的IO,也是Java讓人詬病的一大原因。

于是Java8推出全新日期時(shí)間類。這些類的API功能強(qiáng)大簡(jiǎn)便、線程安全。

但畢竟Java8剛出這些類,諸如序列化、數(shù)據(jù)訪問(wèn)等類庫(kù)都不支持Java8日期時(shí)間類,需在新老類中來(lái)回切換。比如,在業(yè)務(wù)邏輯層使用LocalDateTime,存入數(shù)據(jù)庫(kù)或者返回前端的時(shí)候還要切換回Date。因此,還不如沿用老的日期時(shí)間類。

不過(guò)我們生活在最好的時(shí)代,基本主流類庫(kù)都支持新日期時(shí)間類型,但還有項(xiàng)目因還是用祖?zhèn)魅掌跁r(shí)間類,出現(xiàn)很多古今交錯(cuò)的錯(cuò)誤實(shí)踐。

比如

  • 通過(guò)隨意修改時(shí)區(qū),使讀取到的數(shù)據(jù)匹配當(dāng)前時(shí)鐘
  • 直接對(duì)讀取到的數(shù)據(jù)做加、減幾個(gè)小時(shí)的操作,來(lái)“修正數(shù)據(jù)”

本文旨在分析古今時(shí)間錯(cuò)亂的本質(zhì)原因,看看使用遺留日期時(shí)間類,來(lái)處理日期時(shí)間初始化、格式化、解析、計(jì)算等可能會(huì)遇到的問(wèn)題,以及如何使用新日期時(shí)間類解決。

2 初始化日期時(shí)間

初始化2020年11月11日11點(diǎn)11分11秒時(shí)間,這樣可行嗎?

日志輸出時(shí)間是3029年12月11日11點(diǎn)11分11秒:

  1. date : Sat Dec 11 11:11:11 CST 3920 

這明顯是彩筆才會(huì)寫的垃圾代碼,因?yàn)?/p>

  • 年應(yīng)該是和1900差值
  • 月應(yīng)該是 0~11 而非 1~12
  • 時(shí)應(yīng)該是 0~23,而非 1~24

修正上述代碼如下:

  1. Date date = new Date(2020 - 1900, 10, 11, 11, 11, 11); 

日志輸出:

  1. Mon Nov 11 11:11:11 CST 2019 

當(dāng)有國(guó)際化需求時(shí),又得使用Calendar類初始化時(shí)間。

使用Calendar改造后,初始化時(shí)年參數(shù)直接使用當(dāng)前年即可,月0~11。亦可直接使用Calendar.DECEMBER初始化月份,肯定不會(huì)犯錯(cuò)。

分別使用當(dāng)前時(shí)區(qū)和紐約時(shí)區(qū)初始化兩個(gè)相同日期:

日志輸出

顯示兩個(gè)不同時(shí)間,說(shuō)明時(shí)區(qū)發(fā)生作用。但更習(xí)慣年/月/日 時(shí):分:秒日期時(shí)間格式,對(duì)現(xiàn)在輸出的日期格式還不滿意,那就格式化日期時(shí)間

3 時(shí)區(qū)問(wèn)題

全球有24個(gè)時(shí)區(qū),同一個(gè)時(shí)刻不同時(shí)區(qū)(比如中國(guó)上海和美國(guó)紐約)的時(shí)間不同。全球化項(xiàng)目,若初始化時(shí)間時(shí)未提供時(shí)區(qū),那就不是真正意義上的時(shí)間,只能認(rèn)為是我看到的當(dāng)前時(shí)間的一個(gè)表示。

3.1 Date類

Date無(wú)時(shí)區(qū)概念,任一機(jī)器使用new Date()初始化得到時(shí)間相同。因?yàn)?,Date中保存的是UTC時(shí)間,其為以原子鐘為基礎(chǔ)的統(tǒng)一時(shí)間,不以太陽(yáng)參照計(jì)時(shí),無(wú)時(shí)區(qū)劃分

Date中保存的是一個(gè)時(shí)間戳,代表從1970年1月1日0點(diǎn)(Epoch時(shí)間)到現(xiàn)在的毫秒數(shù)。嘗試輸出Date(0):

  1. System.out.println(new Date(0)); 
  2. System.out.println(TimeZone.getDefault().getID() + ":" + 
  3.     TimeZone.getDefault().getRawOffset()/3600000); 

得到1970年1月1日8點(diǎn)。我的機(jī)器在中國(guó)上海,相比UTC時(shí)差+8小時(shí):

  1. Thu Jan 01 08:00:00 CST 1970 
  2. Asia/Shanghai:8 

對(duì)于國(guó)際化項(xiàng)目,處理好時(shí)間和時(shí)區(qū)問(wèn)題首先就是要正確保存日期時(shí)間。

這里有兩種

3.2 如何正確保存日期時(shí)間

  • 保存UTC

保存的時(shí)間無(wú)時(shí)區(qū)屬性,不涉及時(shí)區(qū)時(shí)間差問(wèn)題的世界統(tǒng)一時(shí)間。常說(shuō)的時(shí)間戳或Java中的Date類就是這種方式,也是推薦方案

  • 保存字面量

比如年/月/日 時(shí):分:秒,務(wù)必同時(shí)保存時(shí)區(qū)信息。有了時(shí)區(qū),才能知道該字面量時(shí)間真正的時(shí)間點(diǎn),否則它只是一個(gè)給人看的時(shí)間表示且只在當(dāng)前時(shí)區(qū)有意義。

而Calendar才具有時(shí)區(qū)概念,所以通過(guò)使用不同時(shí)區(qū)初始化Calendar,才能得到不同時(shí)間。

正確地保存日期時(shí)間后,就是正確展示,即要使用正確時(shí)區(qū),將時(shí)間點(diǎn)展示為符合當(dāng)前時(shí)區(qū)的時(shí)間表示。至此也就能理解為何會(huì)發(fā)生“時(shí)間錯(cuò)亂”。

從字面量解析成時(shí)間 & 從時(shí)間格式化為字面量

對(duì)同一時(shí)間表示,不同時(shí)區(qū)轉(zhuǎn)換成Date會(huì)得到不同時(shí)間戳

比如2020-11-11 11:11:11

對(duì)當(dāng)前上海時(shí)區(qū)/紐約時(shí)區(qū),轉(zhuǎn)化為UTC時(shí)間戳不同

  1. Wed Nov 11 11:11:11 CST 2020:1605064271000 
  2. Thu Nov 12 00:11:11 CST 2020:1605111071000 

這就是UTC的意義,并非時(shí)間錯(cuò)亂。對(duì)同一本地時(shí)間的表示,不同時(shí)區(qū)的人解析得到的UTC時(shí)間必定不同,反過(guò)來(lái)不同本地時(shí)間可能對(duì)應(yīng)同一UTC。

格式化后出現(xiàn)的錯(cuò)亂

即同一Date,在不同時(shí)區(qū)下格式化得到不同時(shí)間表示。

在當(dāng)前時(shí)區(qū)和紐約時(shí)區(qū)格式化2020-11-11 11:11:11

輸出如下,當(dāng)前時(shí)區(qū)Offset(時(shí)差)是+8小時(shí),對(duì)于-5小時(shí)的紐約

因此,有時(shí)數(shù)據(jù)庫(kù)中相同時(shí)間,由于服務(wù)器時(shí)區(qū)設(shè)置不同,讀取到的時(shí)間表示不同。這不是時(shí)間錯(cuò)亂,而是時(shí)區(qū)作用,因?yàn)閁TC時(shí)間需根據(jù)當(dāng)前時(shí)區(qū)解析為正確的本地時(shí)間。

所以要正確處理時(shí)區(qū),在于存和讀兩階段

  • 存,需使用正確的當(dāng)前時(shí)區(qū)來(lái)保存,這樣UTC時(shí)間才會(huì)正確
  • 讀,也須正確設(shè)置本地時(shí)區(qū),才能把UTC時(shí)間轉(zhuǎn)換為正確當(dāng)?shù)貢r(shí)間

Java8處理時(shí)區(qū)問(wèn)題

時(shí)間日期類ZoneId、ZoneOffset、LocalDateTime、ZonedDateTime和DateTimeFormatter,使用起來(lái)更簡(jiǎn)單清晰。

初始化上海、紐約和東京三時(shí)區(qū)

可使用ZoneId.of初始化一個(gè)標(biāo)準(zhǔn)時(shí)區(qū),也可使用ZoneOffset.ofHours通過(guò)一個(gè)offset初始化一個(gè)具有指定時(shí)間差的自定義時(shí)區(qū)。

日期時(shí)間表示

  • LocalDateTime無(wú)時(shí)區(qū)屬性,所以命名為本地時(shí)區(qū)的日期時(shí)間
  • ZonedDateTime=LocalDateTime+ZoneId,帶時(shí)區(qū)屬性

因此,LocalDateTime僅是一個(gè)時(shí)間表示,ZonedDateTime才是一個(gè)有效時(shí)間。這里將把2020-01-02 22:00:00這個(gè)時(shí)間表示,使用東京時(shí)區(qū)解析得到一個(gè)ZonedDateTime。

使用DateTimeFormatter格式化時(shí)間

可直接通過(guò)withZone直接設(shè)置格式化使用的時(shí)區(qū)。最后,分別以上海、紐約和東京三個(gè)時(shí)區(qū)來(lái)格式化這個(gè)時(shí)間輸出:

日志輸出:相同時(shí)區(qū),經(jīng)過(guò)解析存和讀的時(shí)間表示一樣(比如最后一行)

對(duì)不同時(shí)區(qū),比如上海/紐約,輸出本地時(shí)間不同。

+9小時(shí)時(shí)區(qū)的晚上10點(diǎn),對(duì)上海時(shí)區(qū)+8小時(shí),所以上海本地時(shí)間為早10點(diǎn)

而紐約時(shí)區(qū)-5小時(shí),差14小時(shí),為晚上9點(diǎn)

小結(jié)

要正確處理國(guó)際化時(shí)間問(wèn)題,推薦Java8的日期時(shí)間類,即

使用ZonedDateTime保存時(shí)間

然后使用設(shè)置了ZoneId的DateTimeFormatter配合ZonedDateTime進(jìn)行時(shí)間格式化得到本地時(shí)間表示

本文轉(zhuǎn)載自微信公眾號(hào)「 JavaEdge」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 JavaEdge公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: JavaEdge
相關(guān)推薦

2020-02-06 11:35:58

Java 8APIJava

2020-01-15 15:12:38

Java8日期處理代碼

2021-02-24 10:03:17

Java8日期API

2014-12-22 10:14:31

Java8

2016-11-29 12:46:24

JavaJava8時(shí)間日期庫(kù)

2023-01-10 08:27:35

Java8APIJava

2010-11-04 14:57:35

DB2日期時(shí)間

2010-11-04 15:20:33

DB2日期函數(shù)

2010-11-01 14:26:12

DB2日期時(shí)間型數(shù)據(jù)類

2010-11-04 15:30:29

客戶化DB2日期

2017-10-31 20:45:07

JavaJava8Optional

2024-03-18 00:00:00

CalendaJava8Date

2023-05-12 07:40:01

Java8API工具

2010-11-04 15:26:44

DB2日期格式

2018-06-13 15:48:21

Spring BootJava 8API

2015-09-30 09:34:09

java8字母序列

2023-07-26 07:13:55

函數(shù)接口Java 8

2021-01-04 08:39:26

JAVA8OptionalNPE

2014-04-15 09:40:04

Java8stream

2014-07-16 16:42:41

Java8streamreduce
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)