2025年1月18日 星期六

JAVA 7 to 17 DateTime

一、前言

一、前言

返回目錄

在 Java 的發展歷程中,日期和時間的處理一直是一個令人頭痛的問題。舊有的 java.util.Date java.util.Calendar 類別設計複雜、使用起來不直觀,而且不是執行緒安全的。為了徹底解決這些問題,Java 8 引入了全新的 java.time API,這是一個基於 Joda-Time 函式庫的強大、現代化的日期和時間 API。從 Java 8 到 Java 17,雖然 java.time 的核心 API 沒有發生重大變更,但這個 API 在各種情境下的應用更加成熟,也增加了更多與之配合的功能。本文將梳理 Java 7 至 Java 17 中關於日期和時間的新特性,並著重介紹 java.time API,以便讀者更好地理解和使用 Java 的日期和時間處理。

目錄



二、Java 7 及之前的日期和時間處理

返回目錄

在 Java 7 及更早的版本中,處理日期和時間主要依賴於以下兩個類別:

  1. java.util.Date
    • 這個類別代表一個特定的時間點,精確到毫秒。
    • Date 本身是可變的 (mutable),這在多執行緒環境下容易造成問題。
    • Date 類別的 API 設計不直觀,很多方法已經被標記為過時 (deprecated)。
  2. java.util.Calendar
    • Calendar 類別是一個抽象類別,用於在 Date 和日期欄位 (例如:年、月、日、時、分、秒) 之間進行轉換。
    • Calendar 類別的 API 複雜且難以使用,很多方法需要仔細閱讀文件才能正確使用。

舊有 API 的缺點:

  • 可變性 (Mutability): Date 是可變的,容易在多執行緒環境下產生問題,需要額外的同步處理。
  • API 設計不佳: 方法命名不一致,過於複雜,容易混淆。
  • 執行緒安全性問題: Date Calendar 都不是執行緒安全的。
  • 時區處理困難: 舊的 API 在時區處理方面較為複雜和容易出錯。

三、Java 8: java.time API 的誕生

返回目錄

Java 8 引入了全新的 java.time API,這是對日期和時間處理的一次重大革新,它提供了一組更加現代化、易用和執行緒安全的類別,解決了舊有 API 的種種問題。

java.time API 的核心概念:

  1. 不可變性 (Immutability): java.time API 中的類別都是不可變的,每次修改都會產生新的物件,避免了執行緒安全問題。
  2. 清晰的 API 設計: java.time API 提供了清晰、直觀的 API,更容易理解和使用。
  3. 明確的類別區分: java.time API 將不同的日期和時間概念拆分為不同的類別,例如:
    • LocalDate :表示日期,不包含時間,例如: 2024-05-03
    • LocalTime :表示時間,不包含日期,例如: 14:30:00
    • LocalDateTime :表示日期和時間,例如: 2024-05-03T14:30:00
    • ZonedDateTime :表示帶時區的日期和時間。
    • Instant :表示時間軸上的一個點,通常用於表示機器時間。
    • Duration :表示時間長度 (以秒和納秒計算)。
    • Period :表示日期長度 (以年、月、日計算)。
    • DateTimeFormatter :用於格式化和解析日期和時間。

常用的 java.time API:

  • now() 方法: 用於取得當前的日期、時間或日期和時間。例如: LocalDate.now() LocalDateTime.now()
  • of() 方法: 用於建立指定日期和時間的物件。例如: LocalDate.of(2024, 5, 3) LocalDateTime.of(2024, 5, 3, 14, 30, 0)
  • parse() 方法: 用於從字串解析日期和時間。例如: LocalDate.parse("2024-05-03")
  • format() 方法: 用於將日期和時間格式化為字串。例如: localDate.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))
  • plus() minus() 方法: 用於加減日期和時間。例如: localDate.plusDays(1) localTime.minusHours(2)
  • with() 方法: 用於修改日期和時間的特定欄位。例如: localDate.withYear(2025)

