無需函式庫的 JavaScript 時區正確日期格式化
JavaScript 內建的 Intl.DateTimeFormat 無需 moment.js 或 date-fns 即可處理 IANA 時區格式化。學習顯式 timeZone 選項、對日光節約時間安全的顯示、formatToParts、掛鐘轉換的限制,以及何時 Temporal 或時區函式庫仍然有用。
為什麼 Date 沒有時區屬性
JavaScript 的 Date 始終是一個 UTC 毫秒計數。物件內部不儲存時區。當你不帶選項呼叫 .toString() 或 .toLocaleString() 時,JavaScript 使用來自作業系統的執行階段本地時區。即使底層時間戳完全相同,同一段程式碼在紐約的伺服器和東京的筆電上也會產生不同輸出。
用 Intl.DateTimeFormat 格式化
Intl.DateTimeFormat 是用於依地區設定和時區進行格式化的內建 API。它支援 IANA 時區識別碼,自動處理日光節約時間切換,並在現代瀏覽器和 Node.js 中可用。關鍵是顯式傳入 timeZone 選項,而不是依賴執行階段預設值。
- new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York', dateStyle: 'full', timeStyle: 'long' }).format(date)
- date.toLocaleString('en-GB', { timeZone: 'Europe/London', hour12: false })
- date.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' })
- new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', hour12: false }).format(date)
用 formatToParts 擷取各個部分
使用 formatToParts() 把日期的各個組成部分取為 {type, value} 物件,以建構自訂日期字串。這比拆分本地化的日期字串更好,因為標點、順序和文字系統因地區設定而異。
- const parts = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Chicago', year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(date)
- parts.find(p => p.type === 'year').value → '2023'
- parts.find(p => p.type === 'month').value → '11'
- Object.fromEntries(parts.map(p => [p.type, p.value])) → { year, month, day, hour, minute, second }
把掛鐘時間轉換為 UTC(困難的方向)
用經典 Date API 從給定時區的掛鐘時間走向 UTC 更困難。把某個瞬間格式化為 America/New_York 很容易;但建構 America/New_York 中 2026-03-08 02:30 所表示的瞬間則不然,因為該本地時間在日光節約時間切換期間可能被跳過或重複。
- Temporal 在 2026 年達到 Stage 4,但瀏覽器原生支援仍不普及
- 若要在今天所有瀏覽器上用於生產,Temporal polyfill 或 date-fns-tz 的 toDate() 仍是實用選擇
- 避免手動做 UTC 偏移算術 —— 日光節約時間切換在不同年份發生於不同的本地時刻
- 本站的 zonedToEpochMs() 使用一次迭代的偏移校正 —— 見 src/timeUtils.ts
選擇正確的時區識別碼
使用 America/New_York、Europe/London、Asia/Tokyo 或 UTC 這類 IANA 時區名稱。面向使用者的時間應避免使用 UTC-5 這類固定偏移,因為偏移會隨日光節約時間變化。America/New_York 在 1 月可能是 UTC-5,在 7 月是 UTC-4;IANA 名稱讓平台為所選日期套用正確的歷史規則。
- 好:America/Los_Angeles —— 包含歷史和未來的日光節約時間規則
- 好:Europe/Berlin —— 自動處理中歐夏令時間
- 好:Asia/Shanghai —— 無日光節約時間的穩定 UTC+8 顯示
- 日誌、API 儲存、資料庫時間戳和跨區域比較使用 UTC
- 產品介面的最終顯示使用使用者的 IANA 時區
時區格式化常見問題
- JavaScript 能在不用函式庫的情況下把日期格式化為另一個時區嗎?
- 可以。使用 Intl.DateTimeFormat 或 toLocaleString,並帶上 timeZone 選項,如 America/New_York 或 Asia/Tokyo。
- Intl.DateTimeFormat 會處理日光節約時間嗎?
- 會。當你使用 IANA 時區識別碼時,執行階段會為該時區和日期套用正確的偏移。
- 我應該儲存時區偏移還是時區名稱?
- 為事件瞬間儲存 UTC。如果你需要使用者的本地上下文,請儲存像 America/New_York 這樣的 IANA 時區名稱,而不僅僅是數值偏移。
- 如何在 JavaScript 中取得使用者的時區?
- 使用 Intl.DateTimeFormat().resolvedOptions().timeZone,它回傳像 America/New_York 這樣的 IANA 名稱。當你以後需要重建使用者的本地時間時,請儲存該名稱,而非數值偏移。