Java的時間 - java.time包介紹

Java8中引入了新的庫java.time,提供更爲好用的日誌API,從此不再在Date、Calendar這些類中糾結。本文基於java.time API文檔進行記錄和總結。

包簡介

該包下的類定義了基礎的日期-時間概念,包括instant、duration、date、time、time-zone、period等,他們都是基於ISO日曆系統。

所有的類都是不可變的,意味着它們是線程安全的

各個子包的作用

  • java.time.temporal 取得底層字段
  • java.time.format 解析字符串或格式化
  • java.time.chrono 中立的日曆API

API介紹

核心API

Instant

本質上是一個數字時間戳,即在時間線上的某一點,某一時刻。使用可以從Clock中取得。打印日誌或持久化時標記時間很有用。曾和System.currentMillis()關聯

Instant是與java.util.Date. ZonedDateTime同等的類

Instant的組成,包含一個long用於存儲epoch秒數,即1970-01-01T00:00:00Z到現在的秒數;還有一個int用於存儲納秒。

Clock

Clock提供訪問當前Instant,帶時區的日期和時間

他可以替代System.currentMillis()和TimeZone.getDefault(),用於獲取當前的毫秒數和默認時區

時鐘不是必須的,因爲上面的LocalDate等類都有一個now()方法。Clock存在的意義在於允許我們將時間當做對象進行傳遞,因爲一個Clock對象包含了比較全面的信息。

LocalDate

僅有日期,可用於存儲生日之類的數據

它不存儲時間或時區,它僅僅是對日期的描述,不能代表時間線上的一個瞬間

在日期上的操作支持很好,可以訪問年、月、日、年中日等

LocalTime

僅有時間,沒有日期,即所謂的wall time

LocalDateTime

存儲日期和時間,精確度爲納秒

它不存儲時區,因此不能代表時間線上的一個瞬間

ZonedDateTime

帶時區的日期和時間,在將ZoneId帶入計算時非常有用。儘可能不要使用它,帶有時區將使得應用變得複雜。

ZonedDateTime是和java.util.GregorianCalendar同等的類

Duration

兩個時間的差,納秒計

Period

表示對人類有意義的一段時間,比如一年、一天

API命名約定

java.time提供了豐富但統一的API,不同前綴代表不同意思

  • of - 靜態工廠方法
  • parse - 用於解析的靜態方法
  • get - 獲取
  • is - 檢查是否爲true
  • with - setter的不可變等價方法
  • plus - 向一個對象增加
  • minus - 減少
  • to - 將一個對象轉換爲另一個類型
  • at - 將一個對象和另一個對象結合,例如將LocalDate和LocalTime結合成LocalDateTime

閏秒的處理

太陽日的長度是人類測量時間的標準方法,通常一個太陽日被分爲24小時60分鐘60秒,形成86400秒。

現代計時是以原子鐘爲基礎,它精確地定義了一個名爲SI second的東西代表1秒(這是銫原子躍遷的時間),SI second非常接近86400分之一天。

SI second和太陽時的關係,在於前者是觀測地球自轉得來,是絕對意義上的準確時間;後者是計時得來的,和地球自轉無關。在計算機系統中使用的時間就是計時得來。

然而,地球自轉並不絕對穩定,使得一天的長度會發生變化;隨着時間的推移,地球自轉變慢,一天的平均時間邊長,導致SI second和太陽時出現偏差。

UTC time-scale是將額外變化的時間捆綁成整秒的標準做法。現在的UTC時間於1972年提出,引入閏秒的概念,然而閏秒的定義是複雜的。鑑於此,java.time中的API定義了自己的time-scale

Java time-scale將每個日曆日精確地劃分爲86400個細分,即秒。 這些秒數可能與 SI second不同。

Java time-scale對不同時間段的定義略有不同,每個時間段都基於作爲公民時間基礎的協商一致的國際時間表。 每當修改或替換國際商定的時間表時,必須爲其定義新的 Java time-scale。

截止2013年,Java time-scale分爲兩部分

  • 1972-11-03至今,java time-scale和UTC一致,與UTC沒有閏秒的時間相同,對於閏秒的天,閏秒被平均分配在一天的最後1000秒中,以保證每天看起來還是86400秒。即意味着這天的最後1000秒,java的每一秒都比普通的時間快或慢1ms
  • 1972-11-03之前,UTC時間尚未提出,世界協商時間爲UT1,相當於格林威治上的太陽時,由於沒有閏秒的說法,java的time-sclae和世界協商時間一致。

時區ID

時區ID在整個系統中是唯一的,總共有三種時區ID

  • 最簡單的時區ID,以+或-開頭。

  • 帶有前綴的和偏移量後綴的時區ID。如GMT+2、UTC+01:00,合法的前綴有GMT、UTC、UT。由於它有固定偏移量,因此可以調用ZoneId.normalized()標準化成一個ZoneOffset

  • 基於地理區域的ID。必須由兩個以上字符組成,且不能是UTC、GMT、UT、+、- 。基於地理區域的ID的轉換規則是被定義在配置中的,由ZoneRulesProvider實現。

    翻看ZoneRulesProvider的手冊,發現對時區的配置可以自定義,按照指定的規則來即可。默認情況下,它使用了位於java home的lib文件夾下tzdb.dat文件中定義的數據加載規則。我們可以覆蓋這個操作,通過設置系統變量java.time.zone.DefaultZoneRulesProvider

時區由政府部門指定,且修改頻繁。有好幾個組織做這件事。Java默認使用IANA Time Zone Database (TZDB)的規則,其它組織包括IATA和微軟。每個組織的區域ID都不一樣,我們遵循默認的TZDB就好。

時區相關類

  • OffsetTime

    存儲相對於UTC時間的時間和偏移量,如’11:30+01:00’

  • OffsetDateTime

    存儲相對於UTC時間的日期時間和偏移量,如’2010-12-03T11:30+01:00’,精度也是納秒

    Offsetdatetime、 ZonedDateTime 和 Instant都能存儲時間線上的一個瞬間,因爲他們有時區或偏移量

  • 如上兩個Offset類最初是被設計用來支持網絡協議和數據庫操作的。因爲很多數據庫不支持’Europe/Paris這樣的時區,但支持+02:00這樣的偏移量

  • ZoneId

    時區ID,是用來確定Instant和LocalDateTime之間的轉換規則的,有兩種ZoneID

    1. 固定偏移量,相對於UTC時間,如東八區。對應子類ZoneOffset
    2. 地理區域,如Europe/Paris,用於對應UTC時間某個偏移量的地區,對應子類ZoneRegion

    實際規則由ZoneRules類描述。

    ZoneRules定義了每個時區的時區偏移規則。這些規則對時區的歷史和未來轉換建模,Zoneoffsettransition用於過去時間的轉換;Zoneoffsettransitionrule基於算法對未來實現轉換。

參考文檔

  1. java.time Java doc
  2. 關於閏秒 - 陳皓
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章