一、前言
在 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 8:
java.time
API 的誕生 -
四、
java.time
API 演進 -
五、
java.time
API 的應用範例 - 六、時區資訊
- 七、時間格式 (轉換) 範例
- 八、總結
二、Java 7 及之前的日期和時間處理
在 Java 7 及更早的版本中,處理日期和時間主要依賴於以下兩個類別:
-
java.util.Date
:- 這個類別代表一個特定的時間點,精確到毫秒。
-
Date
本身是可變的 (mutable),這在多執行緒環境下容易造成問題。 -
Date
類別的 API 設計不直觀,很多方法已經被標記為過時 (deprecated)。
-
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 的核心概念:
-
不可變性 (Immutability):
java.time
API 中的類別都是不可變的,每次修改都會產生新的物件,避免了執行緒安全問題。 -
清晰的 API 設計:
java.time
API 提供了清晰、直觀的 API,更容易理解和使用。 -
明確的類別區分:
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
,讓時區的處理更加準確。
-
Java 9 導入了新的時區資料
-
與 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 對時區的處理提供了更完善的支持,主要依賴於以下幾個類別:
-
ZoneId
:-
ZoneId
類別代表一個時區的識別符,例如"America/Los_Angeles"
、"Asia/Tokyo"
、"UTC"
等。 -
可以使用
ZoneId.of(String zoneId)
方法來創建ZoneId
物件,或使用ZoneId.systemDefault()
取得系統預設時區。 -
ZoneId
可以用來建立ZonedDateTime
物件。
-
-
ZoneOffset
:-
ZoneOffset
類別代表與 UTC (Coordinated Universal Time) 的時間偏移量,例如"+08:00"
、"-05:00"
。 -
ZoneOffset
可以用來建立OffsetDateTime
或OffsetTime
物件。 -
可以使用
ZoneOffset.of(String offsetId)
方法來創建ZoneOffset
物件。
-
-
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 |
-
The above information is the available time zone. ↩︎
沒有留言:
張貼留言