為什麼 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".