在編寫企業應用程序時,我常常需要處理日期。並且在我的最新項目中 — 保險行業 — 糾正日期計算尤其重要。使用 java.util.Calendar 讓我有些不安。如果您也曾使用這個類處理過日期/時間值,那麼您就知道它使用起來有多麻煩。因此當我接觸到 Joda-Time — 面向 Java 應用程序的日期/時間庫的替代選擇 — 我決定研究一下。其結果是:我很慶幸我這麼做了。
Joda-Time 令時間和日期值變得易於管理、操作和理解。事實上,易於使用是 Joda 的主要設計目標。其他目標包括可擴展性、完整的特性集以及對多種日歷系統的支持。並且 Joda 與 JDK 是百分之百可互操作的,因此您無需替換所有 Java 代碼,只需要替換執行日期/時間計算的那部分代碼。
Joda 大型項目
Joda 實際上是涵蓋眾多用於 Java 語言的替代 API 的大型項目,因此從技術上講,使用 Joda 和 Joda-Time 名稱表示相同的意思是一種誤稱。但在撰寫本文之際,Joda-Time API 目前似乎是唯一處於活躍開發狀態下的 Joda API。考慮到 Joda 大型項目的當前狀態,我想將 Joda-Time 簡稱為 Joda 應該沒什麼問題。
本文將介紹並展示如何使用它。我將介紹以下主題:
日期/時間替代庫簡介
Joda 的關鍵概念
創建 Joda-Time 對象
以 Joda 的方式操作時間 style
以 Joda 的方式格式化時間
您可以 下載 演示這些概念的樣例應用程序的源代碼。
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);
這一行簡單代碼沒有太大的區別。但是現在我將使問題稍微復雜化。假設我希望在這個日期上加上 90 天並輸出結果。使用 JDK,我需要使用清單 1 中的代碼:
清單 1. 以 JDK 的方式向某一個瞬間加上 90 天並輸出結果
Calendar calendar = Calendar.getInstance();
calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
SimpleDateFormat sdf =
new SimpleDateFormat("E MM/dd/yyyy HH:mm:ss.SSS");
calendar.add(Calendar.DAY_OF_MONTH, 90);
System.out.println(sdf.format(calendar.getTime()));
使用 Joda,代碼如清單 2 所示:
清單 2. 以 Joda 的方式向某一個瞬間加上 90 天並輸出結果
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");
兩者之間的差距拉大了(Joda 用了兩行代碼,JDK 則是 5 行代碼)。
現在假設我希望輸出這樣一個日期:距離 Y2K 45 天之後的某天在下一個月的當前周的最後一天的日期。坦白地說,我甚至不想使用 Calendar 處理這個問題。使用 JDK 實在太痛苦了,即使是簡單的日期計算,比如上面這個計算。正是多年前的這樣一個時刻,我第一次領略到 Joda-Time 的強大。使用 Joda,用於計算的代碼如清單 3 所示:
清單 3. 改用 Joda
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(45).plusMonths(1).dayOfWeek()
.withMaximumValue().toString("E MM/dd/yyyy HH:mm:ss.SSS");
清單 3 的輸出為:
Sun 03/19/2000 00:00:00.000
如果您正在尋找一種易於使用的方式替代 JDK 日期處理,那麼您真的應該考慮 Joda。如果不是這樣的話,那麼繼續痛苦地使用 Calendar 完成所有日期計算吧。當您做到這一點後,您完全可以做到使用幾把剪刀修建草坪並使用一把舊牙刷清洗您的汽車。
Joda 和 JDK 互操作性
JDK Calendar 類缺乏可用性,這一點很快就能體會到,而 Joda 彌補了這一不足。Joda 的設計者還做出了一個決定,我認為這是它取得成功的構建:JDK 互操作性。Joda 的類能夠生成(但是,正如您將看到的一樣,有時會采用一種比較迂回的方式)java.util.Date 的實例(和 Calendar)。這使您能夠保留現有的依賴 JDK 的代碼,但是又能夠使用 Joda 處理復雜的日期/時間計算。
例如,完成 清單 3 中的計算後。我只需要做出如清單 4 所示的更改就可以返回到 JDK 中:
清單 4. 將 Joda 計算結果插入到 JDK 對象中
Calendar calendar = Calendar.getInstance();
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(45).plusMonths(1).dayOfWeek()
.withMaximumValue().toString("E MM/dd/yyyy HH:mm:ss.SSS");
calendar.setTime(dateTime.toDate());
就是這麼簡單。我完成了計算,但是可以繼續在 JDK 對象中處理結果。這是 Joda 的一個非常棒的特性。
Joda 的關鍵日期/時間概念
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-Time 對象
現在,我將展示在采用該庫時會經常遇到的一些 Joda 類,並展示如何創建這些類的實例。
可變的 Joda 類
我並不是可變實用類的粉絲;我只是認為它們的用例並不適合廣泛使用。但是如果您認為您的確需要使用可變 Joda 類的話,本節的內容應當會對您的項目有幫助。Readable 和 ReadWritable API 之間的唯一區別在於 ReadWritable 類能夠改變封裝的日期/時間值,因此我在這裡將不再介紹這一點。
本節中介紹的所有實現都具有若干構造函數,允許您初始化封裝的日期/時間。它們可以分為 4 個類別:
使用系統時間。
使用多個字段指定一個瞬間時刻(或局部時間片段),達到這個特定實現所能支持的最細粒度的精確度。
指定一個瞬間時刻(或局部時間片段),以毫秒為單位。
使用另一個對象(例如,java.util.Date,或者是另一個 Joda 對象)。
我將在第一個類中介紹這些構造函數: DateTime。當您使用其他 Joda 類的相應構造函數時,也可以使用這裡介紹的內容。
重載方法
如果您創建了一個 DateTime 的實例,並且沒有提供 Chronology 或 DateTimeZone,Joda 將使用 ISOChronology(默認)和 DateTimeZone(來自系統設置)。然而,Joda ReadableInstant 子類的所有構造函數都包含一個超載方法,該方法以一個 Chronology 或 DateTimeZone 為參數。本文附帶的應用程序的的樣例代碼展示了如何使用這些超載方法。我在這裡不會再詳細介紹它們,因為這些方法使用起來非常簡單。然而,我建議您試著使用一下這個樣例應用程序,看看編寫您的應用程序代碼有多麼簡單,這樣您就可以隨意地在 Joda 的 Chronology 和 DateTimeZone 之間切換,同時不會影響到代碼的其余部分。
ReadableInstant
Joda 通過 ReadableInstant 類實現了瞬間性這一概念。表示時間上的不可變瞬間的 Joda 類都屬於這個類的子類。(將這個類命名為 ReadOnlyInstant 可能更好,我認為這才是設計者需要傳達的意思)。換句話說,ReadableInstant 表示時間上的某一個不可修改的瞬間)。其中的兩個子類分別為 DateTime 和 DateMidnight:
DateTime:這是最常用的一個類。它以毫秒級的精度封裝時間上的某個瞬間時刻。DateTime 始終與 DateTimeZone 相關,如果您不指定它的話,它將被默認設置為運行代碼的機器所在的時區。
可以使用多種方式構建 DateTime 對象。這個構造函數使用系統時間:
DateTime dateTime = new DateTime();
一般來講,我會盡量避免使用系統時鐘來初始化應用程序的實際,而是傾向於外部化設置應用程序代碼使用的系統時間。樣例應用程序執行以下代碼: DateTime dateTime = SystemFactory.getClock().getDateTime();
這使得使用不同日期/時間測試我的代碼變得更加簡單:我不需要修改代碼來在應用程序中運行不同的日期場景,因為時間是在 SystemClock 實現的內部設置的,而不是在應用程序的內部。(我可以修改系統時間,但是那實在太痛苦了!)
下面的代碼使用一些字段值構建了一個 DateTime 對象:
DateTime dateTime = new DateTime(
2000, //year
1, // month
1, // day
0, // hour (midnight is zero)
0, // minute
0, // second
0 // milliseconds
);
正如您所見,Joda 可以使您精確地控制創建 DateTime 對象的方式,該對象表示時間上的某個特定的瞬間。每一個 Joda 類都有一個與此類似的構造函數,您在此構造函數中指定 Joda 類可以包含的所有字段。您可以用它快速了解特定類在哪一種粒度級別上操作。
下一個構造函數將指定從 epoch 到某個時刻所經過的毫秒數。它根據 JDK Date 對象的毫秒值創建一個 DateTime 對象,其時間精度用毫秒表示,因為 epoch 與 Joda 是相同的:
java.util.Date jdkDate = obtainDateSomehow();
long timeInMillis = jdkDate.getTime();
DateTime dateTime = new DateTime(timeInMillis);
並且這個例子與前例類似,唯一不同之處是我在這裡將 Date 對象直接傳遞給構造函數:
java.util.Date jdkDate = obtainDateSomehow();
dateTime = new DateTime(jdkDate);
Joda 支持使用許多其他對象作為構造函數的參數,用於創建 DateTime,如清單 5 所示:
清單 5. 直接將不同對象傳遞給 DateTime 的構造函數
// Use a Calendar
java.util.Calendar calendar = obtainCalendarSomehow();
dateTime = new DateTime(calendar);
// Use another Joda DateTime
DateTime anotherDateTime = obtainDateTimeSomehow();
dateTime = new DateTime(anotherDateTime);
// Use a String (must be formatted properly)
String timeString = "2006-01-26T13:30:00-06:00";
dateTime = new DateTime(timeString);
timeString = "2006-01-26";
dateTime = new DateTime(timeString);
注意,如果您准備使用 String(必須經過解析),您必須對其進行精確地格式化。參考 Javadoc,獲得有關 Joda 的 ISODateTimeFormat 類的更多信息。
DateMidnight:這個類封裝某個時區(通常為默認時區)在特定年/月/日的午夜時分的時刻。它基本上類似於 DateTime,不同之處在於時間部分總是為與該對象關聯的特定 DateTimeZone 時區的午夜時分。
您將在本文看到的其他類都遵循與 ReadableInstant 類相同的模式(Joda Javadoc 將顯示這些內容),因此為了節省篇幅,我將不會在以下小節介紹這些內容。
ReadablePartial
應用程序所需處理的日期問題並不全部都與時間上的某個完整時刻有關,因此您可以處理一個局部時刻。例如,有時您比較關心年/月/日,或者一天中的時間,甚至是一周中的某天。Joda 設計者使用 ReadablePartial 接口捕捉這種表示局部時間的概念,這是一個不可變的局部時間片段。用於處理這種時間片段的兩個有用類分別為 LocalDate 和 LocalTime:
LocalDate:該類封裝了一個年/月/日的組合。當地理位置(即時區)變得不重要時,使用它存儲日期將非常方便。例如,某個特定對象的出生日期 可能為 1999 年 4 月 16 日,但是從技術角度來看,在保存所有業務值的同時不會了解有關此日期的任何其他信息(比如這是一周中的星期幾,或者這個人出生地所在的時區)。在這種情況下,應當使用 LocalDate。
樣例應用程序使用 SystemClock 來獲取被初始化為系統時間的 LocalDate 的實例:
LocalDate localDate = SystemFactory.getClock().getLocalDate();
也可以通過顯式地提供所含的每個字段的值來創建 LocalDate:
LocalDate localDate = new LocalDate(2009, 9, 6);// September 6, 2009
LocalDate 替代了在早期 Joda 版本中使用的 YearMonthDay。
LocalTime:這個類封裝一天中的某個時間,當地理位置不重要的情況下,可以使用這個類來只存儲一天當中的某個時間。例如,晚上 11:52 可能是一天當中的一個重要時刻(比如,一個 cron 任務將啟動,它將備份文件系統的某個部分),但是這個時間並沒有特定於某一天,因此我不需要了解有關這一時刻的其他信息。
樣例應用程序使用 SystemClock 獲取被初始化為系統時間的 LocalTime 的一個實例:
LocalTime localTime = SystemFactory.getClock().getLocalTime();
也可以通過顯式地提供所含的每個字段的值來創建 LocalTime:
LocalTime localTime = new LocalTime(13, 30, 26, 0);// 1:30:26PM
時間跨度
了解特定的時刻或是某個局部時間片段將非常有用,但是如果能夠表達一段時間跨度的話,通常也很有用。Joda 提供了三個類來簡化這個過程。您可以選擇用於表示不同跨度的類:
Duration:這個類表示一個絕對的精確跨度,使用毫秒為單位。這個類提供的方法可以用於通過標准的數學轉換(比如 1 分鐘 = 60 秒,1 天 = 24 小時),將時間跨度轉換為標准單位(比如秒、分和小時)。
您只在以下情況使用 Duration 的實例:您希望轉換一個時間跨度,但是您並不關心這個時間跨度在何時發生,或者使用毫秒處理時間跨度比較方便。
Period:這個類表示與 Duration 相同的概念,但是以人們比較熟悉的單位表示,比如年、月、周。
您可以在以下情況使用 Period:您並不關心這段時期必須在何時發生,或者您更關心檢索單個字段的能力,這些字段描述由 Period 封裝的時間跨度。
Interval:這個類表示一個特定的時間跨度,將使用一個明確的時刻界定這段時間跨度的范圍。Interval 為半開 區間,這表示由 Interval 封裝的時間跨度包括這段時間的起始時刻,但是不包含結束時刻。
可以在以下情況使用 Interval:需要表示在時間連續區間中以特定的點開始和結束的一段時間跨度。
以 Joda 的方式處理時間
現在,您已經了解了如何創建一些非常有用的 Joda 類,我將向您展示如何使用它們執行日期計算。接著您將了解到 Joda 如何輕松地與 JDK 進行互操作。
日期計算
如果您只是需要對日期/時間信息使用占位符,那麼 JDK 完全可以勝任,但是它在日期/時間計算方面的表現十分糟糕,而這正是 Joda 的長處。我將向您展示一些簡單的例子。
假設在當前的系統日期下,我希望計算上一個月的最後一天。對於這個例子,我並不關心一天中的時間,因為我只需要獲得年/月/日,如清單 6 所示:
清單 6. 使用 Joda 計算日期
LocalDate now = SystemFactory.getClock().getLocalDate();
LocalDate lastDayOfPreviousMonth =\
now.minusMonths(1).dayOfMonth().withMaximumValue();
您可能對清單 6 中的 dayOfMonth() 調用感興趣。這在 Joda 中被稱為屬性(property)。它相當於 Java 對象的屬性。屬性是根據所表示的常見結構命名的,並且它被用於訪問這個結構,用於完成計算目的。屬性是實現 Joda 計算威力的關鍵。您目前所見到的所有 4 個 Joda 類都具有這樣的屬性。一些例子包括:
yearOfCentury
dayOfYear
monthOfYear
dayOfMonth
dayOfWeek
我將詳細介紹清單 6 中的示例,以向您展示整個計算過程。首先,我從當前月份減去一個月,得到 “上一個月”。接著,我要求獲得 dayOfMonth 的最大值,它使我得到這個月的最後一天。注意,這些調用被連接到一起(注意 Joda ReadableInstant 子類是不可變的),這樣您只需要捕捉調用鏈中最後一個方法的結果,從而獲得整個計算的結果。
當計算的中間結果對我不重要時,我經常會使用這種計算模式。(我以相同的方式使用 JDK 的 BigDecimal)。假設您希望獲得任何一年中的第 11 月的第一個星期二的日期,而這天必須是在這個月的第一個星期一之後。清單 7 展示了如何完成這個計算:
清單 7. 計算 11 月中第一個星期一之後的第一個星期二
LocalDate now = SystemFactory.getClock().getLocalDate();
LocalDate electionDate = now.monthOfYear()
.setCopy(11) // November
.dayOfMonth() // Access Day Of Month Property
.withMinimumValue() // Get its minimum value
.plusDays(6) // Add 6 days
.dayOfWeek() // Access Day Of Week Property
.setCopy("Monday") // Set to Monday (it will round down)
.plusDays(1); // Gives us Tuesday
清單 7 的注釋幫助您了解代碼如何獲得結果。.setCopy("Monday") 是整個計算的關鍵。不管中間 LocalDate 值是多少,將其 dayOfWeek 屬性設置為 Monday 總是能夠四捨五入,這樣的話,在每月的開始再加上 6 天就能夠讓您得到第一個星期一。再加上一天就得到第一個星期二。Joda 使得執行此類計算變得非常容易。
下面是其他一些因為使用 Joda 而變得超級簡單的計算:
以下代碼計算從現在開始經過兩個星期之後的日期:
DateTime now = SystemFactory.getClock().getDateTime();
DateTime then = now.plusWeeks(2);
您可以以這種方式計算從明天起 90 天以後的日期:
DateTime now = SystemFactory.getClock().getDateTime();
DateTime tomorrow = now.plusDays(1);
DateTime then = tomorrow.plusDays(90);
(是的,我也可以向 now 加 91 天,那又如何呢?)
下面是計算從現在起 156 秒之後的時間:
DateTime now = SystemFactory.getClock().getDateTime();
DateTime then = now.plusSeconds(156);
下面的代碼將計算五年後的第二個月的最後一天:
DateTime now = SystemFactory.getClock().getDateTime();
DateTime then = now.minusYears(5) // five years ago
.monthOfYear() // get monthOfYear property
.setCopy(2) // set it to February
.dayOfMonth() // get dayOfMonth property
.withMaximumValue();// the last day of the month
這樣的例子實在太多了,我向您已經知道了如何計算。嘗試操作一下樣例應用程序,親自體驗一下使用 Joda 計算任何日期是多麼有趣。
JDK 互操作性
我的許多代碼都使用了 JDK Date 和 Calendar 類。但是幸虧有 Joda,我可以執行任何必要的日期算法,然後再轉換回 JDK 類。這將兩者的優點集中到一起。您在本文中看到的所有 Joda 類都可以從 JDK Calendar 或 Date 創建,正如您在 創建 Joda-Time 對象 中看到的那樣。出於同樣的原因,可以從您所見過的任何 Joda 類創建 JDK Calendar 或 Date。
清單 8 展示了從 Joda ReadableInstant 子類轉換為 JDK 類有多麼簡單:
清單 8. 從 Joda DateTime 類創建 JDK 類
DateTime dateTime = SystemFactory.getClock().getDateTime();
Calendar calendar = dateTime.toCalendar(Locale.getDefault());
Date date = dateTime.toDate();
DateMidnight dateMidnight = SystemFactory.getClock()
.getDateMidnight();
date = dateMidnight.toDate();
對於 ReadablePartial 子類,您還需要經過額外一步,如清單 9 所示:
清單 9. 創建表示 LocalDate 的 Date 對象
LocalDate localDate = SystemFactory.getClock().getLocalDate();
Date date = localDate.toDateMidnight().toDate();
要創建 Date 對象,它表示從清單 9 所示的 SystemClock 中獲得的 LocalDate,您必須首先將它轉換為一個 DateMidnight 對象,然後只需要將 DateMidnight 對象作為 Date。(當然,產生的 Date 對象將把它自己的時間部分設置為午夜時刻)。
JDK 互操作性被內置到 Joda API 中,因此您無需全部替換自己的接口,如果它們被綁定到 JDK 的話。比如,您可以使用 Joda 完成復雜的部分,然後使用 JDK 處理接口。
以 Joda 方式格式化時間
使用 JDK 格式化日期以實現打印是完全可以的,但是我始終認為它應該更簡單一些。這是 Joda 設計者進行了改進的另一個特性。要格式化一個 Joda 對象,調用它的 toString() 方法,並且如果您願意的話,傳遞一個標准的 ISO-8601 或一個 JDK 兼容的控制字符串,以告訴 JDK 如何執行格式化。不需要創建單獨的 SimpleDateFormat 對象(但是 Joda 的確為那些喜歡自找麻煩的人提供了一個 DateTimeFormatter 類)。調用 Joda 對象的 toString() 方法,僅此而已。我將展示一些例子。
清單 10 使用了 ISODateTimeFormat 的靜態方法:
清單 10. 使用 ISO-8601
DateTime dateTime = SystemFactory.getClock().getDateTime();
dateTime.toString(ISODateTimeFormat.basicDateTime());
dateTime.toString(ISODateTimeFormat.basicDateTimeNoMillis());
dateTime.toString(ISODateTimeFormat.basicOrdinalDateTime());
dateTime.toString(ISODateTimeFormat.basicWeekDateTime());
清單 10 中的四個 toString() 調用分別創建了以下內容:
20090906T080000.000-0500
20090906T080000-0500
2009249T080000.000-0500
2009W367T080000.000-0500
您也可以傳遞與 SimpleDateFormat JDK 兼容的格式字符串,如清單 11 所示:
清單 11. 傳遞 SimpleDateFormat 字符串
DateTime dateTime = SystemFactory.getClock().getDateTime();
dateTime.toString("MM/dd/yyyy hh:mm:ss.SSSa");
dateTime.toString("dd-MM-yyyy HH:mm:ss");
dateTime.toString("EEEE dd MMMM, yyyy HH:mm:ssa");
dateTime.toString("MM/dd/yyyy HH:mm ZZZZ");
dateTime.toString("MM/dd/yyyy HH:mm Z");
09/06/2009 02:30:00.000PM
06-Sep-2009 14:30:00
Sunday 06 September, 2009 14:30:00PM
09/06/2009 14:30 America/Chicago
09/06/2009 14:30 -0500
查看 Javadoc 中有關 joda.time.format.DateTimeFormat 的內容,獲得與 JDK SimpleDateFormat 兼容的格式字符串的更多信息,並且可以將其傳遞給 Joda 對象的 toString() 方法。
結束語
談到日期處理,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 實現目前正在開發當中,但是在撰寫本文之際因為許可問題而被暫停。
本文配套源碼