中文

JavaScript Temporal API 深度指南,一个在多样化的国际环境中高效处理日期和时间的现代解决方案。

JavaScript Temporal API:面向全球用户的现代日期与时间处理

长期以来,JavaScript 的 `Date` 对象一直是开发者的挫败感之源。其可变性、不一致的 API 以及糟糕的时区支持催生了众多库(如 Moment.js 和 date-fns)来填补空白。现在,随着 Temporal API 的出现,JavaScript 提供了一种现代化的内置解决方案,能够以更高的清晰度和精确度处理日期和时间。本文将全面概述 Temporal API,重点介绍其在多样化的国际环境中的功能、优势和用法。

什么是 Temporal API?

Temporal API 是 JavaScript 中一个新的全局对象,旨在解决 `Date` 对象的缺点。它提供了一个清晰、不可变的 API,用于处理日期、时间、时区和日历系统。至关重要的是,它的目标是以更贴近现实世界使用和期望的方式来表示日期和时间概念,从而使国际化变得更加直接。

主要特性:

基本的 Temporal 对象

Temporal API 引入了几种新的对象类型。以下是一些核心类型:

处理日期

创建 `Temporal.PlainDate`

要创建一个 `Temporal.PlainDate`,您可以使用构造函数:

const plainDate = new Temporal.PlainDate(2024, 10, 27); // 年, 月 (1-12), 日
console.log(plainDate.toString()); // 输出: 2024-10-27

您也可以使用 `from` 方法,它接受一个 ISO 8601 格式的字符串:

const plainDateFromString = Temporal.PlainDate.from('2024-10-27');
console.log(plainDateFromString.toString()); // 输出: 2024-10-27

获取日期组件

您可以使用 `year`、`month` 和 `day` 等属性访问单个日期组件:

console.log(plainDate.year); // 输出: 2024
console.log(plainDate.month); // 输出: 10
console.log(plainDate.day); // 输出: 27

日期算术

要增加或减少天数、周数、月数或年数,请使用 `plus` 和 `minus` 方法。这些方法会返回一个新的 `Temporal.PlainDate` 对象:

const nextWeek = plainDate.plus({ days: 7 });
console.log(nextWeek.toString()); // 输出: 2024-11-03

const lastMonth = plainDate.minus({ months: 1 });
console.log(lastMonth.toString()); // 输出: 2024-09-27

比较日期

您可以使用 `compare` 方法比较日期:

const date1 = new Temporal.PlainDate(2024, 10, 27);
const date2 = new Temporal.PlainDate(2024, 11, 15);

console.log(Temporal.PlainDate.compare(date1, date2)); // 输出: -1 (date1 早于 date2)

处理时间

创建 `Temporal.PlainTime`

要创建 `Temporal.PlainTime`,请使用构造函数:

const plainTime = new Temporal.PlainTime(10, 30, 0); // 时, 分, 秒
console.log(plainTime.toString()); // 输出: 10:30:00

或者使用 `from` 方法和 ISO 8601 时间字符串:

const plainTimeFromString = Temporal.PlainTime.from('10:30:00');
console.log(plainTimeFromString.toString()); // 输出: 10:30:00

获取时间组件

console.log(plainTime.hour); // 输出: 10
console.log(plainTime.minute); // 输出: 30
console.log(plainTime.second); // 输出: 0

时间算术

const later = plainTime.plus({ minutes: 15 });
console.log(later.toString()); // 输出: 10:45:00

同时处理日期和时间

创建 `Temporal.PlainDateTime`

您可以直接创建 `Temporal.PlainDateTime`,或者通过组合一个 `Temporal.PlainDate` 和一个 `Temporal.PlainTime` 来创建:

const plainDateTime = new Temporal.PlainDateTime(2024, 10, 27, 10, 30, 0);
console.log(plainDateTime.toString()); // 输出: 2024-10-27T10:30:00

