八年了,這幾個(gè)時(shí)間API你用過嗎?
相信Java 8中的LocalDate和LocalDateTime大家都非常熟悉了,這些時(shí)間API用起來非常語(yǔ)義化,并且能夠保證線程的安全性。今天介紹另外幾個(gè)可能你不常用的時(shí)間API以及它們的使用場(chǎng)景,或許能夠幫助你更好地進(jìn)行開發(fā)。
Instant
最近我使用java.time.Instant比較多,因?yàn)橛?jì)算JWT的過期時(shí)間和發(fā)行時(shí)間都用的是它。有人會(huì)問為什么不使用LocalDateTime呢?其實(shí)我也比較納悶。經(jīng)過一番查詢才明白了為什么要使用Instant。
時(shí)間是一條向前不斷延伸的時(shí)間線。我們定義過期時(shí)間的時(shí)候肯定是一個(gè)瞬時(shí)時(shí)間點(diǎn),而Instant正好可以用來表示時(shí)間線上的一個(gè)時(shí)間戳,常用來記錄事件時(shí)間戳。

時(shí)間戳示意圖
Instant的范圍需要存儲(chǔ)一個(gè)超過long類型極限的數(shù)字,因此它被設(shè)計(jì)為兩個(gè)部分:
- seconds 從計(jì)算機(jī)元年(1970-01-01T00:00:00Z)開始的秒數(shù),可能是負(fù)數(shù)哦。
- nanos 納秒數(shù),用來修正時(shí)間以保證準(zhǔn)確性,始終是正數(shù),而且小于999,999,999。
常用API的使用
如果你使用Instant.now()獲取當(dāng)前時(shí)間戳,采用的是UTC時(shí)間,并非北京時(shí)間。所以需要加上時(shí)區(qū):
issuedAt = Clock.system(ZoneId.of("Asia/Shanghai")).instant();TemporalAccessor的實(shí)現(xiàn)類,比如常用的LocalDate、LocalDateTime都可以轉(zhuǎn)換為Instant,例如:
Instant current = Instant.from(LocalDateTime.now());
Instant instant = LocalDateTime.now().toInstant(ZoneOffset.ofHours(8));
如果在計(jì)算機(jī)元年以前,需要用負(fù)值表示:
stant instantBefore1970 = Instant.ofEpochSecond(-13434234, 343434);
還有其它一些API就不一一介紹了,請(qǐng)務(wù)必記住,它通常用來記錄事件發(fā)生的瞬時(shí)時(shí)刻。
Duration
這是一個(gè)持續(xù)時(shí)間,通常表示持續(xù)了多少時(shí)間。它記錄了一個(gè)時(shí)間量,可以來自一個(gè)開始時(shí)間和結(jié)束時(shí)間,也可以來自一些時(shí)間計(jì)量單位。它同樣用秒數(shù)和納秒來存儲(chǔ)時(shí)間量,秒數(shù)同樣可以是負(fù)數(shù),納秒只能是正數(shù)。

