Temporal API

JavaScript Temporal API:Date 的现代替代

JavaScript Date 几十年来一直以众所周知的方式存在缺陷。TC39 Temporal——自 2025 年初起为 Stage 4,已在现代浏览器和 Node.js 中发布——干净地分离了瞬间、挂钟日期和带时区的日期时间。本指南讲解新的类型、它们如何对应 Unix 时间戳,以及如何迁移。

一眼看懂 Temporal vs Date

JavaScript 内置的 Date 几十年来一直是已知的 bug 来源:它是可变的,只理解本地时区和 UTC,月份从 0 开始索引,解析也只是松散规定。Temporal 是走标准轨道的替代——Stage 4,并且是 ECMAScript 2026 的一部分——用专门设计的不可变类型修复了上述每个问题。下表是高层对比;本指南的其余部分介绍这些类型以及它们如何对应 Unix 时间戳。

方面Date(旧)Temporal
可变性可变不可变
精度毫秒纳秒
时区仅本地 + UTC任意 IANA 时区(ZonedDateTime)
月份索引从 0 开始(0 = 一月)从 1 开始(1 = 一月)
解析宽松,由实现定义严格的 ISO 8601 / RFC 9557
瞬间 vs 挂钟混在一个类型里分开的类型

为什么有 Temporal

JavaScript Date 混淆了几个不同的概念:一个精确瞬间、一个挂钟时间,以及一个带时区的日期时间。Date 还有众所周知的解析怪癖、单一时区(用户的)和毫秒精度。Temporal 把底层概念暴露为分开的类型,并支持纳秒精度、任意 IANA 时区和显式历法体系。

  • 自 2025 年初起为 Stage 4;最先在 Firefox 139 中默认发布
  • 现在可在 Chrome 144+、Firefox 139+ 和 Node.js 26+ 中使用
  • 垫片(@js-temporal/polyfill)可在任何现代环境中工作
  • 设计为与 Date 共存——双向都存在互操作方法

Temporal 的核心类型

一小组类型干净地覆盖了日期时间问题。每个都有一个规范的字符串形式(ISO 8601,带时区类型由 RFC 9557 扩展):

类型表示示例字符串
Temporal.Instant一个精确瞬间,如 Unix 时间戳(ns 精度)2026-01-01T00:00:00Z
Temporal.ZonedDateTime瞬间 + IANA 时区 + 历法2026-01-01T09:00:00+09:00[Asia/Tokyo]
Temporal.PlainDateTime日期 + 时间,无时区2026-01-01T09:00:00
Temporal.PlainDate一个历法日期(如生日)2026-01-01
Temporal.PlainTime一个挂钟时间09:00:00
Temporal.Duration一段时长,单位感知P3DT4H10M

使用 Unix 时间戳

Temporal.Instant 就是 Unix 时间戳类型。发布的 API 只暴露毫秒和纳秒的 epoch 转换——更早的 fromEpochSeconds 和 fromEpochMicroseconds 方法在 Stage 4 之前已被移除——所以把 Unix 秒乘以 1000 来转换。存储和传输用 Instant,当你需要某个时区的挂钟视图时用 ZonedDateTime。

  • Temporal.Now.instant() —— 当前的精确时刻
  • Temporal.Instant.fromEpochMilliseconds(Date.now()) —— 从 JS 毫秒时间戳
  • Temporal.Instant.fromEpochMilliseconds(1700000000 * 1000) —— 从 Unix 秒(× 1000)
  • Temporal.Instant.fromEpochNanoseconds(1700000000000000000n) —— 从纳秒(BigInt)
  • instant.epochMilliseconds 和 instant.epochNanoseconds —— 读回 epoch 值
  • instant.toZonedDateTimeISO("America/New_York") —— 某个时区的挂钟视图

正确处理时区与 DST

ZonedDateTime 携带一个 IANA 时区,所以 DST 切换、空缺时间和重叠时间都被显式处理。消歧选项让你为含糊的本地时间选择一个策略,而规范的字符串格式是 RFC 9557(一个带方括号时区的 ISO 8601 时间戳)。

  • zdt.timeZoneId —— IANA 名称(如 America/Los_Angeles)
  • zdt.add({ hours: 1 }) —— 以挂钟方式进行的 DST 感知算术
  • zdt.until(zdt2) —— 两个带时区日期时间之间的距离
  • 消歧选项:"earlier"、"later"、"compatible"(默认 —— 空缺取较晚,重叠取较早)、"reject"
  • 往返:instant ↔ 带时区日期时间是无损的

从 Date 迁移的模式

最简单的迁移策略是先在边界处(解析输入、格式化输出)引入 Temporal,再逐步把内部逻辑迁过去。

  • Date → Instant:Temporal.Instant.fromEpochMilliseconds(date.getTime())
  • Instant → Date:new Date(instant.epochMilliseconds)
  • 对新代码,处处优先 Temporal,仅在 API 边缘转换为 Date
  • date-fns 和 Luxon 都有 Temporal 感知的模式/适配器
  • JSON:Temporal.Instant.prototype.toJSON 返回一个 ISO 8601 字符串

浏览器与运行时支持

Temporal 于 2025 年发布,原生支持如今已广泛,但尚未普及。在较旧的浏览器和 Safari 稳定版仍在使用期间,垫片是最稳妥的路径。

  • Firefox 139+:最先发布,默认启用(2025 年 5 月)
  • Chrome 144+:默认启用(2026 年 1 月)
  • Node.js 26+:默认启用(2026 年 5 月)
  • Safari:在 Technology Preview 中可用,尚未进入稳定版
  • 垫片:@js-temporal/polyfill 或 temporal-polyfill —— 相同的 API,任何现代环境

相关参考

FAQ

Temporal 是 Date 的替代吗?
是——Temporal 旨在在新代码中完全取代 Date。Date 为向后兼容仍保留在语言中,二者通过 epoch 毫秒互操作。
如何把 Unix 时间戳转换为 Temporal?
使用 Temporal.Instant.fromEpochMilliseconds()。对于 Unix 秒,乘以 1000:Temporal.Instant.fromEpochMilliseconds(seconds * 1000)。发布的 API 只有 fromEpochMilliseconds 和 fromEpochNanoseconds——较旧的 fromEpochSeconds 方法在 Temporal 达到 Stage 4 之前已被移除。
Temporal 支持纳秒吗?
是。Temporal.Instant 内部具有纳秒精度,并以 BigInt 暴露 epochNanoseconds。
今天可以在 Node.js 中用 Temporal 吗?
在 Node.js 26+ 中,Temporal 默认启用。在更旧的 Node 版本中,安装 @js-temporal/polyfill——API 表面是相同的。
哪些浏览器支持 Temporal?
Firefox 139+(2025 年 5 月)、Chrome 144+(2026 年 1 月)和 Node.js 26+(2026 年 5 月)默认发布 Temporal。Safari 在 Technology Preview 中具备。对更旧的环境,使用 @js-temporal/polyfill。
Temporal 如何处理 DST 空缺?
ZonedDateTime 的构造函数和算术接受一个消歧选项。"compatible"(默认)为空缺选择较晚的瞬间、为重叠选择较早的瞬间;"reject" 在本地时间含糊时抛出错误。