再也不想用 Date 拼时区字符串了,JS Temporal 按 MDN 现在支持到什么程度?

业务里一旦出现「只要日历上的那天、别管用户人在哪个时区」,或「每天八点闹钟别跟着夏令时漂移」,Date 那套可变对象 + 本地/UTC 两顶帽子就容易拧成一团。这里说的是 JavaScript 语言里的 Temporal 日期时间 API,不是同名的 Temporal.io 工作流引擎——搜文档时别点错仓库。

MDN 把它定位成 Date 的完整替代

MDN:Temporal 开篇就写:在时区、日历、算术、格式化等场景下管理日期时间,设计目标是全面替代 Date。Temporal 本身不是构造函数,不能 new Temporal(),用法更接近 Math:下面挂一串类型。

常见切分是:

  • Temporal.Instant:历史上唯一瞬间(纪元纳秒),不带日历字段。
  • Temporal.ZonedDateTime:瞬间 + 时区 + 日历。
  • Temporal.PlainDate / PlainTime / PlainDateTime:墙钟/日历意义,不绑定时区(生日、营业时段这类)。
  • Temporal.Duration:两段时间的差。
  • Temporal.Now:取「现在」的多种形态。

对象默认不可变。改字段走 with(),加减走 add / subtract。序列化格式在 MDN 里指向 RFC 9557(基于 ISO 8601),比旧的 Date 字符串解析坑要规矩一些。

提案已经 Stage 4,但浏览器仍不是 Baseline

tc39/proposal-temporal README 写明提案处于 Stage 4,并在往 ECMA-262 / ECMA-402 合入。实现进度(提案页列举)包括:Firefox 139 已随正式版提供、Chrome 144 已提供、Node.js 26 已提供。Safari / JSC 仍在跟进列表里,不能写成「全绿默认可用」。

MDN 页首同样是 Limited availability / not Baseline。BCD 里:Chrome / Edge 144+、Firefox 139+ 有记录。Safari 多为 preview,iOS Safari 仍可能是 false。上线前用特性检测,例如:

if (typeof Temporal === "undefined") {
  // 走 polyfill 或继续用 Date / 现有库
}

还要不要 polyfill?

要。只要产品还覆盖「未实现 Temporal 的浏览器或旧 Node」,就需要垫一层。提案仓库 Polyfills 一节列出了社区实现(含 champions 维护的 polyfill 与其它稳定发行)。不要用仓库里那份仅供文档 playground / 跑测试的非生产 polyfill。MDN 的 See also 也链到同类包。选型看提案页表格和你自己的包体积、测试矩阵,这里不点名评测。

Node 侧若已升到文档写明支持的主版本,可先 node -e "console.log(typeof Temporal)" 看运行时带没带。浏览器侧仍看兼容表 + 真机。

选型时先问「要不要时区」

动手前先对场景:

  • 只要「2026-11-09」这种日历日 → PlainDate。
  • 只要「08:00」这种墙钟 → PlainTime。
  • 要落到唯一瞬间、跨时区开会 → Instant 或 ZonedDateTime。
  • 两个时间点差多久 → Duration / since / until。

把「无时区的日历日」硬塞进 Date,再 toISOString() 一遍,夏令时边界上最容易 silently 错一天。Temporal 把类型拆开,就是为了少踩这类坑。日历默认偏 ISO。非公历场景才去碰 calendarId,MDN 对 era / monthCode 有一长串「别想当然」的提醒。

别和 Temporal.io 搜串

包管理器、招聘 JD、架构图里出现的 Temporal,有时指工作流编排服务。语言 API 的命名空间是全局 Temporal,类型名带 Plain* / ZonedDateTime。工作流产品则是另一套 SDK 与服务端概念。写标题和文档索引时加「JS / ECMAScript / 日期」之类限定,能少很多点错链接的时间。

可读性上,团队约定「对外 JSON 仍用 ISO 字符串、对内用 Plain* / Zoned*」往往比到处传 Date 实例更少副作用。测试里固定用 Temporal.PlainDate.from("2026-03-08") 这类字面量,也比 new Date("...") 少踩实现相关解析差异。

和旧代码并存

短期很难一把删光 Date。比较稳的接法是:新模块用 Temporal 类型表达业务含义,边界上再和纪元毫秒或 ISO 字符串互换。对用户展示继续交给 toLocaleString / Intl。库作者若公开发布 API,文档里写清「需要原生 Temporal 或哪条 polyfill」,比默默假设全局已有 Temporal 更少坑。类型定义若走 DefinitelyTyped 或自带 types,也要和运行时 polyfill 版本对齐,避免「类型说有、运行时报没有」。

你们线上最低浏览器 / Node 版本卡在哪?准备原生检测失败就垫 polyfill,还是等一波再迁?