原本在 Java 7 和更之前的版本中,因為 Java 原生並沒有好的時間類別
因此比較合適的方式是使用 Joda-Time 這個套件,用來表示日期與時間。
不過在 Java 8 當中釋出了 JSR-310,也就是 java.time 套件之後,狀況就不同了
Joda-Time 官方網站中也說明了,如果使用 Java 8 以上版本,應轉移至 JSR-310。
Joda-Time is the de facto standard date and time library for Java prior to Java SE 8. Users are now asked to migrate to java.time (JSR-310).
Date 與 Calendar 有什麼問題?
在開始使用以前,首先會先想到的問題是,Java 本來就有 Date 和 Calendar 類別
為什麼不使用 Date 和 Calendar,而需要另外使用別的類別呢?
這個問題其實可以參考良葛格的文章 [2],其實主要是使用上的問題。
也就是 Date 與 Calendar 都是可變的,這會導致在使用上常常創造出無謂的 bug
另外 Date 與 Calendar 也有一些奇妙的設計,在沒有特別理解的情況下很容易誤用。
其實講白話一點,我個人覺得就是 Date 與 Calendar 的 API 設計不完善,造成使用者使用上不方便、要注意的細節頗多
另外提供的運算也不夠完整,,有很多使用者必須自行實作的部份。
Joda-Time 與 JSR-310 的關係
接著,有些人可能過去就有在使用 Joda-Time 這個套件,後來這個套件的作者也被找去設計 JSR-310
但為什麼作者不直接將 Joda-Time 移入成為 JSR-310 呢?
這一樣可以參考良葛格的文章 [3] XD,主要是作者覺得 Joda-Time 有些他想修改的問題。
Joda-Time 的作者 Stephen Colebourne 在 [4] 中描述了,他在 JSR-310 中想要改進的 Joda-Time 問題:
- Human/Machine timelines
- Pluggable chronology
- Nulls
- Internal implementation
細節的描述可以參考良葛格的文章 [3] 或者原文 [4]。
JSR-310 概觀
在 Java 8 的 JSR-310 中,主要有幾種不同的元件:
- 表達時間流上的一個瞬間,如 Instant
- 表達人類能看懂的部份時間資訊,例如 LocalDate、LocalTime、Duration 等
- 表達時區資訊,例如 ZoneId、ZoneOffset
- 表達完整的時間資訊,例如 LocalDateTime、ZonedDateTime
在多數的使用情境下,這些資訊大多可以合理地互相轉換
只要在轉換時,把需要補充的資料給補充進去即可。
表達時間流上的一個瞬間
在 JSR-310 中,它明確地把「時間」與「曆法」切割成兩種不同的表達
也就是使用 Instant 這個類別,用來表示時間流上的某一個瞬間。
這個瞬間,是以 Unix Time 來表示。
原則上,一個 Instant 因為是以 Unix Time 來表示,因此它同時代表了年月日時分秒
但它不包含時區資訊,因為 Unix Time 基本上是表示 UTC+0 的時間。
因此,Instant 可以轉成多數的其他表達方法,但在部份情況下需要額外提供時區資訊。
表達部份時間資訊
在人類使用時間的過程中,其實很多情況是不需要完整的資訊
比如說表示「數天期間」、「某年某月某日」、「幾點幾分」等等。
在 JSR-310 中,延續了 Joda-Time 在這方面的設計,有了 Duration、LocalDate 與 LocalTime 的設計。
Duration 是用來表示一段期間、LocalDate 用來表示一個日期、而 LocalTime 則用來表示一個時間。
表達時區資訊
時區的資訊,在 JSR-310 中有兩種表達方式:ZoneId 與 ZoneOffset
具體來說,兩者都可以用來表達時區,但又有一點細節上的不同。
ZoneId 是根據 Time-zone ID 的標準去表示某一個時區,並且這個時區是指特定國家代表的時區
而 ZoneOffset 則是表示一個時區的時差。
在一般狀況底下,ZoneId 跟 ZoneOffset 是類似的東西,但當目標地區存在日光節約時間時就是例外狀況。
因為在實施日光節約時間的期間,地區的時差會產生變動
例如墨西哥平時的時區是 UTC-8 到 UTC-5,但在日光節約時間的期間會變成 UTC-7 到 UTC-5。
表達完整的時間資訊
除了上述這些表達部份資訊的類別以外,當然最重要的就是要表達完整的日期與時間
在 JSR-310 中就是使用 LocalDateTime 與 ZonedDateTime。
兩者的差別在於,LocalDateTime 表示的是單純的日期與時間,並沒有特地對應到某個時區
因此如果需要表達特定時區的某個時間瞬間時,必須在 LocalDateTime 上面附加 ZoneId 後,變成 ZonedDateTime。
格式轉換
在 JSR-310 中,各個格式可以輕易且容易理解地互相轉換。
當要取得某個類別時,通常可以用 of() 系列的函式來取得
例如 ZonedDateTime.ofInstant() 可以從 Instant 加上 ZoneId 資訊後獲得 ZonedDateTime。
另一種方式是讓某個類別,透過 at() 系列的函式附加資訊後,成為另一個類別
例如 LocalDate 是單純表示日期,只要用 atTime() 加上時間後,就可以產生 LocalDateTime
而 LocalDateTime 再用 atZone() 加上 ZoneId 後,就可以產生 ZonedDateTime。
除此之外,當需要從一段字串解析其中的日期時間時,可以使用 DateTimeFormatter 類別。
這個類別中也有內建的許多 Pattern,可以處理常見的格式,包括已經頻繁被使用的時間表達格式的標準 RFC 1123 和 ISO 8601。
相關資源
上篇:在 Java 中解析、表達與計算日期時間(一):時間系統與曆法
沒有留言:
張貼留言