无需库的 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 名称。当你以后需要重建用户的本地时间时,请存储该名称,而非数值偏移。