
JavaScript Temporal 即将到来
新的 JavaScript Temporal 对象正在浏览器实验版本中逐步推出。这对 Web 开发者来说是个大好消息,因为在 JavaScript 中处理日期和时间将大大简化和现代化。
依赖于调度、国际化或时间敏感数据的应用程序将能够使用内置功能来高效、精确、一致地处理日期、时间、持续时间和日历。距离稳定、跨浏览器支持还有很长的路要走,并且随着实现的开发可能会发生变化,但我们已经可以看看 Temporal 目前的状态,它为何令人兴奋,以及它解决了什么问题。
为了帮助您快速上手,本周 MDN 上添加了 超过 270 页的 Temporal 文档,其中包含详细的解释和示例。
JavaScript Temporal 是什么?
要理解 Temporal,我们可以看看 JavaScript 的 Date
对象。JavaScript 在 1995 年创建时,Date
对象是从 Java 早期的、有缺陷的 java.util.Date
实现复制过来的。Java 在 1997 年更换了该实现,但 JavaScript 却坚持使用相同的 API 近 30 年,尽管存在已知问题。
JavaScript Date
对象的主要问题是它只支持用户的本地时间和 UTC,没有时区支持。此外,它的解析行为非常不可靠,并且 Date
本身是可变的,这可能导致难以追踪的错误。还有其他问题,例如夏令时 (DST) 的计算和历史日历的变化,这些问题都极难处理。
所有这些问题使得在 JavaScript 中处理日期和时间变得复杂且容易出错,这可能对某些系统造成严重后果。大多数开发者依赖于 Moment.js 和 date-fns 等专用库来更好地处理应用程序中的日期和时间。
Temporal 被设计为 Date
对象的完整替代品,使日期和时间管理可靠且可预测。Temporal 增加了对时区和日历表示的支持,以及许多用于转换、比较和计算、格式化等的内置方法。API 表面超过 200 个实用方法,您可以在 MDN 上的 Temporal 文档 中找到关于所有这些方法的详细信息。
核心概念
在 Temporal 中,关键概念是它有“瞬间”(历史上的唯一时间点)、“挂钟时间”(区域时间)和“持续时间”。API 具有整体结构来处理这些概念。
- 持续时间:
Temporal.Duration
,表示两个时间点之间的差值。 - 时间点:
- 唯一的时间点:
- 作为时间戳:
Temporal.Instant
- 带时区的日期时间:
Temporal.ZonedDateTime
- 作为时间戳:
- 不考虑时区的日期/时间(“Plain”):
- 完整的日期和时间:
Temporal.PlainDateTime
- 仅日期:
Temporal.PlainDate
- 年和月:
Temporal.PlainYearMonth
- 月和日:
Temporal.PlainMonthDay
- 年和月:
- 仅时间:
Temporal.PlainTime
- 仅日期:
- 完整的日期和时间:
- 唯一的时间点:
- 当前时间:使用
Temporal.now
以各种类实例或特定格式获取当前时间。
Temporal 示例
Temporal 最基本的一些用法包括将当前日期和时间获取为 ISO 字符串,但从下面的示例可以看出,我们现在可以在许多方法中提供时区,这可以处理您可能需要自己进行的复杂计算。
// The current date in the system's time zone
const dateTime = Temporal.Now.plainDateTimeISO();
console.log(dateTime); // e.g.: 2025-01-22T11:46:36.144
// The current date in the "America/New_York" time zone
const dateTimeInNewYork = Temporal.Now.plainDateTimeISO("America/New_York");
console.log(dateTimeInNewYork);
// e.g.: 2025-01-22T05:47:02.555
处理不同的日历也得到了简化,因为可以创建除格里高利历以外的日历系统中的日期,例如希伯来历、中国历和伊斯兰历。下面的代码可以帮助您找出下一个中国新年是什么时候(很快就到了!)。
// Chinese New Years are on 1/1 in the Chinese calendar
const chineseNewYear = Temporal.PlainMonthDay.from({
monthCode: "M01",
day: 1,
calendar: "chinese",
});
const currentYear = Temporal.Now.plainDateISO().withCalendar("chinese").year;
let nextCNY = chineseNewYear.toPlainDate({ year: currentYear });
// If nextCNY is before the current date, move forward by 1 year
if (Temporal.PlainDate.compare(nextCNY, Temporal.Now.plainDateISO()) <= 0) {
nextCNY = nextCNY.add({ years: 1 });
}
console.log(
`The next Chinese New Year is on ${nextCNY.withCalendar("iso8601").toLocaleString()}`,
);
// The next Chinese New Year is on 1/29/2025 (at the time of writing)
处理 Unix 时间戳是一个非常常见的用例,因为许多系统(API、数据库)都使用这种格式来表示时间。下面的示例展示了如何将 Unix 纪元时间戳(以毫秒为单位)转换为 Instant,使用 Temporal.Now
获取当前时间,然后计算从现在到 Unix 时间戳还有多少小时。
// 1851222399924 is our timestamp
const launch = Temporal.Instant.fromEpochMilliseconds(1851222399924);
const now = Temporal.Now.instant();
const duration = now.until(launch, { smallestUnit: "hour" });
console.log(`It will be ${duration.toLocaleString("en-US")} until the launch`);
// "It will be 31,600 hr until the launch" <- @js-temporal/polyfill
// "It will be PT31600H until the launch" <- Firefox Nightly
目前,Firefox 实现中的 toLocaleString
不会输出本地化敏感字符串,因此上面的持续时间(PT31600H
)会以非本地化敏感的 持续时间格式 返回。这可能会发生变化,因为它更多是设计决策而不是技术限制,因为 格式化持续时间 是可能的,因此 polyfill 和 Firefox 实现最终可能会趋于一致。
有很多值得强调的地方,但 API 中我认为一个有趣的模式是 compare()
方法,它允许您以优雅而高效的方式 对持续时间进行排序。
const durations = [
Temporal.Duration.from({ hours: 1 }),
Temporal.Duration.from({ hours: 2 }),
Temporal.Duration.from({ hours: 1, minutes: 30 }),
Temporal.Duration.from({ hours: 1, minutes: 45 }),
];
durations.sort(Temporal.Duration.compare);
console.log(durations.map((d) => d.toString()));
// [ 'PT1H', 'PT1H30M', 'PT1H45M', 'PT2H' ]
尝试 Temporal 和浏览器支持
支持正在缓慢地包含在实验性浏览器版本中,而 Firefox 目前似乎拥有最成熟的实现。在 Firefox 中,Temporal 被内置到 Nightly 版本中,并通过 javascript.options.experimental.temporal
首选项启用。如果您想了解完整的兼容性情况,可以查看(相当宏大的)Temporal 对象浏览器兼容性部分。
以下是跟踪 Temporal 实现的主要浏览器 bug:
- Firefox: 在 Nightly 中默认构建 Temporal
- Safari: [JSC] 实现 Temporal
- Chrome: 实现 Temporal 提案
此外,您可以访问 https://tc39.es/proposal-temporal/docs/,其中提供了 @js-temporal/polyfill
。这意味着您可以在任何浏览器的开发者工具中打开 TC39 文档页面,并在控制台中尝试一些示例,而无需更改标志或偏好设置。
随着实验性实现的落地,现在是尝试 Temporal 并熟悉它将成为处理 JavaScript 中日期和时间的现代方法的绝佳时机。
致谢
- 感谢 Eric Meyer 在此主题上的工作。距离 Eric 努力记录 浏览器兼容性数据 并在他的 mdn/content 分支 中构建文档 已经大约四年了。
- Joshua Chen 从 Eric 那里接过了火炬,并为 MDN 文档 整理了一个拉取请求。
- André Bargull 为 Firefox Temporal 实现所做的工作。
另见
- Maggie Pint 的《修复 JavaScript Date - 入门》(2017)