← 全部文章

無需函式庫的 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 時區

時區格式化常見問題

Two daylight-saving transitions a year break the assumption that "wall-clock time + timezone = a unique moment". Both are encoded in the IANA timezone database (still often called the Olson database) — the authoritative source of timezone rules used by every modern OS, browser, and language runtime.

  • DST gap: 02:00 to 02:59 local time does not exist on the spring-forward day
  • DST overlap: 01:00 to 01:59 local time occurs twice on the fall-back day
  • Ambiguous local time: in the overlap window you must pick "earlier" or "later" explicitly
  • Python uses a fold flag; JS Temporal uses a disambiguation option; java.time defaults to the earlier offset
  • Olson database / IANA tzdata / zoneinfo are three names for the same dataset, updated several times a year
  • Each IANA name (America/Los_Angeles, Europe/Berlin) maps to a list of (transition, offset, abbreviation) tuples
  • Stale tzdata on a device produces silently-wrong local times after any DST or zone change
  • Server best practice: do all date arithmetic in UTC, convert to local only at display

FAQ

Can JavaScript format a date in another timezone without a library?
Yes. Use Intl.DateTimeFormat or toLocaleString with a timeZone option, such as America/New_York or Asia/Tokyo.
Does Intl.DateTimeFormat handle daylight saving time?
Yes. When you use an IANA timezone identifier, the runtime applies the correct offset for that timezone and date.
Should I store timezone offsets or timezone names?
Store UTC for event instants. If you need the user's local context, store an IANA timezone name such as America/New_York, not only a numeric offset.
How do I get the user's timezone in JavaScript?
Use Intl.DateTimeFormat().resolvedOptions().timeZone, which returns an IANA name like America/New_York. Store that name, not a numeric offset, when you need to reconstruct the user's local time later.
What is the DST gap (spring-forward)?
The DST gap is the wall-clock hour that is skipped when daylight saving time begins. In most US zones, when the clock would tick from 02:00 to 02:59 it instead jumps to 03:00. Local times in that window do not exist on the transition day. Unix timestamps themselves never skip; only the wall-clock interpretation does.
What is the DST overlap (fall-back)?
The DST overlap is the wall-clock hour that occurs twice when daylight saving time ends. In most US zones, the clock ticks from 01:59 back to 01:00, so every minute from 01:00 to 01:59 happens twice. A wall-clock time in that window maps to two Unix timestamps; converters and schedulers must specify "earlier" or "later".