Java 8 Duration
常用API的使用
比如你花了3天時(shí)間寫了個(gè)需求:
Duration days = Duration.ofDays(3);
再精確點(diǎn),從2022年4月2日到2022年4月5日,共3天:
Duration duration = Duration.between(LocalDateTime.of(2022, 4, 2,0,0),
LocalDateTime.of(2022, 4, 5,0,0));
注意這里只能使用能夠表示到秒的Temporal實(shí)現(xiàn),比如LocalDate只能表示到天,是不行的。
剩下的持續(xù)了多少年、多少月、多少分鐘、多少小時(shí)、多少秒就不一一演示了。
格式化
格式基于 ISO-8601 持續(xù)時(shí)間格式PnDTnHnMn.nS ,其中天數(shù)被認(rèn)為是 24 小時(shí)。字符串以可選符號(hào)開頭,由 ASCII 負(fù)號(hào)或正號(hào)表示。如果為負(fù),則整個(gè)周期都被否定。接下來是大寫或小寫的 ASCII 字母“P”。然后有四個(gè)部分,每個(gè)部分由一個(gè)數(shù)字和一個(gè)后綴組成。這些部分具有“D”、“H”、“M”和“S”的 ASCII 后綴,表示天、小時(shí)、分鐘和秒,接受大寫或小寫。后綴必須按順序出現(xiàn)。ASCII 字母“T”必須出現(xiàn)在小時(shí)、分鐘或秒部分的第一次出現(xiàn)(如果有)之前。必須存在四個(gè)部分中的至少一個(gè),如果存在“T”,則必須在“T”之后至少有一個(gè)部分。每個(gè)部分的數(shù)字部分必須由一個(gè)或多個(gè) ASCII 數(shù)字組成。該數(shù)字可以以 ASCII 負(fù)號(hào)或正號(hào)為前綴。天數(shù)、小時(shí)數(shù)和分鐘數(shù)必須解析為long 。秒數(shù)必須解析為帶有可選分?jǐn)?shù)的long整數(shù)。小數(shù)點(diǎn)可以是點(diǎn)或逗號(hào)。小數(shù)部分可能有 0 到 9 個(gè)數(shù)字。
實(shí)力:
格式 | 含義 |
? | 20.345秒 |
? | 15分鐘 |
? | 10小時(shí) |
? | 2天 |
? | 2天3小時(shí)4分鐘 |
? | 減6小時(shí)加3分鐘,等于減5小時(shí)57分鐘 |
? | 否定全部,減6小時(shí)3分鐘 |
? | 加6小時(shí)減3分鐘,等于持續(xù)5小時(shí)57分鐘 |
Period
說到這里就不得不說一下Period,感覺它和Duration非常類似,只不過它建立在年月日上,以年、月和日為單位對(duì)時(shí)間量或時(shí)間量進(jìn)行建模。非常容易理解這里就不細(xì)說了,記住它最多精確到天就夠了,它同樣是用來記錄持續(xù)時(shí)間的,只不過粒度粗了一些。
常用API
持續(xù)了2年:
Period years = Period.ofYears(2);
// 其它時(shí)間單位就不演示了。
這里可以使用周進(jìn)行初始化:
Period weeks = Period.ofWeeks(3);
新冠從2019年12月持續(xù)到2022年4月
Period covid19Period = Period.between(LocalDate.of(2019,12,1),
LocalDate.of(2022,4,7));
格式化
基于 ISO-8601 句點(diǎn)格式PnYnMnD和PnW 。字符串以可選符號(hào)開頭,由 ASCII 負(fù)號(hào)或正號(hào)表示。如果為負(fù),則整個(gè)周期都被否定。接下來是大寫或小寫的 ASCII 字母“P”。然后有四個(gè)部分,每個(gè)部分由一個(gè)數(shù)字和一個(gè)后綴組成。必須存在四個(gè)部分中的至少一個(gè)。這些部分具有“Y”、“M”、“W”和“D”的 ASCII 后綴,表示年、月、周和日,接受大寫或小寫。后綴必須按順序出現(xiàn)。每個(gè)部分的數(shù)字部分必須由 ASCII 數(shù)字組成。該數(shù)字可以以 ASCII 負(fù)號(hào)或正號(hào)為前綴。該數(shù)字必須解析為int 。前導(dǎo)加號(hào)/減號(hào)和其他單位的負(fù)值不是 ISO-8601 標(biāo)準(zhǔn)的一部分。此外,ISO-8601 不允許在PnYnMnD和PnW格式之間混合。任何基于周的輸入乘以 7 并視為天數(shù)。
示例:
格式 | 含義 |
? | 兩年 |
? | 仨月 |
? | 四周 |
? | 五天 |
? | 一年倆月零3天 |
? | 一年倆月三周零四天,? |
? | ? |
? | ? |
這個(gè)感覺用來做日歷計(jì)算比較合適一些。






























