使用 Temporal 协调全球事件

Web 功能: Temporal

众所周知,使用旧版 Date 对象安排跨不同时区的活动非常困难,尤其是在夏令时 (DST) 转换期间,因为时间可能会被跳过或重复。

Temporal API 提供 Temporal.ZonedDateTime 来表示特定时区中的日期和时间,并以可预测的方式自动处理夏令时转换。

如何实现

如需协调全球活动并处理潜在的夏令时冲突,请执行以下操作:

  1. 强制性: 创建 ZonedDateTime:使用 Temporal.ZonedDateTime.from() 创建可感知时区的日期时间对象。
  2. 强制性: 处理歧义:使用 disambiguation 选项控制时间不明确或不存在(例如,在时钟更改期间)时的行为。
  3. 强制性: 转换时区:使用 .withTimeZone() 查看其他地点的等效时间。

示例:安排和冲突检测

// 1. Define the event time and target time zone
const date = "2025-03-09";
const time = "02:30"; // This time is skipped in New York during Spring Forward
const timeZone = "America/New_York";
const inputStr = `${date}T${time}[${timeZone}]`;

// 2. Detect conflicts using 'reject'
let hasConflict = false;
try {
  // 'reject' throws RangeError if the time is ambiguous or does not exist
  Temporal.ZonedDateTime.from(inputStr, { disambiguation: 'reject' });
} catch (e) {
  if (e instanceof RangeError) {
    hasConflict = true;
    console.log("This time falls in a DST transition gap or overlap.");
  }
}

// 3. Resolve the time safely using 'compatible' (default)
// 'compatible' will resolve to a valid time even if skipped or repeated
const hostTime = Temporal.ZonedDateTime.from(inputStr, { disambiguation: 'compatible' });
console.log(`Resolved time: ${hostTime.toString()}`);

// 4. Convert to another time zone (e.g., Tokyo)
const tokyoTime = hostTime.withTimeZone("Asia/Tokyo");
// Use toLocaleString() for user-facing display instead of manual hour/minute padding
console.log(`Tokyo time: ${tokyoTime.toLocaleString('en-US', { hour: 'numeric', minute: '2-digit' })}`);

策略性实施和最佳实践

  • 对于与特定地理位置绑定的活动(例如在特定城市举行的会议),请使用 Temporal.ZonedDateTime。
  • 务必使用 disambiguation: 'reject',以便在夏令时转换期间检测日程冲突并向用户发出警告。
  • 务必在您希望系统在发生冲突时自动选择合理时间时使用 disambiguation: 'compatible'(默认值)。
  • 请勿将 Temporal.PlainDateTime 用于全球活动,因为它不包含时区信息,无法反映夏令时变化。
  • 要使用 .withTimeZone() 计算其他位置的等效时间,而不改变原始对象(Temporal 对象不可变)。
  • 务必使用原生 .toLocaleString() 格式(例如 { hour: 'numeric', minute: '2-digit' })来显示面向用户的时间,而不是手动计算时间和填充。

后备策略

Temporal 的回退和浏览器支持

对于尚不支持原生 Temporal API 的浏览器,请使用功能检测并动态加载 temporal-polyfill(约 15 KB min+gz)。

请注意,导入 temporal-polyfill 不会自动将 Temporal 对象分配给全局范围。如果您的代码依赖于 globalThis.Temporal,请明确分配它。

// Check if Temporal is supported natively
(async () => {
  if (typeof Temporal === 'undefined') {
    // Load the polyfill conditionally
    const module = await import('https://esm.sh/temporal-polyfill');
    globalThis.Temporal = module.Temporal;
    // Extend Date.prototype if needed
    Date.prototype.toTemporalInstant = module.toTemporalInstant;
  }
  initializeApp();
})();