你以为前端处理日期时间只能靠 dayjs?今天 JavaScript 终于用 Temporal 把这件事彻底原生化了

写了八年前端,每次项目初始化都要装一个 dayjs 或者 date-fns——不是我们喜欢多一个依赖,是 JavaScript 原生的 Date 实在没法用。2026 年 3 月,这件事终于有了语言层面的答案。

Temporal API 在 2026 年 3 月进入 TC39 Stage 4,成为 ES2026 标准的一部分,目前已经原生内置在 Chrome 144+、Firefox 139+、Edge 144+ 和 Node.js 26+。Safari 暂时还没在稳定版里推出,但官方 polyfill @js-temporal/polyfill 已经能覆盖所有场景,删掉 polyfill 改用原生只需要改一行 import。

Date 有什么问题

Date 对象的问题不是 bug,是设计缺陷。最被人吐槽的几个:

  • 月份从 0 开始:January = 0,December = 11。拿到 getMonth() 返回 5,其实是指六月。
  • Date 是可变的:date.setMonth(date.getMonth() + 1) 会直接修改原对象,多线程或者异步回调里特别容易踩坑。
  • 没有时区类型:Date 只知道”本地时间”和 UTC,ZonedDateTime 这种概念根本不存在,必须自己处理 DST(夏令时)。
  • 解析结果不可靠:new Date(“2026-09-14”) 在不同浏览器里可能解析成 UTC 0 点,也可能解析成本地时间。

date-fns 和 dayjs 之所以流行,就是因为它们把 Date 的这些坑用更合理的 API 包装了一层。moment.js 更早,但它本身 231KB 的体积和可变 API 早就被社区嫌弃了。

Temporal 怎么解决

Temporal 的核心思路是把”日期时间”这个概念拆成多个精确的类型,而不是用一个模糊的 Date 对象对付所有场景。

用哪个类型由场景决定:

  • Temporal.PlainDate — 只有日期,没有时间、没有时区。适合生日、截止日期。
  • Temporal.PlainDateTime — 本地日期时间,没有时区。适合”这周二中午开会”这种场景。
  • Temporal.ZonedDateTime — 带时区的精确时刻,自动处理 DST。适合航班、会议室预约。
  • Temporal.Instant — UTC 时间戳,适合存数据库、跨时区对比。
  • Temporal.Duration — 一段时间,适合做时间差计算。

“`javascript
// Date 时代:月份从 0 开始,setMonth 直接修改原对象
const d = new Date(“2026-09-14”);
d.getMonth(); // 8,不是 9
d.setMonth(d.getMonth() + 1);
d.getMonth(); // 9,原对象被改了

// Temporal 时代:Immutable + 月份从 1 开始
const d = Temporal.PlainDate.from(“2026-09-14”);
d.month; // 9,符合直觉
d.add({ months: 1 }); // 返回新的 PlainDate,原对象不变
“`

DST 处理才是真正杀手锏

很多人以为 Date 处理夏令时很难,其实更难的是”加了 24 小时不等于加了一天”这件事在某些 DST 切换时刻是真的。

“`javascript
// 在纽约,3月8日 凌晨2点时钟会拨快到3点
const before = Temporal.ZonedDateTime.from(
“2026-03-08T02:00:00[America/New_York]”
);
const after = before.add({ days: 1 });

// Temporal 努力保持”同一时钟时间”,所以结果是 03-09 09:00
// 而不是 03-09 08:00(加了24小时但实际只过了23小时)
after.toString();
// “2026-03-09T09:00:00-04:00[America/New_York]”
“`

Date 想正确处理这个,必须自己写一堆边界判断。Temporal 直接内置了DST aware 算法。

性能怎么样

有个基准测试(Firefox 139,原生 Temporal)显示了格式化场景下的数字:

操作数/秒
dayjs 3,512,727
moment.js 1,132,355
luxon 976,759
Native Temporal 42,613
date-fns 42,598

Temporal 在这个测试里比 dayjs 慢约 80 倍——主要原因是 Temporal 用了完整的 Intl API 做本地化格式化,而 dayjs 用的是自己写的轻量格式化器。

但这个比较本身有问题。 dayjs 在格式化上快,不代表它比 Temporal 更好。Temporal 的价值在于正确性,不在于格式化速度。如果你的场景需要正确处理时区和 DST,用 Temporal;如果你只是需要”把日期显示成 2026-09-14″,两者都能用,差别不大。

而且这个测试是在 Firefox 139 上跑的,Chrome 144+ 和 Edge 144+ 的 V8 对 Intl API 的优化会更好一些。

怎么开始

如果你现在就想用,最简单的方式是装官方 polyfill:

“`bash
npm install @js-temporal/polyfill
“`

然后在代码里:

“`javascript
import { Temporal } from “@js-temporal/polyfill”;

const today = Temporal.Now.plainDateISO();
const nextWeek = today.add({ weeks: 1 });
console.log(nextWeek.toString()); // 2026-09-21
“`

当 Safari 稳定版推出 Temporal 之后,删掉 import,全局 Temporal 对象就在那里等你,一行不用改。

目前各运行环境状态:

  • Chrome 144+ ✅ 稳定
  • Firefox 139+ ✅ 稳定
  • Edge 144+ ✅ 稳定
  • Node.js 26+ ✅ 稳定
  • Deno 2.7+ ✅ 稳定
  • Safari — 技术预览版有,正式版还没有
  • Bun — 开发中(基于 JavaScriptCore)

下一步

如果你现在在项目里用的是 date-fns 或者 dayjs,可以逐步把新写的日期逻辑迁移到 Temporal,老代码不用动。具体可以先从”新增的日期比较和计算”开始,这些地方用 Temporal 的 immutable 类型收益最明显,也最容易验证正确性。

如果你的日期处理逻辑复杂、跨时区、涉及 DST——比如排班系统、航班预订、跨国会议——Temporal 能让你删掉很大一块手动时区处理的代码。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 12 阅读