const date = new Temporal.PlainDate(2024, 10, 27);
const time = new Temporal.PlainTime(10, 30, 0);
const combinedDateTime = date.toPlainDateTime(time);
console.log(combinedDateTime.toString()); // 输出: 2024-10-27T10:30:00

时区

对于处理不同地区用户的应用程序来说,正确处理时区至关重要。Temporal API 通过 `Temporal.ZonedDateTime` 和 `Temporal.TimeZone` 对象提供了强大的时区支持。

创建 `Temporal.ZonedDateTime`

要创建 `Temporal.ZonedDateTime`,您需要一个 `Temporal.PlainDateTime` 和一个时区标识符。时区标识符基于 IANA 时区数据库(例如,`America/Los_Angeles`、`Europe/London`、`Asia/Tokyo`)。

const plainDateTime = new Temporal.PlainDateTime(2024, 10, 27, 10, 30, 0);
const timeZone = 'America/Los_Angeles';
const zonedDateTime = plainDateTime.toZonedDateTime(timeZone);
console.log(zonedDateTime.toString()); // 输出: 2024-10-27T10:30:00-07:00[America/Los_Angeles] (偏移量将取决于夏令时规则)

或者,从一个 `Instant` 创建 `Temporal.ZonedDateTime`。

const instant = Temporal.Instant.fromEpochSeconds(1666866600); // 示例时间戳
const zonedDateTimeFromInstant = instant.toZonedDateTimeISO(timeZone); // 像 'America/Los_Angeles' 这样的时区
console.log(zonedDateTimeFromInstant.toString());

在时区之间转换

您可以使用 `withTimeZone` 方法将一个 `Temporal.ZonedDateTime` 转换为不同的时区:

const newTimeZone = 'Europe/London';
const zonedDateTimeInLondon = zonedDateTime.withTimeZone(newTimeZone);
console.log(zonedDateTimeInLondon.toString()); // 输出: 2024-10-27T18:30:00+01:00[Europe/London]

处理时区偏移量

`Temporal.TimeZone` 对象的 `getOffsetStringFor` 方法可以为给定的 `Temporal.Instant` 提供偏移量字符串:

const timeZoneObject = new Temporal.TimeZone(timeZone);
const offsetString = timeZoneObject.getOffsetStringFor(zonedDateTime.toInstant());
console.log(offsetString); // 输出: -07:00 (取决于夏令时规则)

为了进行准确的计算,使用正确的 IANA 时区标识符至关重要。这些标识符会定期维护和更新,以反映夏令时和时区边界的变化。

时间段 (Durations)

`Temporal.Duration` 对象表示一个时间跨度。它可以用于对日期和时间进行加减运算。

创建 `Temporal.Duration`

您可以使用构造函数创建一个 `Temporal.Duration`,指定年、月、日、时、分、秒、毫秒、微秒和纳秒:

const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9); // 年, 月, 日, 小时, 分钟, 秒, 毫秒, 微秒, 纳秒
console.log(duration.toString()); // 输出: P1Y2M3DT4H5M6.007008009S

或者使用 ISO 8601 时间段字符串:

const durationFromString = Temporal.Duration.from('P1Y2M3DT4H5M6S');
console.log(durationFromString.toString()); // 输出: P1Y2M3DT4H5M6S

将时间段添加到日期和时间

const plainDate = new Temporal.PlainDate(2024, 10, 27);
const duration = new Temporal.Duration(0, 0, 7); // 7 天
const newDate = plainDate.plus(duration);
console.log(newDate.toString()); // 输出: 2024-11-03

请注意,将涉及月份或年份的时间段添加到日期时需要仔细考虑,因为一个月或一年中的天数是可变的。

日历系统

Temporal API 支持除公历之外的不同日历系统。这对于需要处理各种文化背景下日期的应用程序至关重要。虽然支持仍在发展中,但它为未来的扩展奠定了基础。

使用其他日历

