20230629 6. 日期和時間 API

日期和時間 API

爲什麼處理時間會如此之難呢?問題出在人類自身上

  • Java 1.0 有一個 Date 類,事後證明它過於簡單了
  • 當 Java 1.1 引入 Calendar 類之後,Date 類中的大部分方法就被棄用了。但是, Calendar API 還不夠給力,它的實例是易變的,並且它沒有處理諸如閏秒這樣的問題。
  • 第 3 次升級很吸引人,那就是 Java SE 中引入的 java.time API ,它修正了過去的缺陷,並且應該會服役相當長的一段時間。

時間線( InstantDuration

在歷史上,基本的時間單位 是從地球的自轉中推導出來的。地球自轉一週需要 24 個小時,即 24 * 60 * 60 = 86400 秒,因此,看起來這好像只是一個有關如何精確定義 1 秒的天文度量問題。遺憾的是,地球有輕微的顫動,所以需要更加精確的定義。1967 年,人們根
據銫 133 原子內在的特性推導出了與其歷史定義相匹配的秒的新的精確定義。自那以後,原子鐘網絡就一直被當作官方時間。

官方時間的監護者們時常需要將絕對時間與地球自轉進行同步。首先,官方的秒需要稍作調整,從 1972 年開始,偶爾需要插入“閏秒” (在理論上,偶爾也需要移除 1 秒,但是這還從來沒發生過 )。閏秒是個痛點,許多計算機系統使用“平滑”方式來人爲地在緊鄰閏秒之前讓時間變慢或變快,以保證每天都是 86400 秒。這種做法可以奏效,因爲計算機上的本地時間並非那麼精確,而計算機也慣於將自身時間與外部的時間服務進行同步

Java 的 Date 和 Time API 規範要求 Java 使用的時間尺度爲:

  • 每天 86400 秒
  • 每天正午與官方時間精確匹配
  • 在其他時間點上,以精確定義的方式與官方時間接近匹配

在 Java 中, Instant 表示時間線上的某個點。 被稱爲“新紀元”的時間線原點被設置爲穿過倫敦格林威治皇家天文臺的本初子午線所處時區的 1970 年 1 月 1 日的午夜。這與 UNIX/POSIX 時間中使用的慣例相同。從該原點開始,時間按照每天 86400 秒向前或向回度量,精確到納秒。

Instant 的值向回可追溯 10 億年 ( Instant.MIN )。最大的值 Instant.MAX 是公元 1 000 000 000 年的 12 月 31 日

靜態方法調用 Instant.now() 會給出當前的時刻。你可以按照常用的方式,用 equalscompareTo 方法來比較兩個 Instant 對象,因此你可以將 Instant 對象用作時間戳

爲了得到兩個時刻之間的時間差,可以使用靜態方法 Duration.between

Duration 是兩個時刻之間的時間量。 你可以通過調用 toNanostoMillisgetSecondstoMinutestoHourstoDays 獲得 Duration 按照傳統單位度量的時間長度

Instant instant1 = Instant.now();
System.out.println(instant1);       // 2022-02-08T10:52:40.603Z

Thread.sleep(1234);

Instant instant2 = Instant.now();

Duration duration1 = Duration.between(instant1, instant2);
Duration duration2 = Duration.between(instant2, instant1);

System.out.println(duration1);      // PT1.309S
System.out.println(duration2);      // PT-1.309S

System.out.println(duration1.toMillis());   // 1309

Duration 對象的內部存儲所需的空間超過了一個 long 的值,因此秒數存儲在一個 long 中,而納秒數存儲在一個額外的 int 中。如果想要讓計算精確到納秒級,那麼實際上你需要整個 Duration 的存儲內容。如果不要求這麼高的精度,那麼你可以用 long 的值來執行計算,然後直接調用 toNanos

大約 300 年時間對應的納秒數纔會溢出 long 的範圍

如果想要檢查某個算法是否至少比另一個算法快 10 倍:

Duration timeElapsed = Duration.between(start, end);
Duration timeElapsed2 = Duration.between(start2, end2);
boolean overTenTimesFaster =
        timeElapsed.multipliedBy(10).minus(timeElapsed2).isNegative();
// boolean overTenTimesFaster = timeElapsed.toNanos() * 10 < timeElapsed2.toNanos();

用於時間的 InstantDuration 的算術運算

方法 描述
plusminus 在當前的 InstantDuration 上加上或減去一個 Duration
plusNanosplusMillisplusSecondsminusNanosminusMillisminusSeconds 在當前的 InstantDuration 上加上或減去給定時間單位
plusMinutesplusHoursplusDaysminusMinutesminusHoursminusDays 在當前 Duration 上加上或減去給定時間單位的數值
multipliedBydividedBynegated 返回由當前的 Duration 乘以或除以給定 long-1 而得到的 Duration注意,可以縮放 Duration ,但是不能縮放 Instant
isZeroisNegative 檢查當前的 Duration 是否是 0 或負值

InstantDuration 類都是不可修改的類,所以諸如 multipliedByminus 這樣的方法都會返回一個新的實例

本地日期( LocalDatePeriod

讓我們從絕對時間轉移到人類時間。 Java API 中有兩種 人類時間本地日期/時間時區時間 。本地日期/時間包含日期和當天的時間,但是與時區信息沒有任何關聯。1903 年 6 月 14 日就是一個本地日期的示例( lambda 的發明者 Alonzo Church 在這一天誕生)。因爲這個日期既沒有當天的時間,也沒有時區信息,因此它並不對應精確的時刻。 與之相反的是, 1969 年 7 月 16 日 09:32:00 EDT (阿波羅 11 號發射的時刻)是一個時區日期/時間,表示的是時間線上的一個精確的時刻。

有許多計算並不需要時區,在某些情況下,時區甚至是一種障礙。 假設你安排每週 10:00 開一次會。如果你加 7 天到最後一次會議的時區時間上,那麼你可能會碰巧跨越了夏令時的時間調整邊界,這次會議可能會早一小時或晚一小時!

API 的設計者們推薦程序員不要使用時區時間,除非確實想要表示絕對時間的實例。生日、假日、 計劃時間等通常最好都表示成本地日期和時間。

LocalDate 是帶有年、月、 日的日期。爲了構建 LocalDate 對象,可以使用 nowof 靜態方法:

LocalDate today = LocalDate.now();
LocalDate alonzosBirthday = LocalDate.of(1903, 6, 14);
alonzosBirthday = LocalDate.of(1903, Month.JUNE, 14);

與 UNIX 和 java.util.Date 使用的月從 0 開始計算而年從 1900 開始計算的不規則的慣用法不同,你需要提供通常使用的月份的數字。或者,你可以使用 java.time.Month 枚舉

最有用的操作 LocalDate 對象的方法:

方法 描述
nowof 這些靜態方法會構建一個 LocalDate ,要麼從當前時間構建,要麼從給定的年、月、日構建
plusDaysplusWeeksplusMonthsplusYears 在當前的 LocalDate 上加上一定量的天、星期、月或年
minusDaysminusWeeksminusMonthsminusYears 在當前的 LocalDate 上減去一定量的天、星期、月或年
plusminus 加上或減去一個 DurationPeriod
withDayOfMonthwithDayOfYearwithMonthwithYear 返回一個新的 LocalDate 。其月的日期、年的日期、月或年修改爲給定的值
getDayOfMonth 獲取月的日期(在 131 之間)
getDayOfYear 獲取年的日期(在 1366 之間)
getDayOfWeek 獲取星期日期,返回 DayOfWeek 枚舉值
getMonthgetMonthValue 獲取月份的 Month 枚舉值,或者是 1 - 12 之間的數
getYear 獲取年份,在 -999 999 999 到 999 999 999 之間
until 獲取 Period ,或者兩個日期之間按照給定的 ChronoUnits 計算的數值
isBeforeisAfter 將當前的 LocalDate 與另一個 LocalDate 進行比較
isLeapYear 如果當前是閏年,則返回 true

程序員日是每年的第 256 天,如何很容易地計算出它:

LocalDate programmersDay = LocalDate.of(2022, 1, 1).plusDays(255);

兩個 Instant 之間的時長是 Duration ,而用於本地日期的等價物是 Period ,它表示的是流逝的年、月或日的數量。可以調用 birthday.plus(Period.ofYears(1)) 來獲取下一年的生日。當然,也可以直接調用 birthday.plusYears(1)

util 方法會產生兩個本地日期之間的時長

LocalDate birthday = LocalDate.now();
birthday.plus(Period.ofYears(1));

LocalDate nextBirthday = birthday.plusYears(1);

Period period = birthday.until(nextBirthday);
System.out.println(period);     // P1Y
System.out.println(period.getYears());     // 1


long days = birthday.until(nextBirthday, ChronoUnit.DAYS);
System.out.println(days);   // 365

有些方法可能會創建出並不存在的日期。例如,在 1 月 31 日上加上 1 個月不應該產生 2 月 31 日。這些方法並不會拋出異常,而是會返回該月有效的最後一天

getDayOfWeek 會產生星期日期,即 DayOfWeek 枚舉的某個值。 DayOfWeek.MONDAY 的枚舉值爲 1 ,而 DayOfWeek.SUNDAY 的枚舉值爲 7

DayOfWeek 枚舉具有便捷方法 plusminus ,以 7 爲模計算星期日期

int value = DayOfWeek.MONDAY.getValue();
System.out.println(value);    // 1

DayOfWeek dayOfWeek = DayOfWeek.SATURDAY.plus(3);
System.out.println(dayOfWeek);      // TUESDAY

週末實際上在每週的末尾。這與 java.util.Calendar 所差異,在 Calendar 中,星期六的值爲 1 ,而星期天的值爲 7

除了 LocalDate 之外,還有 MonthDayYearMonthYear 類可以描述部分日期。例如, 12 月 25 日(沒有指定年份)可以表示成一個 MonthDay 對象

MonthDay monthDay = MonthDay.of(Month.DECEMBER, 5);
System.out.println(monthDay);   // --12-05

日期調整器( TemporalAdjusters

TemporalAdjusters 類提供了大量用於常見調整的靜態方法。你可以將調整方法的結果傳遞給 with 方法。例如,某個月的第一個星期二可以像下面這樣計算:

LocalDate firstTuesday = LocalDate.of(year, month, 1).with(
        TemporalAdjusters.nextOrSame(DayOfWeek.TUESDAY));

with 方法會返回一個新 LocalDate 對象, 不會修改原來的對象

TemporalAdjusters 類中的日期調整器:

方法 描述
next(dayOfWeek)previous(dayOfWeek) 下一個或上一個給定的星期日期
nextOrSame(dayOfWeek)previousOrSame(dayOfWeek) 從給定的日期開始的下一個或上一個給定的星期日期
dayOfWeekInMonth(ordinal, dayOfWeek) 月份中的第 n 個 weekday
lastInMonth(DayOfWeek dayOfWeek) 月份中的最後一個 weekday
firstDayOfMonth()firstDayOfNextMonth()firstDayOfNextYear()lastDayOfMonth()lastDayOfYear() 方法名所描述的日期

還可以通過實現 TemporalAdjuster 接口來創建自己的調整器。下面是用於計算下一個工作日的調整器:

LocalDate today = LocalDate.now();

TemporalAdjuster NEXT_WORKDAY = w -> {
    LocalDate result = (LocalDate) w;
    do {
        result = result.plusDays(1);
    } while (result.getDayOfWeek().getValue() >= 6);
    return result;
};

LocalDate backToWork = today.with(NEXT_WORKDAY);

注意, lambda 表達式的參數類型爲 Temporal ,它必須被強制轉型爲 LocalDate 。可以用 ofDateAdjuster 方法來避免這種強制轉型,該方法期望得到的參數是類型爲 UnaryOperator<LocalDate> 的 lambda 表達式

TemporalAdjuster NEXT_WORKDAY = TemporalAdjusters.ofDateAdjuster(w -> {
    LocalDate result = w;   // 無需轉換
    do {
        result = result.plusDays(1);
    } while (result.getDayOfWeek().getValue() >= 6);
    return result;
});

本地時間( LocalTime

LocalTime 表示當日時刻,例如 15:30:00 。可以用 nowof 方法創建其實例:

LocalTime rightNow = LocalTime.now();
LocalTime bedtime = LocalTime.of(22, 30); // or LocalTime.of(22, 30, 0)

plusminus 操作是按照一天 24 小時循環操作的

LocalTime 自身並不關心 AM/PM 。這種愚蠢的設計將問題拋給格式器去解決

LocalTime 的方法:

方法 描述
nowof 這些靜態方法會構建一個 LocalTime ,要麼從當前時間構建,要麼從給定的小時和分鐘,以及可選的秒和納秒構建
plusHoursplusMinutesplusSecondsplusNanos 在當前的 LocalTime 上加上一定量的小時、分鐘、秒或納秒
minusHoursminusMinutesminusSecondsminusNanos 在當前的 LocalTime 上減去一定量的小時、分鐘、秒或納秒
plusminus 加上或減去一個 Duration
withHourwithMinutewithSecondwithNano 返回一個新的 LocalTime ,其小時、分鐘、秒和納秒修改爲給定的值
getHourgetMinutegetSecondgetNano 獲取當前 LocalTime 的小時、分鐘、秒或納秒
toSecondOfDaytoNanoOfDay 返回午夜到當前 LocalTime 的秒或納秒的數量
isBeforeisAfter 將當前的 LocalTime 與另一個 LocalTime 進行比較

還有一個表示日期和時間的 LocalDateTime 類。這個類適合存儲固定時區的時間點,例如,用於排課或排程。但是,如果你的計算需要跨越夏令時,或者需要處理不同時區的用戶,那麼就應該使用 ZonedDateTime

時區時間( ZonedDateTime

時區,可能是因爲完全是人爲創造的原因,它甚至比地球不規則的轉動引發的複雜性還要麻煩。在理性的世界中,我們都會遵循格林尼泊時間。有些人在 2:00 喫午飯,而有些人卻在 22:00 喫午飯。這就是中國的做法,中國橫跨了多個時區,但是使用了同一個時間(東八區 UTC+8)。在其他地方,時區顯得並不規則,並且還有國際日期變更線,而夏令時使事情變得更糟了

互聯網編碼分配管理機構( Internet Assigned Numbers Authority, IANA )保存着一個數據庫,裏面存儲着世界上所有已知的時區,它每年會更新數次,而批量更新會處理夏令時的變更規則。Java 使用了 IANA 數據庫

每個時區都有一個 ID ,例如 America/New_YorkEurope/Berlin 。要想找出所有可用的時區,可以調用 ZoneId.getAvailableZoneIds()

屬於中國的時區 ID 有 6 個:

Asia/Chongqing
Asia/Shanghai
Asia/Urumqi
Asia/Macao
Asia/Hong_Kong
Asia/Taipei

給定一個時區 ID ,靜態方法 ZoneId.of(id) 可以產生一個 ZoneId 對象。可以通過調用 local.atZone(zoneId) 用這個對象將 LocalDateTime 對象轉換爲 ZonedDateTime 對象,或者可以通過調用靜態方法 ZonedDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond, zone) 來構造一個 ZonedDateTime 對象

ZonedDateTime apollo11launch = ZonedDateTime.of(1969, 7, 16, 9, 32, 0, 0,
        ZoneId.of("America/New_York"));
// 1969-07-16T09:32-04:00[America/New_York]

這是一個具體的時刻,調用 apollo11launch.toInstant 可以獲得對應的 Instant 對象。反過來,如果你有一個時刻對象,調用 instant.atZone(ZoneId.of("UTC")) 可以獲得格林威治皇家天文臺的 ZonedDateTime 對象,或者使用其他 ZoneId 獲得地球上其他地方的 ZoneId

時區時間( ZonedDateTime )對應時刻( Instant )的相互轉換:

ZonedDateTime apollo11launch = ZonedDateTime.of(1969, 7, 16, 9, 32, 0, 0,
                                                ZoneId.of("America/New_York"));
System.out.println("apollo11launch: " + apollo11launch);    // apollo11launch: 1969-07-16T09:32-04:00[America/New_York]
Instant instant = apollo11launch.toInstant();
System.out.println("instant: " + instant);      // instant: 1969-07-16T13:32:00Z

// 相同時刻
ZonedDateTime sameInstantZonedDateTime = apollo11launch.withZoneSameInstant(ZoneId.of("Asia/Chongqing"));
System.out.println(sameInstantZonedDateTime);      // 1969-07-16T21:32+08:00[Asia/Chongqing]
System.out.println(sameInstantZonedDateTime.toInstant());      // 1969-07-16T13:32:00Z

// 相同本地時間
ZonedDateTime sameLocalZonedDateTime = apollo11launch.withZoneSameLocal(ZoneId.of("Asia/Chongqing"));
System.out.println(sameLocalZonedDateTime);     // 1969-07-16T09:32+08:00[Asia/Chongqing]
System.out.println(sameLocalZonedDateTime.toInstant());     // 1969-07-16T01:32:00Z

UTC 代表 協調世界時 ,這是英文 Coordinated Universal Time 和法文首字母縮寫的折中,它與這兩種語言中的縮寫都不一致。UTC 是不考慮夏令時的格林威治皇家天文臺時間

ZonedDateTime 的許多方法都與 LocalDateTime 的方法相同,它們大多數都很直接,但是夏令時帶來了一些複雜性

方法 描述
nowofofInstant 構建一個 ZonedDateTime ,要麼從當前時間構建, 要麼從一個 LocalDateTime 、一個 LocalDate 、與 ZoneId 一起的年 / 月 / 日 / 分鐘 / 秒 / 納秒,或從一個 InstantZoneId 中創建。這些都是靜態方法
plusDaysplusWeeksplusMonthsplusYearsplusHoursplusMinutes 、 plusSeconds 、 plusNanos 在當前的 ZonedDateTime 上加上一定量的時間單位
minusDaysminusWeeksminuMonthsminusYearsminusHoursminuMinutesminusSecondsminusNanos 在當前的 ZonedDateTime 上減去一定量的時間單位
plusminus 加上或減去一個 DurationPeriod
withDayOfMonthwithDayOfYearwithMonthwithYearwithHourwithMinutewithSecondwithNano 返回一個新的 ZonedDateTime ,其某個時間單位被修改爲給定的值
withZoneSameInstantwithZoneSameLocal 返回一個給定時區的新的 ZonedDateTime ,要麼表示同一個時刻,要麼表示同一個本地時間
getDayOfMonth 獲取月的日期(在 1 - 31 之間)
getDayOfYear 獲取年的日期(在 1 - 366 之間)
getDayOfWeek 獲取星期日期,返回 DayOfWeek 枚舉的某個值
getMonthgetMonthValue 獲取月份的 Month 枚舉值, 或者在 1 - 12 之間的數字
getYear 獲取年份,在喃999 999 999 - 999 999
getHourgetMinutegetSecondgetNano 獲取當前的 ZonedDateTime 的小時、分鐘、秒和納秒
getOffset 獲取作爲 ZoneOffset 實例的距離 UTC 的偏移量。偏移量在 -12:00 ~+14:00 之間變化。有些時區有小數偏移量。偏移量會隨夏令時而發生變化
toLocalDatetoLocalTimetoInstant 產生本地日期或本地時間 ,或者對應的 Instant 對象
isBeforeisAfter 將當前的 ZonedDateTime 與另一個 ZonedDateTime 進行比較

當夏令時開始 ,時鐘向前撥快一小時。當夏令時結束時,時鐘要向回撥慢一小時,這樣同一個本地時間就會有出現兩次。當你構建位於這個時間段內的時間對象時,就會得到這兩個時刻中較早的一個。一個小時後的時間會具有相同的小時和分鐘,但是時區的偏移量會發生變化。

在調整跨越夏令時邊界的日期時特別注意。例如,如果你將會議設置在下個星期,不要直接加上一個 7 天的 Duration ,而是應該使用 Period 類。

警告:還有一個 OffsetDateTime 類,它表示與 UTC 具有偏移量的時間,但是沒有時區規則的束縛。這個類被設計用於專用應用,這些應用特別需要剔除這些規則的約束,例如某些網絡協議。對於人類時間,還是應該使用 ZonedDateTime

格式化和解析( DateTimeFormatter

DateTimeFormatter 類提供了三種用於打印日期/ 時間值的格式器:

  • 預定義的格式器
  • Locale 相關的格式器
  • 帶有定製模式的格式器

預定義的格式器:

格式器 描述 示例
BASIC_ISO_DATE 年、月、日、時區偏移盤,中間沒有分隔符 19690716-0400
ISO_LOCAL_DATE
ISO_LOCAL_TIME
ISO_LOCAL_DATE_TIME
分隔符爲 - : T 1969-07-16
09:32:00
1969-07-16T09:32:00
ISO_OFFSET_DATE
ISO_OFFSET_TIME
ISO_OFFSET_DATE_TIME
類似 ISO_LOCAL_XXX ,但是有時區偏移量 1969-07-16-04:00
09:32:00-04:00
1969-07-16T09:32:00-04:00
ISO_ZONED_DATE_TIME 有時區偏移量和時區 ID 1969-07-16T09:32:00-04:00[America/New_York]
ISO_INSTANT 在 UTC 中,用 Z 時區 ID 來表示 1969-07-16T13:32:00Z
ISO_DATE
ISO_TIME
ISO_DATE_TIME
類似 ISO_OFFSET_DATEISO_OFFSET_TIMEISO_ZONED_DATE_TIME ,但是時區信息是可選的 1969-07-16-04:00
09:32:00-04:00
1969-07-16T09:32:00-04:00[America/New_York]
ISO_ORDINAL_DATE LocalDate 的年和年日期 1969-197-04:00
ISO_WEEK_DATE LocalDate 的年、星期和星期日期 1969-W29-3-04:00
RFC_1123_DATE_TIME 用於郵件時間戳的標準,編篡於 RFC822 ,並在 RFC1123 中將年份更新到 4 位 Wed, 16 Jul 1969 09:32:00 -0400

使用標準的格式器:

ZonedDateTime apollo11launch = ZonedDateTime.of(1969, 7, 16, 9, 32, 0, 0,
        ZoneId.of("America/New_York"));

String format = DateTimeFormatter.BASIC_ISO_DATE.format(apollo11launch);
System.out.println(DateTimeFormatter.BASIC_ISO_DATE);

標準格式器主要是爲了機器刻度的時間戳而設計的。爲了向人類讀者表示日期和時間,可以使用 Locale 相關的格式器。 對於日期和時間而言,有 4 種與 Locale 相關的格式化風格,即 SHORTMEDIUMLONGFULL ,在 Locale.US 的情況下 :

風格 日期 時間
SHORT 7/16/69 9:32 AM
MEDIUM Jul 16, 1969 9:32:00 AM
LONG July 16, 1969 9:32:00 AM EDT
FULL Wednesday, July 16, 1969 9:32:00 AM EDT

靜態方法 ofLocalizedDateofLocalizedTimeofLocalizedDateTime 可以創建這種格式器,這些方法使用了默認的 Locale ,爲了切換到不同的 Locale ,可以直接使用 withLocale 方法:

DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
String formatted = formatter.format(apollo11launch);
System.out.println(formatted);		// July 16, 1969 9:32:00 AM EDT
formatted = formatter.withLocale(Locale.FRENCH).format(apollo11launch);
// 16 juillet 1969 09:32:00 EDT
System.out.println(formatted);

DayOfWeekMonth 枚舉都有 getDisplayName 方法,可以按照不同的 Locale 和格式給出星期日期和月份的名字:

for (DayOfWeek w : DayOfWeek.values()) {
    System.out.print(w.getDisplayName(TextStyle.SHORT, Locale.ENGLISH) + " ");
}
// Mon Tue Wed Thu Fri Sat Sun

System.out.println();

for (Month m : Month.values()) {
    System.out.print(m.getDisplayName(TextStyle.SHORT, Locale.ENGLISH) + " ");
}
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec 

java.time.format.DateTimeFormatter 類被設計用來替代 java.text.DateFormat 。如果你爲了向後兼容性而需要後者的實例,那麼可以調用 formatter.toFormat()

最後,可以通過指定模式來定製自己的日期格式:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E yyyy-MM-dd HH:mm");
String formatted = formatter.format(apollo11launch);
System.out.println(formatted);  // 星期三 1969-07-16 09:32

按照顯得晦澀且隨時間推移不斷擴充的規則,每個字母都表示一個不同的時間域,而字母重複的次數對應於所選擇的特定格式

常用的日期/時間格式的格式化符號,示例中的結果是在 Locale.US 的情況下 :

時間域或目的 示例
ERA G : AD
GGGG : Anno Domini
GGGGG : A
YEAR_OF_ERA yy : 69
yyyy : 1969
MONTH_OF_YEAR M : 7
MM : 07
MMM : Jul
MMMM : July
MMMMM : J
DAY_OF_MONTH d : 6
dd : 06
DAY_OF_WEEK e : 3
E : Wed
EEEE : Wednesday
EEEEE : W
HOUR_OF_DAY H : 9
HH : 09
CLOCK_HOUR_OF_AM_PM K : 9
KK : 09
AMPM_OF_DAY a : AM
MINUTE_OF_HOUR mm : 02
SECOND_OF_MINUTE ss : 00
NANO_OF_SECOND nnnnnn : 000000
時區 ID VV : America/New_York
時區名 z : EDT
zzzz : Eastern Daylight Time
時區偏移盤 x : -04
xx : -0400
xxx : -04:00
XXX : 與 xxx 相同,但是 Z 表示 0
本地化的時區偏移盤 0 : GMT-4
0000 : GMT-04:00

爲了解析字符串中的日期/時間值,可以使用衆多的靜態 parse 方法之一:

// 使用了標準的 ISO_LOCAL_DATE 格式器
LocalDate churchsBirthday = LocalDate.parse("1903-06-14");
// 使用的是一個定製的格式器
ZonedDateTime apollo11launch = ZonedDateTime.parse("1969-07-16 03:32:00-0400",
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssxx"));

與遺留代碼的互操作

Instant 類近似於 java.util.Date 。在 Java SE 中,這個類有兩個額外的方法:將 Date 轉換爲 InstanttoInstant 方法,以及反方向轉換的靜態的 from 方法

ZonedDateTime 近似於 java.util.GregorianCalendar ,在 Java SE 中,這個類有細粒度的轉換方法。toZonedDateTime 方法可以將 GregorianCalendar 轉換爲 ZonedDateTime ,而靜態的 from 方法可以執行反方向的轉換

另一個可用於日期和時間類的轉換集位於 java.sql 包中。你還可以傳遞一個 DateTimeFormatter 給使用 java.text.Format 的遺留代碼

to 遺留類 from 遺留類
Instantjava.util.Date Date.from(instant) date.toInstant()
ZonedDateTimejava.util.GregorianCalendar GregorianCalendar.from(zonedDateTime) cal.toZonedDateTime()
Instantjava.sql.Timestamp Timestamp.from(instant) timestamp.toInstant()
LocalDateTimejava.sql.Timestamp Timestamp.valueOf(localDateTime) timestamp.toLocalDateTime()
LocalDatejava.sql.Date Date.valueOf(localDate) date.toLocalDate()
LocalTimejava.sql.Time Time.valueOf(localTime) time.toLocalTime()
DateTimeFormatterjava.text.DateFormat formatter.toFormat()
java.util.TimeZoneZoneId Timezone.getTimeZone(id) timeZone.toZoneId()
java.nio.file.attribute.FileTimeInstant FileTime.from(instant) fileTime.toInstant()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章