四、 java.time API 演進

返回目錄

從 Java 9 到 Java 17, java.time API 本身沒有發生重大變更,其核心類別和 API 都保持不變。然而,Java 語言的整體發展以及其他 API 的改進,使得 java.time 的使用場景更加豐富,並與其他特性更好地整合:

  • 更好的時區支援:
    • Java 9 導入了新的時區資料 tzdata ,讓時區的處理更加準確。
  • 與 Stream API 的整合:
    • java.time API 的類別可以與 Stream API 很好地搭配使用,進行日期和時間的篩選、排序、轉換等操作。例如,可以使用 Stream 來處理一系列日期,並找出其中的最大或最小日期。
  • 更方便的格式化和解析:
    • DateTimeFormatter 提供了更多預定義的格式,並且可以自定義格式。
  • 更好的效能:
  • Java 11 開始對 java.time API 進行了一些效能改進,例如減少物件建立的次數、優化內部計算等。

雖然沒有重大的 API 變更,但這些改進讓開發者在使用 java.time 時,能夠更有效地處理各種複雜的日期和時間情境。

五、 java.time API 的應用範例

返回目錄

以下是一些 java.time API 的應用範例:

  • 取得當前日期和時間:

  • 建立特定日期和時間的物件:

  • 格式化日期和時間:

  • 解析日期和時間:

  • 日期和時間的加減:

六、時區資訊

返回目錄

在處理日期和時間時,時區 (Time Zone) 是非常重要的考量因素。時區是用來定義地球上不同地區的時間差異。Java 8 引入的 java.time API 對時區的處理提供了更完善的支持,主要依賴於以下幾個類別:

  1. ZoneId :

    • ZoneId 類別代表一個時區的識別符,例如 "America/Los_Angeles" "Asia/Tokyo" "UTC" 等。
    • 可以使用 ZoneId.of(String zoneId) 方法來創建 ZoneId 物件,或使用 ZoneId.systemDefault() 取得系統預設時區。
    • ZoneId 可以用來建立 ZonedDateTime 物件。
  2. ZoneOffset :

    • ZoneOffset 類別代表與 UTC (Coordinated Universal Time) 的時間偏移量,例如 "+08:00" "-05:00"
    • ZoneOffset 可以用來建立 OffsetDateTime OffsetTime 物件。
    • 可以使用 ZoneOffset.of(String offsetId) 方法來創建 ZoneOffset 物件。
  3. ZonedDateTime :

    • ZonedDateTime 類別表示帶有時區資訊的日期和時間。
    • ZonedDateTime 可以通過 LocalDateTime ZoneId 創建,也可以使用 ZonedDateTime.now(ZoneId zone) 來取得指定時區的當前時間。
    • ZonedDateTime 可以進行時區之間的轉換。

如何取得時區資訊?

  • 系統預設時區: 使用 ZoneId.systemDefault() 來取得系統的預設時區。
  • 所有可用的時區: 使用 ZoneId.getAvailableZoneIds() 來取得所有可用的時區 ID。
  • 指定時區: 使用 ZoneId.of(String zoneId) 來取得特定時區的 ZoneId 物件。

範例:時區轉換的程式碼

程式碼說明:

  • printAvailableTimeZones()
    • 使用 ZoneId.getAvailableZoneIds() 取得所有可用的時區 ID。
    • 列印所有時區 ID。
  • timeZoneConversion()
    • 取得當前的 LocalDateTime 物件。
    • 使用 ZoneId.of("Asia/Taipei") 建立台北時區。
    • 使用 ZonedDateTime.of(localDateTime, taipeiZone) 建立帶時區的台北時間。
    • 使用 withZoneSameInstant(losAngelesZone) 將台北時間轉換為洛杉磯時間。
    • 使用 withZoneSameInstant(utcZone) 將台北時間轉換為 UTC 時間。
    • 使用 withZoneSameInstant(systemZone) 將台北時間轉換為系統預設時區時間。
    • 使用 DateTimeFormatter 格式化輸出。