要使用特定的日历,您可以在创建 Temporal 对象时指定它:

const hebrewDate = new Temporal.PlainDate(5785, 1, 1, { calendar: 'hebrew' });
console.log(hebrewDate.toString()); // 具体输出可能因实现和格式化而异。在撰写本文时,许多环境需要 polyfill。

重要提示: 对非公历日历的支持可能需要 polyfill 或特定的浏览器/环境支持。请查阅 Temporal API 文档和浏览器兼容性表格以获取最新信息。

格式化日期和时间

虽然 Temporal API 专注于日期和时间的操纵,但格式化通常由 `Intl.DateTimeFormat` 对象处理,该对象是国际化 API 的一部分。Temporal 对象与 `Intl.DateTimeFormat` 可以无缝协作。

使用 `Intl.DateTimeFormat`

以下是如何使用 `Intl.DateTimeFormat` 格式化 `Temporal.PlainDate`:

const plainDate = new Temporal.PlainDate(2024, 10, 27);
const formatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
console.log(formatter.format(plainDate)); // 输出: October 27, 2024

const formatterGerman = new Intl.DateTimeFormat('de-DE', { year: 'numeric', month: 'long', day: 'numeric' });
console.log(formatterGerman.format(plainDate)); // 输出: 27. Oktober 2024

您可以自定义格式选项以满足您的需求。`Intl.DateTimeFormat` 的第一个参数是区域设置(locale),它决定了格式化时使用的语言和地区惯例。使用不同的区域设置(例如 'en-US'、'de-DE'、'fr-FR'、'ja-JP')会产生不同的输出格式。

格式化 `Temporal.ZonedDateTime`

格式化 `Temporal.ZonedDateTime` 与此类似,但您还可以在输出中包含时区信息:

const plainDateTime = new Temporal.PlainDateTime(2024, 10, 27, 10, 30, 0);
const timeZone = 'America/Los_Angeles';
const zonedDateTime = plainDateTime.toZonedDateTime(timeZone);

const formatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', timeZoneName: 'short' });
console.log(formatter.format(zonedDateTime)); // 输出: October 27, 2024, 10:30 AM PDT (时区缩写取决于夏令时规则)

国际化最佳实践

在全球化背景下处理日期和时间时,请牢记以下最佳实践:

Temporal API 与传统 Date 对象的比较

下表突出了 Temporal API 相较于传统 `Date` 对象的主要区别和优势:

特性 传统的 `Date` 对象 Temporal API
可变性 可变(修改原始对象) 不可变(返回新对象)
时区支持 有限且经常有问题 强大且准确,基于 IANA 时区数据库
API 不一致且难以使用 清晰、一致且直观
精度 毫秒 纳秒
日历系统 仅限公历 支持其他日历系统(支持在不断发展中)
国际化 需要外部库来实现强大的国际化 内置支持并与 `Intl.DateTimeFormat` 无缝集成

浏览器支持和 Polyfills

作为一个相对较新的 API,浏览器对 Temporal API 的支持仍在发展中。请查看最新的浏览器兼容性表格(例如,在 MDN Web Docs 上)以了解哪些浏览器和环境原生支持它。对于没有原生支持的旧版浏览器或环境,您可以使用 polyfill 来提供 Temporal API 的功能。在网上搜索“Temporal API polyfill”以找到合适的选项。

结论

JavaScript Temporal API 代表了 JavaScript 在处理日期和时间方面的重大进步。其不可变性、清晰的 API、强大的时区支持和日历系统功能使其成为一个强大的工具,帮助开发者构建需要在多样化的国际环境中准确可靠地处理日期和时间的应用程序。虽然浏览器支持仍在发展中,但 Temporal API 的优势使其值得在新项目中学习和采用。通过拥抱 Temporal API 并遵循国际化最佳实践,您可以创建为全球用户提供无缝且准确的日期和时间体验的应用程序。

进一步学习