• (3)使用 Joda-Time


    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 時間相同)…

    在實作上,使用 ReadableInstant 介面來定義 Instant 的行為,其實作類別包括有:

    • Instant:簡單的實作類別,常用作時區、日歷轉換時的中性資料。
    • DateTime:最常用的類別,常搭配時區、日歷資訊來建立或取得日期、時間等欄位資訊。
    • MutableDateTime:前三個類別的實例都是不可變(Immutable),MutableDateTime 的實例為可變。

    Joda-Time 記取了 Date 與 Calendar 實例為可變時可能發生的問題,因而 Instant、DateTime 都設計為不可變。ReadableInstant 介面的實例,行為上可基於毫秒數來進行時間運算,若要取得年、月、日、時、分、秒等欄位資訊,必須提供時區與年曆資訊。例如,若要取得目前時間,並使用預設年曆與時區(稍後說明)來取得月份資訊,以下是個簡單範例:

    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,實作上由 ReadablePartial 定義出 Partial 的行為,其實作包括有以下類別,實例皆為不可變,從名稱上應可一目瞭然各自的作用:

    • LocalDate、LocalTime、LocalDateTime
    • YearMonth、MonthDay
    • Partial
    • YearMonthDay
    • TimeOfDay

    既然是片段的時間資訊,那麼只要補齊不全的部份,就可以轉為時間軸上確切的某個瞬間…

    具體來說,也就是 ReadablePartial 的實例可以組合以產生 ReadableInstant 的實例。例如:

    LocalDate date = new LocalDate(2004, 12, 25); // 年、月、日的日期片段資訊
    LocalTime time = new LocalTime(12, 20); // 時、分的時間片段資訊
    DateTime dt = date.toDateTime(time); // 使用預設時區
    

    反過來說,如果你有個 ReadableInstant 實例,像是 DateTime,也可以取得其中的時間片段。例如:

    DateTime dt = new DateTime();
    LocalDate date = new LocalDate(dt);  // 只取出日期片段資訊
    

    Interval

    有時你會想要表達時間上某個區段,像是西元 1975 年 5 月 26 日到 西元 1978 年 7 月 23 日,像這類包括開始瞬間與結束瞬間的區段,Joda-Time 定義為 Interval …

    實作上由 ReadableInterval 介面定義行為,實作類別有:

    • Interval
    • MutableInterval

    建立 Interval 實例的程式片段之一如下:

    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,不過持續的時間單位是毫秒。實作上使用 ReadableDuration 介面定義,實作類別有 Duration。

    自然地,如果在某個瞬間加上 Duration,就會得到另一個瞬間:

    具體來說,可以給予 ReadableInstant 的實例某個 ReadableDuration 實例,從而得到另一個 ReadableInstant,例如 …

    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
    • MutablePeriod
    • Years、Months、Weeks、Days
    • Hours、Minutes、Seconds

    自然地,如果在某個瞬間加上 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 設計上,年曆系統是可以抽換的,用以從某個瞬間計算日期時間的各個欄位,Chronology 是年曆實作類別的抽象父類別,許多情況下若沒有指定,會使用 ISOChronology 作為預設,這是依 ISO8601 的實作類別。

    在 【Joda-Time 與 JSR310 】(2)時間的 ABC 中談過,JDK 中 Calendar 的實作類別 GregorianCalendar,其實是個儒略曆與格里高利曆的混合年曆,在 Joda-Time 中對應的實作類別是 GJChronology,如果你要純綷的儒略曆,在 Joda-Time 中是 JulianChronology,如果你要純綷的格里高利曆,在 Joda-Time 中是 GregorianChronology。除此之外,Joda-Time 中還有…

    • Buddhist – BuddhistChronology
    • Coptic – CopticChronology
    • Ethiopic – EthiopicChronology
    • Islamic – IslamicChronology
      要使用科普特曆(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 概念,也就是 ReadablePartial 的實例之一 LocalDate:

    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 中 Days.daysBetween 之類的方法:

    再來看個需求好了,如果要知道某個日期起加上 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 的設計上又有什麼不同呢?這會是下篇文章要探討的主題!

  • 相关阅读:
    vue--组件基础
    vue中的一些知识点--多看文档
    关于组件--React
    数组方法-->map()
    正则表达式使用
    border-image 和 border-color 不能同时使用问题
    gulp
    oninput 中文输入
    linux文档权限
    为什么使用 use strict
  • 原文地址:https://www.cnblogs.com/aprz512/p/5622853.html
Copyright © 2020-2023  润新知