Temporal Polyfill
JavaScript Temporal API 的 Polyfill 实现——ES2026 标准中原生的现代日期时间 API,旨在彻底替代有缺陷的 Date 对象。
Temporal 是 JavaScript 语言级别的新一代日期时间 API,2026 年 3 月达到 TC39 Stage 4 成为 ES2026 标准。提供不可变对象、纳秒精度、原生时区和多日历系统支持,temporal-polyfill 在浏览器原生支持全面铺开前提供生产可用的过渡方案。
Temporal Polyfill
JavaScript Temporal API 的 Polyfill 实现——ES2026 标准中原生的现代日期时间 API,旨在彻底替代有缺陷的 Date 对象。
技术简介说明
Temporal 是 JavaScript 语言级别的新一代日期时间 API,于 2026 年 3 月达到 TC39 Stage 4,正式成为 ES2026 标准的一部分。它经历了约 9 年的提案演进,旨在彻底解决 JavaScript 原始 Date 对象的根本性缺陷——可变性、缺少时区支持、月份从 0 开始计数、无法安全表示纯日期等。
Temporal 作为语言内置的全局命名空间(类似 Intl、Math),提供了不可变的、类型安全的日期时间操作。它原生支持时区、日历系统、纳秒精度时间戳,并且 API 设计充分考虑了 DST(夏令时)安全。
在浏览器原生支持全面铺开之前,temporal-polyfill 和 @js-temporal/polyfill 提供了生产可用的 Polyfill 实现,使开发者可以提前使用 Temporal API。随着 Chrome 144+ 和 Firefox 139+ 已经原生支持 Temporal,Polyfill 主要用于兼容旧浏览器和作为过渡方案。
基本信息
| 项目 | 详情 |
|---|---|
| 官网/规范 | https://tc39.es/proposal-temporal/docs/ |
| MDN 文档 | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal |
| GitHub(提案) | https://github.com/tc39/proposal-temporal |
| Polyfill(temporal-polyfill) | https://www.npmjs.com/package/temporal-polyfill |
| Polyfill(@js-temporal/polyfill) | https://www.npmjs.com/package/@js-temporal/polyfill |
| License | MIT |
| 最新版本 | temporal-polyfill v1.0.1(2026 年) |
| 标准状态 | ES2026 标准(TC39 Stage 4,2026 年 3 月) |
| 主要推动者 | Google(V8 团队)、Bloomberg、Igalia、Mozilla |
快速上手
安装
# 轻量级 Polyfill(推荐,~19.8KB gzipped)
npm install temporal-polyfill
# 官方完整 Polyfill(Alpha 阶段,体积较大)
npm install @js-temporal/polyfill
# CDN(temporal-polyfill)
<script src="https://cdn.jsdelivr.net/npm/temporal-polyfill/global.min.js"></script>基础配置
// temporal-polyfill(推荐)
import { Temporal } from 'temporal-polyfill'
// @js-temporal/polyfill
import { Temporal } from '@js-temporal/polyfill'
// 检测是否需要 Polyfill(条件加载)
const TemporalNS = globalThis.Temporal || (await import('temporal-polyfill')).Temporal最小示例
import { Temporal } from 'temporal-polyfill'
// 当前时间(纳秒精度)
const now = Temporal.Now.instant()
console.log(now.toString()) // '2026-07-07T06:30:00.123456789Z'
// 本地日期
const today = Temporal.Now.plainDateISO()
console.log(today.toString()) // '2026-07-07'
// 带时区的日期时间
const zoned = Temporal.Now.zonedDateTimeISO()
console.log(zoned.toString()) // '2026-07-07T14:30:00+08:00[Asia/Shanghai]'
// 日期计算
const tomorrow = today.add({ days: 1 })
// 时间差(Duration)
const diff = tomorrow.until(today)
console.log(diff.toString()) // '-PT24H'
// 格式化
console.log(zoned.toLocaleString('zh-CN', {
dateStyle: 'full',
timeStyle: 'long'
})) // '2026年7月7日星期一 下午2:30:00 [Asia/Shanghai]'核心概念与架构
设计哲学
Temporal 的核心设计原则:
- 不可变:所有操作返回新对象,原始对象永不改变
- 显式优于隐式:时区转换必须显式指定,不依赖全局状态
- 类型安全:不同概念使用不同对象类型,避免语义混淆
- 纳秒精度:内部使用纳秒级时间戳,满足高精度场景
- DST 安全:API 设计考虑了夏令时边界情况
核心对象体系
Temporal 定义了一套完整的对象类型,按语义精确分类:
| 对象 | 语义 | 示例 |
|---|---|---|
Temporal.Instant | 时间线上的精确时间点(UTC 纳秒) | 2026-07-07T06:30:00Z |
Temporal.ZonedDateTime | 带时区的日期时间 | 2026-07-07T14:30:00+08:00[Asia/Shanghai] |
Temporal.PlainDateTime | 不带时区的日期时间 | 2026-07-07T14:30:00 |
Temporal.PlainDate | 纯日期(无时间) | 2026-07-07 |
Temporal.PlainTime | 纯时间(无日期) | 14:30:00 |
Temporal.PlainYearMonth | 年月(如信用卡有效期) | 2026-07 |
Temporal.PlainMonthDay | 月日(如生日) | 07-07 |
Temporal.Duration | 时间跨度 | P1Y2M3DT4H5M6S |
Temporal.Now | 获取当前时间的工具对象 | Temporal.Now.instant() |
Temporal.TimeZone | 时区抽象 | Temporal.TimeZone.from('Asia/Shanghai') |
Temporal.Calendar | 日历系统抽象 | Temporal.Calendar.from('iso8601') |
类型分离的价值
传统 Date 对象将"日期"和"带时区的时间"混为一谈。Temporal 通过类型分离解决了这个问题:
// ❌ Date 对象的歧义
const d = new Date('2026-07-07')
// 这是 UTC 的 7 月 7 日还是本地的 7 月 7 日?
// ✅ Temporal 的精确语义
const plainDate = Temporal.PlainDate.from('2026-07-07') // 明确:纯日期
const zoned = Temporal.ZonedDateTime.from('2026-07-07T00:00:00[Asia/Shanghai]') // 明确:带时区核心特性
- ES2026 标准:TC39 Stage 4,语言级别的日期时间解决方案
- 不可变设计:所有对象创建后不可修改
- 纳秒精度:内部使用 BigInt 存储纳秒级时间戳
- 原生时区支持:IANA 时区数据库集成,无需额外数据文件
- 日历系统支持:原生支持非格里高利日历(伊斯兰历、佛历、希伯来历等)
- DST 安全:API 设计显式处理夏令时边界
- 类型安全:11 种精确类型,消除日期语义歧义
- 国际化友好:与
IntlAPI 深度集成,支持toLocaleString - 链式且可组合:
instant.toZonedDateTimeISO('Asia/Shanghai').toPlainDate() - Duration 运算:支持日历感知的 duration 运算(考虑闰年、月份天数)
生态图
Polyfill 方案
| 包名 | 状态 | 体积 | 说明 |
|---|---|---|---|
temporal-polyfill | Beta/v1.0.1 | ~19.8KB gzipped | 轻量级,适合生产环境 |
@js-temporal/polyfill | Alpha | ~60KB+ | 官方完整实现,功能更全面 |
浏览器原生支持进度(2026 年中)
| 浏览器 | 支持状态 |
|---|---|
| Chrome 144+ | ✅ 原生支持 |
| Firefox 139+ | ✅ 原生支持 |
| Safari | 🟡 Technical Preview 中大部分可用 |
| Edge | 🟡 Beta 实验性支持 |
| Node.js | 🟡 Flag 后可用 |
| Deno | 🟡 部分支持 |
| Bun | 🟡 部分支持 |
相关生态
- Intl API:Temporal 与
Intl.DateTimeFormat深度集成 - TypeScript 类型:
@types/temporal或内置类型定义 - 迁移工具:从 Date/Moment.js/date-fns 迁移到 Temporal 的指南和 codemod
关键依赖
Temporal 作为语言内置 API,无外部依赖。Polyfill 同样零外部依赖。
适用场景
- 新项目的首选日期方案:2026 年新项目应优先考虑使用 Temporal API(原生或 Polyfill)
- 需要高精度时间戳:日志、监控、科学计算等需要纳秒精度的场景
- 复杂时区处理:全球化应用、跨时区日程安排
- 非格里高利日历:需要支持伊斯兰历、佛历、希伯来历的应用
- 替代 Moment.js/date-fns/Luxon:Temporal 的完整功能集可以替代所有第三方日期库
- 服务端日期处理:Node.js 环境下的精确日期运算
- 未来保障:使用语言标准意味着不需要依赖第三方库的维护状态
开发与工程化
条件加载 Polyfill
// 推荐:仅在浏览器不支持时加载 Polyfill
const loadTemporal = async () => {
if (globalThis.Temporal) return globalThis.Temporal
const { Temporal } = await import('temporal-polyfill')
return Temporal
}
const Temporal = await loadTemporal()Webpack/Vite 配置
// webpack.config.js - 条件加载
module.exports = {
// 无需特殊配置,使用动态 import 即可
}
// vite.config.js
// Vite 原生支持动态 import,无需额外配置测试
// Vitest / Jest 中使用 Temporal
import { Temporal } from 'temporal-polyfill'
// 固定时间进行测试
const mockNow = Temporal.ZonedDateTime.from('2026-07-07T14:30:00[Asia/Shanghai]')
// 使用 Temporal.Now 的 mock 能力
// 注意:Temporal.Now 不能被直接 mock,需要传入具体值替代CI/CD 集成
Temporal 作为标准 API,在 CI/CD 中无需特殊配置。但需注意 Node.js 版本的 Temporal 支持状态。
性能与安全
性能特点
- 解析速度:基准测试显示 Temporal 实现已匹配或超过原生
Date对象 - 内存效率:不可变设计 + 纳秒精度使用 BigInt,内存占用略高于 Date
- 包体积:原生支持时无额外体积;Polyfill(temporal-polyfill)约 19.8KB gzipped
与第三方库性能对比
在主流浏览器中,Temporal 的性能已经可以匹配甚至超过第三方库:
- 解析 ISO 字符串:Temporal ≈ 原生 Date > Day.js > date-fns > Luxon
- 日期计算:Temporal ≈ date-fns > Day.js > Luxon
安全注意事项
- Temporal 是语言内置 API,攻击面极小
- Polyfill 由 TC39 提案团队维护,代码质量有保障
- 用户输入的日期字符串仍应通过
Temporal.PlainDate.from()的异常处理进行校验
技术对比
| 特性 | Temporal | Day.js | date-fns | Luxon | Moment.js |
|---|---|---|---|---|---|
| 类型 | 语言标准 | 第三方库 | 第三方库 | 第三方库 | 第三方库 |
| 浏览器支持 | Chrome 144+(2026) | 所有 | 所有 | 所有 | 所有 |
| 不可变 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 时区 | ✅ 原生 | 插件 | @date-fns/tz | ✅ Intl | moment-timezone |
| 日历系统 | ✅ 多日历 | ❌ | ❌ | ✅ Intl | ❌ |
| 纳秒精度 | ✅ | 毫秒 | 毫秒 | 毫秒 | 毫秒 |
| Tree-shaking | N/A(内置) | ❌ | ✅ | ✅ | ❌ |
| 包体积 | 0(原生) | 2KB | ~17.5KB | ~53KB | ~72KB |
| 类型数量 | 11 种精确类型 | 1 种 | N/A(函数) | 3 种 | 1 种 |
| 维护方 | TC39 / 浏览器厂商 | 社区 | 社区 | Moment 团队 | 维护模式 |
| DST 安全 | ✅ | 部分 | ✅ | ✅ | ❌ |
Temporal 的独特优势
- 语言标准:不依赖任何第三方库,随浏览器和 Node.js 版本升级
- 类型精确:11 种对象类型消除了日期语义歧义
- 纳秒精度:唯一提供纳秒级精度的方案
- 多日历支持:唯一原生支持非格里高利日历的方案
最佳实践
推荐模式
// ✅ 使用条件加载模式
const Temporal = globalThis.Temporal ||
(await import('temporal-polyfill')).Temporal
// ✅ 优先使用 ZonedDateTime 处理带时区的场景
const meeting = Temporal.ZonedDateTime.from({
year: 2026, month: 7, day: 7,
hour: 14, minute: 30,
timeZone: 'Asia/Shanghai'
})
// ✅ 使用 PlainDate 处理纯日期
const birthday = Temporal.PlainDate.from({ month: 7, day: 7 })
// ✅ 使用 Duration 进行精确的时间差计算
const diff = meeting.until(otherMeeting, { largestUnit: 'hours' })
// ✅ 使用 Temporal.Now 获取当前时间
const now = Temporal.Now.instant()
const todayLocal = Temporal.Now.plainDateISO()
// ✅ 替代 Date.now()
const timestamp = Temporal.Now.instant().epochMilliseconds常见陷阱
- Temporal.Now 不可 mock:测试中无法直接 mock
Temporal.Now,需要传入具体值 - Polyfill 体积:temporal-polyfill 约 19.8KB,虽然轻量但仍需考虑
- 浏览器支持不完整:Safari 和 Node.js 的支持仍在进行中,生产环境需要 Polyfill
- PlainDate vs ZonedDateTime:选择正确的类型很重要,混用可能导致意外行为
- Duration 的 ISO 格式:Duration 使用 ISO 8601 格式(
P1Y2M3DT4H5M6S),学习成本较高 - 两个 Polyfill 的选择:
temporal-polyfill更轻量,@js-temporal/polyfill功能更全但仍在 Alpha
技术局限与边界
- 浏览器支持仍在推进:Chrome 144+ 和 Firefox 139+ 已支持,但 Safari 和 Node.js 仍在完善中
- Polyfill 非 100% 完整:Polyfill 可能无法覆盖所有边界情况
- 生态不成熟:Temporal 刚进入标准,第三方库、教程、工具链不如 Moment.js/date-fns 丰富
- 学习曲线:11 种对象类型的概念体系需要时间学习
- 迁移成本:从现有 Date/Moment.js 代码迁移到 Temporal 需要大量重构
- Node.js 支持滞后:Node.js 中 Temporal 仍需要 flag 启用,服务端使用受限
- API 复杂度:对于简单的日期格式化场景,Temporal 可能"过度设计"
- 两个 Polyfill 并行:社区分裂为两个 Polyfill 实现,选择时可能困惑
学习资源
- TC39 提案仓库:https://github.com/tc39/proposal-temporal
- 规范文档:https://tc39.es/proposal-temporal/docs/
- MDN 文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal
- Bryntum 2026 状态分析:https://bryntum.com/blog/javascript-temporal-is-it-finally-here/
- Bloomberg 技术博客:https://bloomberg.github.io/js-blog/post/temporal/
- Reddit 讨论:https://www.reddit.com/r/javascript/comments/1rqx4dh/temporal_the_9year_journey_to_fix_time_in/
- PkgPulse 迁移指南:https://www.pkgpulse.com/guides/temporal-api-replace-momentjs-date-fns-2026
- temporal-polyfill npm:https://www.npmjs.com/package/temporal-polyfill
- YouTube 教程:https://www.youtube.com/watch?v=Q0BOHt6iHAY
2026 年现状
标准状态
- ES2026 标准:2026 年 3 月达到 TC39 Stage 4,正式成为 JavaScript 标准
- 这是 JavaScript 日期处理领域 9 年努力的里程碑
浏览器支持
- Chrome 144+:✅ 原生支持(2026 年 1 月)
- Firefox 139+:✅ 原生支持(2025 年 5 月)
- Safari:🟡 Technical Preview 中大部分 API 可用
- Edge:🟡 Beta 实验性支持
- Node.js:🟡 需要 flag 启用
- Deno / Bun:🟡 部分支持
Polyfill 状态
- temporal-polyfill v1.0.1:轻量级(~19.8KB),生产可用
- @js-temporal/polyfill:仍在 Alpha 阶段,功能更全
发展趋势
- 2026 年是 Temporal 元年:进入标准 + 主流浏览器支持,是日期处理领域的分水岭
- 第三方库的过渡角色:date-fns、Day.js、Luxon 可能会逐步提供 Temporal 互操作层,或定位为 Temporal 的补充
- Moment.js 的终结者:Temporal 的完整功能集(时区、日历、不可变、纳秒精度)使得第三方日期库的必要性大幅降低
- 渐进式迁移:在浏览器支持完全普及前(预计 2027-2028 年),Polyfill 是必要的过渡方案
- 长期趋势:Temporal 将成为 JavaScript 日期处理的唯一标准方案,第三方日期库将逐步退出历史舞台