任何企業應用程序都需要處理時間問題。應用程序需要知道當前的時間點和下一個時間點,有時它們還必須計算這兩個時間點之間的路徑。使用 JDK 完成這項任務將非常痛苦和繁瑣。現在來看看 Joda Time,一個面向 Java™ 平台的易於使用的開源時間/日期庫。正如您在本文中了解的那樣,Joda-Time 輕松化解了處理日期和時間的痛苦和繁瑣。
在編寫企業應用程序時,我常常需要處理日期。並且在我的最新項目中 — 保險行業 — 糾正日期計算尤其重要。使用 java.util.Calendar
讓我有些不安。如果您也曾使用這個類處理過日期/時間值,那么您就知道它使用起來有多麻煩。因此當我接觸到 Joda-Time — 面向 Java 應用程序的日期/時間庫的替代選擇 — 我決定研究一下。其結果是:我很慶幸我這么做了。
Joda-Time 令時間和日期值變得易於管理、操作和理解。事實上,易於使用是 Joda 的主要設計目標。其他目標包括可擴展性、完整的特性集以及對多種日歷系統的支持。並且 Joda 與 JDK 是百分之百可互操作的,因此您無需替換所有 Java 代碼,只需要替換執行日期/時間計算的那部分代碼。
Joda 實際上是涵蓋眾多用於 Java 語言的替代 API 的大型項目,因此從技術上講,使用 Joda 和 Joda-Time 名稱表示相同的意思是一種誤稱。但在撰寫本文之際,Joda-Time API 目前似乎是唯一處於活躍開發狀態下的 Joda API。考慮到 Joda 大型項目的當前狀態,我想將 Joda-Time 簡稱為 Joda 應該沒什么問題。
為什么要使用 Joda?考慮創建一個用時間表示的某個隨意的時刻 — 比如,2000 年 1 月 1 日 0 時 0 分。我如何創建一個用時間表示這個瞬間的 JDK 對象?使用 java.util.Date
?事實上這是行不通的,因為自 JDK 1.1 之后的每個 Java 版本的 Javadoc 都聲明應當使用 java.util.Calendar
。Date
中不贊成使用的構造函數的數量嚴重限制了您創建此類對象的途徑。
然而,Date
確實有一個構造函數,您可以用來創建用時間表示某個瞬間的對象(除 “現在” 以外)。該方法使用距離 1970 年 1 月 1 日子時格林威治標准時間(也稱為 epoch)以來的毫秒數作為一個參數,對時區進行校正。考慮到 Y2K 對軟件開發企業的重要性,您可能會認為我已經記住了這個值 — 但是我沒有。Date
也不過如此。
那么 Calendar
又如何呢?我將使用下面的方式創建必需的實例:
Calendar calendar = Calendar.getInstance(); calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0); |
使用 Joda,代碼應該類似如下所示:
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0); |
Joda 使用以下概念,它們可以應用到任何日期/時間庫:
- 不可變性(Immutability)
- 瞬間性(Instant)
- 局部性(Partial)
- 年表(Chronology)
- 時區(Time zone)
我將針對 Joda 依次討論每一個概念。
我在本文討論的 Joda 類具有不可變性,因此它們的實例無法被修改。(不可變類的一個優點就是它們是線程安全的)。我將向您展示的用於處理日期計算的 API 方法全部返回一個對應 Joda 類的新實例,同時保持原始實例不變。當您通過一個 API 方法操作 Joda 類時,您必須捕捉該方法的返回值,因為您正在處理的實例不能被修改。您可能對這種模式很熟悉;比如,這正是 java.lang.String
的各種操作方法的工作方式。
Instant
表示時間上的某個精確的時刻,使用從 epoch 開始計算的毫秒表示。這一定義與 JDK 相同,這就是為什么任何 Joda Instant
子類都可以與 JDK Date
和 Calendar
類兼容的原因。
更通用一點的定義是:一個瞬間 就是指時間線上只出現一次且唯一的一個時間點,並且這種日期結構只能以一種有意義的方式出現一次。
一個局部時間,正如我將在本文中將其稱為局部時間片段一樣,它指的是時間的一部分片段。瞬間性指定了與 epoch 相對的時間上的一個精確時刻,與此相反,局部時間片段指的是在時間上可以來回 “移動” 的一個時刻,這樣它便可以應用於多個實例。比如,6 月 2 日 可以應用於任意一年的 6 月份(使用 Gregorian 日歷)的第二天的任意瞬間。同樣,11:06 p.m. 可以應用於任意一年的任意一天,並且每天只能使用一次。即使它們沒有指定一個時間上的精確時刻,局部時間片段仍然是有用的。
我喜歡將局部時間片段看作一個重復周期中的一點,這樣的話,如果我正在考慮的日期構建可以以一種有意義的方式出現多次(即重復的),那么它就是一個局部時間。
Joda 本質 — 以及其設計核心 — 的關鍵就是年表(它的含義由一個同名抽象類捕捉)。從根本上講,年表是一種日歷系統 — 一種計算時間的特殊方式 — 並且是一種在其中執行日歷算法的框架。受 Joda 支持的年表的例子包括:
- ISO(默認)
- Coptic
- Julian
- Islamic
Joda-Time 1.6 支持 8 種年表,每一種都可以作為特定日歷系統的計算引擎。
時區是值一個相對於英國格林威治的地理位置,用於計算時間。要了解事件發生的精確時間,還必須知道發生此事件的位置。任何嚴格的時間計算都必須涉及時區(或相對於 GMT),除非在同一個時區內發生了相對時間計算(即時這樣時區也很重要,如果事件對於位於另一個時區的各方存在利益關系的話)。
DateTimeZone
是 Joda 庫用於封裝位置概念的類。許多日期和時間計算都可以在不涉及時區的情況下完成,但是仍然需要了解 DateTimeZone
如何影響 Joda 的操作。默認時間,即從運行代碼的機器的系統時鍾檢索到的時間,在大部分情況下被使用。
談到日期處理,Joda 是一種令人驚奇的高效工具。無論您是計算日期、打印日期,或是解析日期,Joda 都將是工具箱中的便捷工具。在本文中,我首先介紹了 Joda,它可以作為 JDK 日期/時間庫的替代選擇。然后介紹了一些 Joda 概念,以及如何使用 Joda 執行日期計算和格式化。
Joda-Time 衍生了一些相關的項目,您可能會發現這些項目很有用。現在出現了一個針對 Grails Web 開發框架的 Joda-Time 插件。joda-time-jpox 項目的目標就是添加一些必需的映射,以使用 DataNucleus 持久化引擎持久化 Joda-Time 對象。並且,一個針對 Google Web Toolkit(也稱為 Goda-Time)的 Joda-Time 實現目前正在開發當中