【Joda-Time 與 JSR310 】(3)使用 Joda-Time
【Joda-Time 與 JSR310 】(2)時間的 ABC << 前情 Joda-Time 是由 Stephen Colebourne 於 2002 年開始建立,版本 1.0 於 2005 年釋出,而 2007 年釋出版本 2.0,撰寫此文件的時候,最新版本為 2.3。 有鑑於 Date 與 Calendar 的問題,Joda-Time 抽取了時間處理時幾個重要觀念,作為實作與使用 API 時的重要考量 … Instant連續時間軸上的某個瞬間,採用 UTC 1970 年 1 月 1 日 00:00:00 至該瞬間歷經的毫秒數(與 Unix 時間相同)… 在實作上,使用
Joda-Time 記取了 DateTime dt = new DateTime(); // 使用預設年曆與時區 int month = dt.getMonthOfYear(); // 取得目前月份,1 就是一月 month = dt.monthOfYear().get(); // 取得目前月份的另一方式 String monthDesc = dt.monthOfYear().getAsText(); // 取得月份的文字描述 Partial人類在日常生活上使用時間,通常不需要完整的時間概念,只需要片段的時間資訊。例如,我們會說某人的生日是「5 月 26 日」,現在的時間是「下午 1 點 6 分」… 實際上,「5 月 26 日」這樣的片段時間資訊,可以指任何一年的「5 月 26 日」,而「下午 1 點 6 分」這樣的片段時間資訊,也可以用於任何一天的「下午 1 點 6 分」… 片段時間資訊的概念,在 Joda-Time 中定義為 Partial,實作上由
既然是片段的時間資訊,那麼只要補齊不全的部份,就可以轉為時間軸上確切的某個瞬間… 具體來說,也就是 LocalDate date = new LocalDate(2004, 12, 25); // 年、月、日的日期片段資訊 LocalTime time = new LocalTime(12, 20); // 時、分的時間片段資訊 DateTime dt = date.toDateTime(time); // 使用預設時區 反過來說,如果你有個 DateTime dt = new DateTime(); LocalDate date = new LocalDate(dt); // 只取出日期片段資訊 Interval有時你會想要表達時間上某個區段,像是西元 1975 年 5 月 26 日到 西元 1978 年 7 月 23 日,像這類包括開始瞬間與結束瞬間的區段,Joda-Time 定義為 Interval … 實作上由 ReadableInterval 介面定義行為,實作類別有:
建立 DateTime start = new DateTime(1975, 5, 26, 0, 0, 0); DateTime end = new DateTime(1978, 7, 23, 0, 0, 0); Interval interval = new Interval(start, end); Duration現在的時間「再 1000 毫秒」後會是多?這類再持續多久的期間概念,就是 Joda-Time 中定義的 Interval,不過持續的時間單位是毫秒。實作上使用 自然地,如果在某個瞬間加上 Duration,就會得到另一個瞬間: 具體來說,可以給予 DateTime start = new DateTime(1975, 5, 26, 0, 0, 0); Duration oneThousandMillis = new Duration(1000); DateTime end = start.plus(oneThousandMillis); Period使用毫秒?那要表示「再三分鐘」、「再兩天」的概念不是很麻煩嗎?是的!沒有錯!人類通常很少使用毫秒,因此,Joda-Time 把人類慣用的時間持續定義為 Period,實作上使用 ReadablePeriod 介面定義行為,實作的類別有以下幾個:
自然地,如果在某個瞬間加上 Period,也會得到另一個瞬間: 只不過這次是人類常用的時間概念,例如,想知道兩個時間軸上的瞬間到底是多少天嗎?可以這麼撰寫程式來得知: DateTime start = new DateTime(1975, 5, 26, 0, 0, 0); DateTime end = new DateTime(1978, 7, 23, 0, 0, 0); Days days = Days.daysBetween(start, end); 以上是有關於使用 Joda-Time 時,應該知道的幾個重要觀念,其中我們看到了年曆與時區 … 在 Joda-Time 的 API 設計上,年曆系統是可以抽換的,用以從某個瞬間計算日期時間的各個欄位, 在 【Joda-Time 與 JSR310 】(2)時間的 ABC 中談過,JDK 中
要使用科普特曆(Coptic calendar)來表示倫敦的日期時間,以下是個示範: DateTimeZone zone = DateTimeZone.forID("Europe/London"); Chronology coptic = CopticChronology.getInstance(zone); DateTime dt = new DateTime(coptic); int year = dt.getYear(); int month = dt.getMonthOfYear(); Joda-Time 會將時區資料編譯包裝為單一 JAR 檔案,你可以按照 Time zone update 資訊,隨時更新與重新編譯 JAR 檔案,Available Time Zones 中則列出了 Joda-Time 中所有的時區資訊。 談了這麼多觀念,該來端出些牛肉,看看使用 Joda-Time 與使用 JDK 的日期時間 API,到底有什麼不同,先來看看原先 Date 與 Calendar 怎麼了? 中有問題的這個片段: DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date birthDate = dateFormat.parse(args[0]); Date nowDate = new Date(); long lifeMillis = nowDate.getTime() - birthDate.getTime(); System.out.printf("你今年的歲數為:%d%n", lifeMillis / (365 * 24 * 60 * 60 * 1000)); 不但囉嗦而且出錯了,改用 Joda-Time 之後是這樣的: Years years = Years.yearsBetween( DateTime.parse("1975-05-26"), DateTime.now()); System.out.printf("你今年的歲數為:%d%n", years.getYears()); 接下來這個片段要表示的日期不正確: Date date = new Date(13, 8, 2); DateFormat dateFormat = DateFormat.getDateInstance(); System.out.printf("Taiwan Java Developer Day is %s.%n", dateFormat.format(date)); 這個也不對: Calendar calendar = Calendar.getInstance(); calendar.set(2013, 8, 2); DateFormat dateFormat = DateFormat.getDateInstance(); System.out.printf("Taiwan Java Developer Day is %s.%n", dateFormat.format(calendar.getTime())); 其實,這兩個程式碼想要的結果,並不需要完整的時間概念,它沒有要時、分、秒的資訊,因而只需要使用 Joda-Time 中的 Partial 概念,也就是 LocalDate javaTwoDate = new LocalDate(2013, 8, 2); System.out.printf("Taiwan Java Developer Day is %s.%n", javaTwoDate); 那原先這個方法呢? public static long daysBetween(Calendar begin, Calendar end) { long daysBetween = 0; while(begin.before(end)) { begin.add(Calendar.DAY_OF_MONTH, 1); daysBetween++; } return daysBetween; } 其實你需要的只是 Joda-Time 中 再來看個需求好了,如果要知道某個日期起加上 5 天、6 個月、3 週後會的日期時間是什麼,並使用指定的格式輸出。使用 JDK 的話,會需要如下的計算: Calendar calendar = Calendar.getInstance(); calendar.set(1975, Calendar.MAY, 26, 0, 0, 0); calendar.add(Calendar.DAY_OF_MONTH, 5); calendar.add(Calendar.MONTH, 6); calendar.add(Calendar.WEEK_OF_MONTH, 3); SimpleDateFormat df = new SimpleDateFormat("E MM/dd/yyyy"); System.out.println(df.format(calendar.getTime())); Joda-Time 實現了 流暢 API 的概念,寫來會輕鬆且流暢易讀: LocalDate birthDate = new LocalDate(1975, 5, 26); System.out.println(birthDate .plusDays(5) .plusMonths(6) .plusWeeks(3).toString("E MM/dd/yyyy")); 那麼,既然 Joda-Time 這麼好,為什麼還要有 JSR310,幹嘛不把 Joda-Time 納入 JDK 就好了呢?JSR310 的設計上又有什麼不同呢?這會是下篇文章要探討的主題! |