先记住这个答案
new Intl.DateTimeFormat(locale, { timeZone }) 里的 timeZone 传 IANA 时区名,比如 Asia/Shanghai 和 America/New_York,这样 format(date) 就会按那个地区的规则把时间戳换成当地的年月日时分,夏令时也会自动处理。Date 对象本身不会被修改,服务器也不用切换系统时区。locale 和 timeZone 是两件事:locale 决定显示成中文还是英文、字段怎么排,timeZone 决定显示哪个地区的钟点。
timeZone用Asia/Shanghai这种 IANA 名称locale管语言格式,timeZone管显示哪个地区的钟点- 自定义格式用
formatToParts()按片段拼 - 列表里复用同一个 formatter,别每行 new
同一时刻显示成三个地区的时间
做跨境业务或者航班、会议类页面,经常要把同一个时间同时显示成几个地区的当地时间。每个地区建一个 formatter 就行。
timeZoneJavaScriptconst instant = new Date('2026-09-10T00:00:00Z')
const options = {
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', hourCycle: 'h23'
}
for (const timeZone of ['Asia/Shanghai', 'America/New_York', 'Asia/Tokyo']) {
const fmt = new Intl.DateTimeFormat('zh-CN', { ...options, timeZone })
console.log(timeZone, fmt.format(instant))
}查看输出与解释
Asia/Shanghai 2026/09/10 08:00
America/New_York 2026/09/09 20:00
Asia/Tokyo 2026/09/10 09:00三行都用中文格式,因为 locale 都是 zh-CN;钟点各不相同,因为 timeZone 不同。纽约这时候用夏令时,是 UTC-4,所以还是 9 号晚上 8 点。如果是 1 月份,formatter 会自动按 UTC-5 算,不用你操心。
hourCycle: 'h23' 让小时按 0~23 显示。不写的话,有些语言默认 12 小时制,会带上午下午。
想要固定格式,用 formatToParts()
format() 的输出格式由语言决定,你没法精确控制。想要 2026-09-10 这种格式,或者要把日期和时间分开渲染,就用 formatToParts() 拿到各个片段自己拼。
YYYY-MM-DDJavaScriptconst fmt = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit'
})
const parts = fmt.formatToParts(new Date('2026-09-10T00:00:00Z'))
const get = (type) => parts.find((p) => p.type === type).value
console.log(`${get('year')}-${get('month')}-${get('day')}`)
try {
new Intl.DateTimeFormat('zh-CN', { timeZone: 'Beijing' }) // 不是合法的 IANA 名称
} catch (err) {
console.log(err.name)
}查看输出与解释
2026-09-10
RangeErrorformatToParts() 返回的是 { type, value } 数组,type 有 year、month、day、literal 等。按 type 取值,不依赖语言把年放前面还是后面,比用正则去切 format() 的结果稳得多。
时区名写错会在构造时直接抛 RangeError,比如 Beijing 和 GMT+8 都不合法。更坑的是 CST,它不报错,而是被当成美国中部时间 America/Chicago,格式化出来差了十几个小时。中国大陆统一用 Asia/Shanghai。
- formatter 要缓存复用
- 构造
Intl.DateTimeFormat要解析语言和时区数据,成本不低。一个一千行的订单列表,每行 new 一个 formatter,性能差距很明显。按locale、timeZone和选项组合建好实例放在模块顶层或者useMemo里,所有行共用。
容易答错的地方
- 以为传了
zh-CN就是北京时间 zh-CN只决定显示成中文、用中国大陆的格式习惯。时区是另一个参数,不传就用运行环境的时区。部署在海外服务器上的 Node 服务,zh-CN格式化出来可能是 UTC 时间。- 用固定偏移代替时区名
Etc/GMT-8或者较新引擎支持的+08:00都能表示东八区,但它们没有夏令时规则。对中国没影响,对纽约、伦敦这类地区,用固定偏移就会有半年差一小时。有夏令时的地区一定要用America/New_York这样的地区名。
面试官还会怎么问?
怎么在时间后面显示「GMT+8」这类时区标记?
加 timeZoneName 选项。short 会显示 GMT+8,纽约夏天显示 GMT-4;换成 long 则显示完整名称,比如「中国标准时间」。具体文本由语言决定,要单独渲染就从 formatToParts() 里取 type 为 timeZoneName 的片段。
怎么知道 formatter 最终用的是哪个时区?
调 fmt.resolvedOptions().timeZone。不传 timeZone 时,它返回运行环境的默认时区,这也是在前端拿用户时区名的常用写法。
Next.js 这类 SSR 页面怎么避免时间显示不一致?
服务端和浏览器的默认时区通常不同,都用默认值格式化,文本就对不上,会触发 hydration 报错。要么把用户时区存进 cookie 或用户设置,服务端格式化时显式传进去;要么时间这一块只在客户端渲染。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。