JavaScript Temporal API:Date のモダンな代替
JavaScript Date は何十年も前から既知の形で壊れています。TC39 Temporal——2025 年初頭から Stage 4 で、モダンなブラウザと Node.js に搭載——は、瞬間、壁時計の日付、ゾーン付き日時をきれいに分離します。本ガイドでは新しい型、それらが Unix タイムスタンプにどう対応するか、そして移行方法を説明します。
ひと目でわかる Temporal vs Date
JavaScript の組み込み Date は何十年もバグの既知の原因でした。可変であり、ローカルタイムゾーンと UTC しか理解せず、月は 0 始まりで、パースは緩く仕様化されています。Temporal は標準化された代替——Stage 4 で ECMAScript 2026 の一部——であり、これらの問題のそれぞれを目的別の不変な型で解決します。下の表は大まかな対比です。本ガイドの残りで型と、それらが Unix タイムスタンプにどう対応するかを扱います。
| 観点 | Date(旧) | Temporal |
|---|---|---|
| 可変性 | 可変 | 不変 |
| 精度 | ミリ秒 | ナノ秒 |
| タイムゾーン | ローカル + UTC のみ | 任意の IANA ゾーン(ZonedDateTime) |
| 月のインデックス | 0 始まり(0 = 1 月) | 1 始まり(1 = 1 月) |
| パース | 寛容、実装依存 | 厳密な ISO 8601 / RFC 9557 |
| 瞬間 vs 壁時計 | 1 つの型に混在 | 別々の型 |
なぜ Temporal があるのか
JavaScript Date は複数の異なる概念を混同しています:正確な瞬間、壁時計の時刻、ゾーン付き日時です。Date にはまた、よく知られたパースの癖、単一のタイムゾーン(ユーザーのもの)、そしてミリ秒精度があります。Temporal は基礎となる概念を別々の型として公開し、ナノ秒精度、任意の IANA タイムゾーン、明示的なカレンダー系をサポートします。
- 2025 年初頭から Stage 4。最初に Firefox 139 で既定搭載された
- 現在は Chrome 144+、Firefox 139+、Node.js 26+ で利用可能
- ポリフィル(@js-temporal/polyfill)はあらゆるモダンな環境で動く
- Date との共存を前提に設計——相互運用メソッドが双方向に存在する
Temporal の中核となる型
小さな型の集合が日時の問題をきれいにカバーします。各型には正準の文字列形式があります(ISO 8601、ゾーン付き型では RFC 9557 で拡張):
| 型 | 表すもの | 例の文字列 |
|---|---|---|
| Temporal.Instant | Unix タイムスタンプのような正確な瞬間(ns 精度) | 2026-01-01T00:00:00Z |
| Temporal.ZonedDateTime | 瞬間 + IANA タイムゾーン + カレンダー | 2026-01-01T09:00:00+09:00[Asia/Tokyo] |
| Temporal.PlainDateTime | 日付 + 時刻、タイムゾーンなし | 2026-01-01T09:00:00 |
| Temporal.PlainDate | カレンダー上の日付(例:誕生日) | 2026-01-01 |
| Temporal.PlainTime | 壁時計の時刻 | 09:00:00 |
| Temporal.Duration | 単位を意識した期間 | P3DT4H10M |
Unix タイムスタンプを扱う
Temporal.Instant が Unix タイムスタンプの型です。搭載 API はミリ秒とナノ秒の epoch 変換しか公開しません——以前の fromEpochSeconds と fromEpochMicroseconds メソッドは Stage 4 の前に削除されました——なので Unix 秒は 1000 を掛けて変換します。保存と転送には Instant を、特定のタイムゾーンでの壁時計表示が必要なときは ZonedDateTime を使います。
- Temporal.Now.instant() — 現在の正確な瞬間
- Temporal.Instant.fromEpochMilliseconds(Date.now()) — JS のミリ秒タイムスタンプから
- Temporal.Instant.fromEpochMilliseconds(1700000000 * 1000) — Unix 秒から(× 1000)
- Temporal.Instant.fromEpochNanoseconds(1700000000000000000n) — ナノ秒から(BigInt)
- instant.epochMilliseconds と instant.epochNanoseconds — epoch 値を読み戻す
- instant.toZonedDateTimeISO("America/New_York") — あるゾーンでの壁時計表示
タイムゾーンと DST を正しく扱う
ZonedDateTime は IANA タイムゾーンを持つので、DST 切替、ギャップ時刻、重複時刻はすべて明示的に扱われます。曖昧性解決オプションで曖昧なローカル時刻に対する方針を選べ、正準の文字列形式は RFC 9557(ゾーンを角括弧で付した ISO 8601 タイムスタンプ)です。
- zdt.timeZoneId — IANA 名(例:America/Los_Angeles)
- zdt.add({ hours: 1 }) — 壁時計の観点での DST 対応の算術
- zdt.until(zdt2) — 2 つのゾーン付き日時の間の距離
- 曖昧性解決オプション:"earlier"、"later"、"compatible"(既定 — ギャップは後、重複は前)、"reject"
- 往復:instant ↔ ゾーン付き日時は無損失
Date からの移行パターン
最も簡単な移行戦略は、まず境界(入力のパース、出力のフォーマット)で Temporal を導入し、内部ロジックを徐々に移すことです。
- Date → Instant:Temporal.Instant.fromEpochMilliseconds(date.getTime())
- Instant → Date:new Date(instant.epochMilliseconds)
- 新しいコードではどこでも Temporal を優先し、API の縁でのみ Date に変換する
- date-fns と Luxon はどちらも Temporal 対応のモード/アダプターを持つ
- JSON:Temporal.Instant.prototype.toJSON は ISO 8601 文字列を返す
ブラウザとランタイムのサポート
Temporal は 2025 年に搭載され、ネイティブサポートは今や広範ですが、まだ普遍的ではありません。古いブラウザや Safari 安定版がまだ使われている間は、ポリフィルが最も安全な道です。
- Firefox 139+:最初に搭載、既定で有効(2025 年 5 月)
- Chrome 144+:既定で有効(2026 年 1 月)
- Node.js 26+:既定で有効(2026 年 5 月)
- Safari:Technology Preview で利用可能、安定版にはまだ未搭載
- ポリフィル:@js-temporal/polyfill または temporal-polyfill — 同じ API、あらゆるモダンな環境
関連リファレンス
FAQ
- Temporal は Date の代替ですか?
- はい——Temporal は新しいコードで Date を完全に置き換えるよう設計されています。Date は後方互換性のため言語に残り、両者は epoch ミリ秒を介して相互運用します。
- Unix タイムスタンプを Temporal にどう変換しますか?
- Temporal.Instant.fromEpochMilliseconds() を使います。Unix 秒なら 1000 を掛けます:Temporal.Instant.fromEpochMilliseconds(seconds * 1000)。搭載 API には fromEpochMilliseconds と fromEpochNanoseconds しかありません——古い fromEpochSeconds メソッドは Temporal が Stage 4 に達する前に削除されました。
- Temporal はナノ秒をサポートしますか?
- はい。Temporal.Instant は内部でナノ秒精度を持ち、epochNanoseconds を BigInt として公開します。
- 今日 Node.js で Temporal を使えますか?
- Node.js 26+ では Temporal が既定で有効です。古い Node では @js-temporal/polyfill をインストールします——API の表面は同一です。
- どのブラウザが Temporal をサポートしますか?
- Firefox 139+(2025 年 5 月)、Chrome 144+(2026 年 1 月)、Node.js 26+(2026 年 5 月)が Temporal を既定で搭載します。Safari は Technology Preview にあります。それより古いものには @js-temporal/polyfill を使います。
- Temporal は DST のギャップをどう扱いますか?
- ZonedDateTime のコンストラクターと算術は曖昧性解決オプションを受け取ります。"compatible"(既定)はギャップには後の瞬間、重複には前の瞬間を選びます。"reject" はローカル時刻が曖昧ならエラーを投げます。