程式碼輸出:
可用的時區放在附錄 [1]

七、時間格式 (轉換) 範例

返回目錄

DateTimeFormatter 類別提供了強大的日期和時間格式化和解析功能。以下是一些範例:

程式碼說明:

  • dateTimeFormatting():
    • 使用 LocalDateTime.now() 取得當前日期和時間。
    • 使用 DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)、MEDIUM 和 LONG 取得預定義的格式。
    • 使用 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") 建立自定義的格式。
    • 使用 DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale( Locale.US ) 建立指定美國地區的格式。
    • 使用 format() 方法將日期和時間格式化為字串。
  • dateTimeParsing():
    • 定義一個日期和時間的字串。
    • 使用 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") 建立對應格式的 DateTimeFormatter。
    • 使用 DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.TAIWAN) 建立指定台灣地區的格式。
    • 使用 LocalDateTime.parse() 和 LocalDate.parse() 方法從字串解析日期和時間。
    • 印出解析結果。
    • 解析帶有時區的日期字串時,先將時區資訊切割掉,再使用 DateTimeFormatter 解析

程式碼輸出 (會因執行時間而異):

八、總結

返回目錄

Java 的日期和時間處理從早期 java.util.Date java.util.Calendar 的混亂局面,到 Java 8 引入 java.time API 後,得到了根本性的改善。 java.time API 以其不可變性、清晰的設計和執行緒安全性,解決了舊有 API 的諸多問題,使得日期和時間的處理變得更加容易和可靠。

java.time API 提供了非常彈性和易用的方式來處理日期、時間、時區和格式轉換。 透過 ZoneId 和 ZonedDateTime,我們可以方便地進行時區轉換,而 DateTimeFormatter 則提供了多樣化的格式化和解析能力。

到 Java 17, java.time 的核心 API 雖然沒有發生大的變化,但通過與其他新特性的整合,例如 Stream API 的搭配使用,使得開發者能夠更高效地處理各種複雜的日期和時間情境。理解並善用 java.time API 是現代 Java 開發中不可或缺的一部分,它能讓你寫出更健壯、更易於維護的程式碼。

附錄:

返回目錄

