Temporal 是 Date 的语义级替代而非升级,因 Date 仅存毫秒数且跨时区逻辑不可靠;应使用 PlainDateTime 处理无时区日历时间、ZonedDateTime 表达真实事件时刻、Instant 进行底层精确计算。

Temporal 不是 Date 的升级补丁,而是从语义层彻底重写的替代方案——只要还在用 Date 实例做跨时区逻辑,就注定踩坑。
为什么 new Date() 在夏令时切换日一定会算错
因为 Date 内部只存毫秒数,构造时把字符串(如 '2024-03-10T02:30')强行映射到本地时区,而北美东部时间这天 2:00–2:59 根本不存在。Chrome 会静默跳到 3:30,Safari 可能返回 2:30 并套用错误偏移,且 Date.prototype.toString() 还会用当前系统规则重新解释这个已损坏的时间点。
- 所有基于字符串的
new Date(string)都不可靠,尤其含时区或本地格式 -
Date.prototype.getTimezoneOffset()返回的是“此刻”的偏移,不是目标时刻的真实偏移 - 没有 API 能回答“2024-11-03 01:30 在 America/Chicago 是标准时间还是夏令时间”
用 Temporal.PlainDateTime 处理“无时区时间”
门店营业时间、生日、会议预约开始时间这类值,本质是日历+钟表组合,和 UTC 或本地时区无关。Date 强行绑定本地时区,导致部署在东京的服务器解析 '09:00' 得到的时间戳和纽约服务器完全不同。
- 用
Temporal.PlainDateTime.from('2024-06-15T09:00')明确表示“纯日历时间”,不参与任何时区计算 - 后续要转成真实时刻时,必须显式传入用户选择的时区,例如
.withTimeZone('Europe/Berlin') - 避免用
PlainDateTime做加减运算——它没有时区上下文,.add({days: 1})只是日历推进,不反映真实流逝
用 Temporal.ZonedDateTime 处理“真实事件时刻”
航班起飞、订单创建、定时任务触发——这些是物理世界中发生的具体瞬间,必须绑定 IANA 时区名(如 'America/New_York'),而非静态偏移(如 '+05:00')。
-
Temporal.ZonedDateTime.from('2024-11-03T01:30-05:00[America/Chicago]')会查 tzdata 确认该时刻实际对应 -06:00 还是 -05:00 - 遇到夏令时重叠(如 1:30 出现两次)或间隙(如 2:30 不存在),默认用
disambiguation: 'compatible'(取后一个),可显式设为'earlier'或'later' - 跨时区比较必须先统一转成
Temporal.Instant,再用.getEpochNanoseconds()比数值;.equals()会因时区不同返回 false 却指向同一时刻
用 Temporal.Instant 做底层时间轴计算
所有精确计时、定时器、日志时间戳、数据库存储都应该基于 Temporal.Instant——它代表 Unix 纪元起的纳秒数,完全无视闰秒、夏令时、日历,是唯一可安全做加减和比较的类型。
-
Temporal.Now.instant()返回当前精确瞬时点,比Date.now()多 3 位小数精度 - 业务逻辑中做“加 24 小时”应写
instant.add({hours: 24}),而非在ZonedDateTime上调用.add({days: 1})(后者可能因夏令时跳变少算一小时) - 显示给用户看时,才用
instant.toZonedDateTimeISO('Asia/Shanghai')转成带时区视图,绝不直接用toLocaleString()
真正容易被忽略的点是:Temporal 的三类对象(PlainDateTime、ZonedDateTime、Instant)之间没有隐式转换,也不该手动拆解再拼装。一旦混用或绕过构造器直接操作字段,就退回了 Date 的混乱状态。

















