Temporal を使用したグローバル イベントの調整

ウェブ機能: Temporal

従来の Date オブジェクトでは、異なるタイムゾーンにまたがるイベントのスケジュール設定が非常に困難です。特に、夏時間(DST)の移行期には、時間がスキップされたり、繰り返されたりすることがあります。

Temporal API には、特定のタイムゾーンの日時を表す Temporal.ZonedDateTime が用意されており、DST の移行を自動的かつ予測可能に処理します。

実装方法

グローバル イベントを調整し、DST の競合を処理するには:

  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 はタイムゾーン情報を持たず、DST の変更を考慮できないため、グローバル イベントには使用しないでください。
  • 推奨: .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();
})();