時區 ID 時區 ID 時區 ID 時區 ID 時區 ID
Asia/Aden America/Cuiaba Etc/GMT+9 Etc/GMT+8 Africa/Nairobi
America/Marigot Asia/Aqtau Pacific/Kwajalein America/El_Salvador Asia/Pontianak
Africa/Cairo Pacific/Pago_Pago Africa/Mbabane Asia/Kuching Pacific/Honolulu
Pacific/Rarotonga America/Guatemala Australia/Hobart Europe/London America/Belize
America/Panama Asia/Chungking America/Managua America/Indiana/Petersburg Asia/Yerevan
Europe/Brussels GMT Europe/Warsaw America/Chicago Asia/Kashgar
Chile/Continental Pacific/Yap CET Etc/GMT-1 Etc/GMT-0
Europe/Jersey America/Tegucigalpa Etc/GMT-5 Europe/Istanbul America/Eirunepe
Etc/GMT-4 America/Miquelon Etc/GMT-3 Europe/Luxembourg Etc/GMT-2
Etc/GMT-9 America/Argentina/Catamarca Etc/GMT-8 Etc/GMT-7 Etc/GMT-6
Europe/Zaporozhye Canada/Yukon Canada/Atlantic Atlantic/St_Helena Australia/Tasmania
Libya Europe/Guernsey America/Grand_Turk Asia/Samarkand America/Argentina/Cordoba
Asia/Phnom_Penh Africa/Kigali Asia/Almaty US/Alaska Asia/Dubai
Europe/Isle_of_Man America/Araguaina Cuba Asia/Novosibirsk America/Argentina/Salta
Etc/GMT+3 Africa/Tunis Etc/GMT+2 Etc/GMT+1 Pacific/Fakaofo
Africa/Tripoli Etc/GMT+0 Israel Africa/Banjul Etc/GMT+7
Indian/Comoro Etc/GMT+6 Etc/GMT+5 Etc/GMT+4 Pacific/Port_Moresby
US/Arizona Antarctica/Syowa Indian/Reunion Pacific/Palau Europe/Kaliningrad
America/Montevideo Africa/Windhoek Asia/Karachi Africa/Mogadishu Australia/Perth
Brazil/East Etc/GMT Asia/Chita Pacific/Easter Antarctica/Davis
Antarctica/McMurdo Asia/Macao America/Manaus Africa/Freetown Europe/Bucharest
Asia/Tomsk America/Argentina/Mendoza Asia/Macau Europe/Malta Mexico/BajaSur
Pacific/Tahiti Africa/Asmera Europe/Busingen America/Argentina/Rio_Gallegos Africa/Malabo
Europe/Skopje America/Catamarca America/Godthab Europe/Sarajevo Australia/ACT
GB-Eire Africa/Lagos America/Cordoba Europe/Rome Asia/Dacca
Indian/Mauritius Pacific/Samoa America/Regina America/Fort_Wayne America/Dawson_Creek
Africa/Algiers Europe/Mariehamn America/St_Johns America/St_Thomas Europe/Zurich
America/Anguilla Asia/Dili America/Denver Africa/Bamako Europe/Saratov
GB Mexico/General Pacific/Wallis Europe/Gibraltar Africa/Conakry
Africa/Lubumbashi Asia/Istanbul America/Havana NZ-CHAT Asia/Choibalsan
America/Porto_Acre Asia/Omsk Europe/Vaduz US/Michigan Asia/Dhaka
America/Barbados Europe/Tiraspol Atlantic/Cape_Verde Asia/Yekaterinburg America/Louisville
Pacific/Johnston Pacific/Chatham Europe/Ljubljana America/Sao_Paulo Asia/Jayapura
America/Curacao Asia/Dushanbe America/Guyana America/Guayaquil America/Martinique
Portugal Europe/Berlin Europe/Moscow Europe/Chisinau America/Puerto_Rico
America/Rankin_Inlet Pacific/Ponape Europe/Stockholm Europe/Budapest America/Argentina/Jujuy
Australia/Eucla Asia/Shanghai Universal Europe/Zagreb America/Port_of_Spain
Europe/Helsinki Asia/Beirut Asia/Tel_Aviv Pacific/Bougainville US/Central
Africa/Sao_Tome Indian/Chagos America/Cayenne Asia/Yakutsk Pacific/Galapagos
Australia/North Europe/Paris Africa/Ndjamena Pacific/Fiji America/Rainy_River
Indian/Maldives Australia/Yancowinna SystemV/AST4 Asia/Oral America/Yellowknife
Pacific/Enderbury America/Juneau Australia/Victoria America/Indiana/Vevay Asia/Tashkent
Asia/Jakarta Africa/Ceuta Asia/Barnaul America/Recife America/Buenos_Aires
America/Noronha America/Swift_Current Australia/Adelaide America/Metlakatla Africa/Djibouti
America/Paramaribo Asia/Qostanay Europe/Simferopol Europe/Sofia Africa/Nouakchott
Europe/Prague America/Indiana/Vincennes Antarctica/Mawson America/Kralendijk Antarctica/Troll
Europe/Samara Indian/Christmas America/Antigua Pacific/Gambier America/Indianapolis
America/Inuvik America/Iqaluit Pacific/Funafuti UTC Antarctica/Macquarie
Canada/Pacific America/Moncton Africa/Gaborone Pacific/Chuuk Asia/Pyongyang
America/St_Vincent Asia/Gaza Etc/Universal PST8PDT Atlantic/Faeroe
Asia/Qyzylorda Canada/Newfoundland America/Kentucky/Louisville America/Yakutat America/Ciudad_Juarez
Asia/Ho_Chi_Minh Antarctica/Casey Europe/Copenhagen Africa/Asmara Atlantic/Azores
Europe/Vienna ROK Pacific/Pitcairn America/Mazatlan Australia/Queensland
Pacific/Nauru Europe/Tirane Asia/Kolkata SystemV/MST7 Australia/Canberra
MET Australia/Broken_Hill Europe/Riga America/Dominica Africa/Abidjan
America/Mendoza America/Santarem Kwajalein America/Asuncion Asia/Ulan_Bator
NZ America/Boise Australia/Currie EST5EDT Pacific/Guam
Pacific/Wake Atlantic/Bermuda America/Costa_Rica America/Dawson Asia/Chongqing
Eire Europe/Amsterdam America/Indiana/Knox America/North_Dakota/Beulah Africa/Accra
Atlantic/Faroe Mexico/BajaNorte America/Maceio Etc/UCT Pacific/Apia
GMT0 America/Atka Pacific/Niue Australia/Lord_Howe Europe/Dublin
Pacific/Truk MST7MDT America/Monterrey America/Nassau America/Jamaica
Asia/Bishkek America/Atikokan Atlantic/Stanley Australia/NSW US/Hawaii
SystemV/CST6 Indian/Mahe Asia/Aqtobe America/Sitka Asia/Vladivostok
Africa/Libreville Africa/Maputo Zulu America/Kentucky/Monticello Africa/El_Aaiun
Africa/Ouagadougou America/Coral_Harbour Pacific/Marquesas Brazil/West America/Aruba
America/North_Dakota/Center America/Cayman Asia/Ulaanbaatar Asia/Baghdad Europe/San_Marino
America/Indiana/Tell_City America/Tijuana Pacific/Saipan SystemV/YST9 Africa/Douala
America/Chihuahua America/Ojinaga Asia/Hovd America/Anchorage Chile/EasterIsland
America/Halifax Antarctica/Rothera America/Indiana/Indianapolis US/Mountain Asia/Damascus
America/Argentina/San_Luis America/Santiago Asia/Baku America/Argentina/Ushuaia Atlantic/Reykjavik
Africa/Brazzaville Africa/Porto-Novo America/La_Paz Antarctica/DumontDUrville Asia/Taipei
Antarctica/South_Pole Asia/Manila Asia/Bangkok Africa/Dar_es_Salaam Poland
Atlantic/Madeira Antarctica/Palmer America/Thunder_Bay Africa/Addis_Ababa Asia/Yangon
Europe/Uzhgorod Brazil/DeNoronha Asia/Ashkhabad Etc/Zulu America/Indiana/Marengo
America/Creston America/Punta_Arenas America/Mexico_City Antarctica/Vostok Asia/Jerusalem
Europe/Andorra US/Samoa PRC Asia/Vientiane Pacific/Kiritimati
America/Matamoros America/Blanc-Sablon Asia/Riyadh Iceland Pacific/Pohnpei
Asia/Ujung_Pandang Atlantic/South_Georgia Europe/Lisbon Asia/Harbin Europe/Oslo
Asia/Novokuznetsk CST6CDT Atlantic/Canary America/Knox_IN Asia/Kuwait
SystemV/HST10 Pacific/Efate Africa/Lome America/Bogota America/Menominee
America/Adak Pacific/Norfolk Europe/Kirov America/Resolute Pacific/Kanton
Pacific/Tarawa Africa/Kampala Asia/Krasnoyarsk Greenwich SystemV/EST5
America/Edmonton Europe/Podgorica Australia/South Canada/Central Africa/Bujumbura
America/Santo_Domingo US/Eastern Europe/Minsk Pacific/Auckland Africa/Casablanca
America/Glace_Bay Canada/Eastern Asia/Qatar Europe/Kiev Singapore
Asia/Magadan SystemV/PST8 America/Port-au-Prince Europe/Belfast America/St_Barthelemy
Asia/Ashgabat Africa/Luanda America/Nipigon Atlantic/Jan_Mayen Brazil/Acre
Asia/Muscat Asia/Bahrain Europe/Vilnius America/Fortaleza Etc/GMT0
US/East-Indiana America/Hermosillo America/Cancun Africa/Maseru Pacific/Kosrae
Africa/Kinshasa Asia/Kathmandu Asia/Seoul Australia/Sydney America/Lima
Australia/LHI America/St_Lucia Europe/Madrid America/Bahia_Banderas America/Montserrat
Asia/Brunei America/Santa_Isabel Canada/Mountain America/Cambridge_Bay Asia/Colombo
Australia/West Indian/Antananarivo Australia/Brisbane Indian/Mayotte US/Indiana-Starke
Asia/Urumqi US/Aleutian Europe/Volgograd America/Lower_Princes America/Vancouver
Africa/Blantyre America/Rio_Branco America/Danmarkshavn America/Detroit America/Thule
Africa/Lusaka Asia/Hong_Kong Iran America/Argentina/La_Rioja Africa/Dakar
SystemV/CST6CDT America/Tortola America/Porto_Velho Asia/Sakhalin Etc/GMT+10
America/Scoresbysund Asia/Kamchatka Asia/Thimbu Africa/Harare Etc/GMT+12
Etc/GMT+11 Navajo America/Nome Europe/Tallinn Turkey
Africa/Khartoum Africa/Johannesburg Africa/Bangui Europe/Belgrade Jamaica
Africa/Bissau Asia/Tehran WET Europe/Astrakhan Africa/Juba
America/Campo_Grande America/Belem Etc/Greenwich Asia/Saigon America/Ensenada
Pacific/Midway America/Jujuy Africa/Timbuktu America/Bahia America/Goose_Bay
America/Virgin America/Pangnirtung Asia/Katmandu America/Phoenix Africa/Niamey
America/Whitehorse Pacific/Noumea Asia/Tbilisi Europe/Kyiv America/Montreal
Asia/Makassar America/Argentina/San_Juan Hongkong UCT Asia/Nicosia
America/Indiana/Winamac SystemV/MST7MDT America/Argentina/ComodRivadavia America/Boa_Vista America/Grenada
Asia/Atyrau Australia/Darwin Asia/Khandyga Asia/Kuala_Lumpur Asia/Famagusta
Asia/Thimphu Asia/Rangoon Europe/Bratislava Asia/Calcutta America/Argentina/Tucuman
Asia/Kabul Indian/Cocos Japan Pacific/Tongatapu America/New_York
Etc/GMT-12 Etc/GMT-11 America/Nuuk Etc/GMT-10 SystemV/YST9YDT
Europe/Ulyanovsk Etc/GMT-14 Etc/GMT-13 W-SU America/Merida
EET America/Rosario Canada/Saskatchewan America/St_Kitts Arctic/Longyearbyen
America/Fort_Nelson America/Caracas America/Guadeloupe Asia/Hebron Indian/Kerguelen
SystemV/PST8PDT Africa/Monrovia Asia/Ust-Nera Egypt Asia/Srednekolymsk
America/North_Dakota/New_Salem Asia/Anadyr Australia/Melbourne Asia/Irkutsk America/Shiprock
America/Winnipeg Europe/Vatican Asia/Amman Etc/UTC SystemV/AST4ADT
Asia/Tokyo America/Toronto Asia/Singapore Australia/Lindeman America/Los_Angeles
SystemV/EST5EDT Pacific/Majuro America/Argentina/Buenos_Aires Europe/Nicosia
Pacific/Guadalcanal Europe/Athens US/Pacific Europe/Monaco

  1. The above information is the available time zone. ↩︎

沒有留言:

張貼留言