JavaScript Temporal API:Date 的現代替代
JavaScript Date 幾十年來一直以眾所周知的方式存在缺陷。TC39 Temporal——自 2025 年初起為 Stage 4,已在現代瀏覽器和 Node.js 中發布——乾淨地分離了瞬間、掛鐘日期和帶時區的日期時間。本指南講解新的類型、它們如何對應 Unix 時間戳,以及如何遷移。
一眼看懂 Temporal vs Date
JavaScript 內建的 Date 幾十年來一直是已知的 bug 來源:它是可變的,只理解本地時區和 UTC,月份從 0 開始索引,剖析也只是寬鬆規定。Temporal 是走標準軌道的替代——Stage 4,並且是 ECMAScript 2026 的一部分——用專門設計的不可變類型修復了上述每個問題。下表是高層對比;本指南的其餘部分介紹這些類型以及它們如何對應 Unix 時間戳。
| 方面 | Date(舊) | Temporal |
|---|---|---|
| 可變性 | 可變 | 不可變 |
| 精度 | 毫秒 | 奈秒 |
| 時區 | 僅本地 + UTC | 任意 IANA 時區(ZonedDateTime) |
| 月份索引 | 從 0 開始(0 = 一月) | 從 1 開始(1 = 一月) |
| 剖析 | 寬鬆,由實作定義 | 嚴格的 ISO 8601 / RFC 9557 |
| 瞬間 vs 掛鐘 | 混在一個類型裡 | 分開的類型 |
為什麼有 Temporal
JavaScript Date 混淆了幾個不同的概念:一個精確瞬間、一個掛鐘時間,以及一個帶時區的日期時間。Date 還有眾所周知的剖析怪癖、單一時區(使用者的)和毫秒精度。Temporal 把底層概念公開為分開的類型,並支援奈秒精度、任意 IANA 時區和明確曆法體系。
- 自 2025 年初起為 Stage 4;最先在 Firefox 139 中預設發布
- 現在可在 Chrome 144+、Firefox 139+ 和 Node.js 26+ 中使用
- polyfill(@js-temporal/polyfill)可在任何現代環境中運作
- 設計為與 Date 共存——雙向都存在互通方法
Temporal 的核心類型
一小組類型乾淨地涵蓋了日期時間問題。每個都有一個規範的字串形式(ISO 8601,帶時區類型由 RFC 9557 擴充):
| 類型 | 表示 | 範例字串 |
|---|---|---|
| Temporal.Instant | 一個精確瞬間,如 Unix 時間戳(ns 精度) | 2026-01-01T00:00:00Z |
| Temporal.ZonedDateTime | 瞬間 + IANA 時區 + 曆法 | 2026-01-01T09:00:00+09:00[Asia/Tokyo] |
| Temporal.PlainDateTime | 日期 + 時間,無時區 | 2026-01-01T09:00:00 |
| Temporal.PlainDate | 一個曆法日期(如生日) | 2026-01-01 |
| Temporal.PlainTime | 一個掛鐘時間 | 09:00:00 |
| Temporal.Duration | 一段時長,單位感知 | P3DT4H10M |
使用 Unix 時間戳
Temporal.Instant 就是 Unix 時間戳類型。發布的 API 只公開毫秒和奈秒的 epoch 轉換——更早的 fromEpochSeconds 和 fromEpochMicroseconds 方法在 Stage 4 之前已被移除——所以把 Unix 秒乘以 1000 來轉換。儲存和傳輸用 Instant,當你需要某個時區的掛鐘檢視時用 ZonedDateTime。
- Temporal.Now.instant() —— 目前的精確時刻
- Temporal.Instant.fromEpochMilliseconds(Date.now()) —— 從 JS 毫秒時間戳
- Temporal.Instant.fromEpochMilliseconds(1700000000 * 1000) —— 從 Unix 秒(× 1000)
- Temporal.Instant.fromEpochNanoseconds(1700000000000000000n) —— 從奈秒(BigInt)
- instant.epochMilliseconds 和 instant.epochNanoseconds —— 讀回 epoch 值
- instant.toZonedDateTimeISO("America/New_York") —— 某個時區的掛鐘檢視
正確處理時區與 DST
ZonedDateTime 攜帶一個 IANA 時區,所以 DST 切換、空缺時間和重疊時間都被明確處理。消歧選項讓你為含糊的本地時間選擇一個策略,而規範的字串格式是 RFC 9557(一個帶方括號時區的 ISO 8601 時間戳)。
- zdt.timeZoneId —— IANA 名稱(如 America/Los_Angeles)
- zdt.add({ hours: 1 }) —— 以掛鐘方式進行的 DST 感知算術
- zdt.until(zdt2) —— 兩個帶時區日期時間之間的距離
- 消歧選項:"earlier"、"later"、"compatible"(預設 —— 空缺取較晚,重疊取較早)、"reject"
- 往返:instant ↔ 帶時區日期時間是無損的
從 Date 遷移的模式
最簡單的遷移策略是先在邊界處(剖析輸入、格式化輸出)引入 Temporal,再逐步把內部邏輯遷過去。
- Date → Instant:Temporal.Instant.fromEpochMilliseconds(date.getTime())
- Instant → Date:new Date(instant.epochMilliseconds)
- 對新程式碼,處處優先 Temporal,僅在 API 邊緣轉換為 Date
- date-fns 和 Luxon 都有 Temporal 感知的模式/配接器
- JSON:Temporal.Instant.prototype.toJSON 回傳一個 ISO 8601 字串
瀏覽器與執行階段支援
Temporal 於 2025 年發布,原生支援如今已廣泛,但尚未普及。在較舊的瀏覽器和 Safari 穩定版仍在使用期間,polyfill 是最穩妥的路徑。
- Firefox 139+:最先發布,預設啟用(2025 年 5 月)
- Chrome 144+:預設啟用(2026 年 1 月)
- Node.js 26+:預設啟用(2026 年 5 月)
- Safari:在 Technology Preview 中可用,尚未進入穩定版
- polyfill:@js-temporal/polyfill 或 temporal-polyfill —— 相同的 API,任何現代環境
相關參考
FAQ
- Temporal 是 Date 的替代嗎?
- 是——Temporal 旨在在新程式碼中完全取代 Date。Date 為向後相容仍保留在語言中,二者透過 epoch 毫秒互通。
- 如何把 Unix 時間戳轉換為 Temporal?
- 使用 Temporal.Instant.fromEpochMilliseconds()。對於 Unix 秒,乘以 1000:Temporal.Instant.fromEpochMilliseconds(seconds * 1000)。發布的 API 只有 fromEpochMilliseconds 和 fromEpochNanoseconds——較舊的 fromEpochSeconds 方法在 Temporal 達到 Stage 4 之前已被移除。
- Temporal 支援奈秒嗎?
- 是。Temporal.Instant 內部具有奈秒精度,並以 BigInt 公開 epochNanoseconds。
- 今天可以在 Node.js 中用 Temporal 嗎?
- 在 Node.js 26+ 中,Temporal 預設啟用。在更舊的 Node 版本中,安裝 @js-temporal/polyfill——API 表面是相同的。
- 哪些瀏覽器支援 Temporal?
- Firefox 139+(2025 年 5 月)、Chrome 144+(2026 年 1 月)和 Node.js 26+(2026 年 5 月)預設發布 Temporal。Safari 在 Technology Preview 中具備。對更舊的環境,使用 @js-temporal/polyfill。
- Temporal 如何處理 DST 空缺?
- ZonedDateTime 的建構函式和算術接受一個消歧選項。"compatible"(預設)為空缺選擇較晚的瞬間、為重疊選擇較早的瞬間;"reject" 在本地時間含糊時拋出錯誤。