JavaScript の Temporal API を実際に動かす - Date の footgun を捨てる
長年 JavaScript の日付処理は Date オブジェクトの「footgun」に悩まされてきた。月が 0 始まり、パースの挙動が実装依存、ミュータブルで setMonth が元オブジェクトを破壊する、タイムゾーンはローカルと UTC しか扱えない、といった問題だ。この積年の課題を根本から解決する Temporal API が、2026年についに標準の一部になった。
Temporal は 2026年3月11日に TC39 の Stage 4 に到達し、ES2026 仕様に組み込まれた。ブラウザでは Chrome 144+ / Firefox 139+ / Edge 144+ にネイティブ実装が入り、ランタイム側でも Deno 2.7(2026年2月25日リリース) が V8 14.5 への更新にあわせて Temporal を安定化し、--unstable-temporal フラグが不要になった。
この記事では Temporal が Date の何を直すのかを整理し、手元ですぐ動かせる形で挙動を確かめる。
Date の何が問題で、Temporal は何を変えるのか
Temporal の設計原則は主に 3 つ。
1. 不変(immutable)である。 すべての Temporal 型は不変で、add や withTimeZone などの操作は必ず新しいオブジェクトを返す。元のオブジェクトは変わらない。Date.prototype.setMonth のように既存の値を破壊してしまう事故が起きない。
2. 用途ごとに型が分かれている。 タイムゾーンを持たない日付だけの Temporal.PlainDate、日時の Temporal.PlainDateTime、タイムゾーン付きの Temporal.ZonedDateTime、絶対時刻の Temporal.Instant などがある。「これはローカル時刻なのか UTC なのか」が型で明示される。
3. タイムゾーンと暦を第一級で扱う。 IANA タイムゾーン名(America/New_York など)を直接指定でき、夏時間(DST)の切り替えも自動で処理される。
実際に試す
前提: Node.js 18 以上(動作確認は Node.js v20.17.0)。ネイティブ Temporal はまだ Node の安定版に入っていないため、ここでは仕様と同一 API を提供する公式系ポリフィル temporal-polyfill(動作確認は v1.0.1)を使う。これで任意の Node バージョンから、ブラウザ/Deno のネイティブ Temporal と同じコードを動かせる。
まず作業ディレクトリを用意してインストールする。
1 | |
次に demo.mjs を作成する。
1 | |
実行する。
1 | |
期待される出力(today はこの記事の日付で実行した場合)。
1 | |
出力から読み取れるポイントを補足する。
- 不変性:
today.add({ months: 1 })を呼んだあともtodayは2026-07-10のまま。加算結果はnextMonthとして別に返る。DateのsetMonthと違い、うっかり元の値を壊すことがない。 - タイムゾーン変換: ニューヨークの 3/15 14:30(EDT, UTC-4)が、東京では 3/16 03:30(JST, UTC+9)になる。同一の瞬間を保ったまま表示だけが切り替わっている。オフセットも自動で付く。
- 期間:
untilは ISO 8601 の期間表現P5M15D(5ヶ月15日)を返す。largestUnitで最大単位を指定できる。 - DST の自動処理: ここが
Dateでは特に厄介だった部分だ。米国の夏時間は 2026年3月8日 02:00 に始まり、時計が 02:00 から 03:00 へ飛ぶ。そのため01:30(EST, UTC-5)に 1 時間を足すと、存在しない02:30ではなく03:30(EDT, UTC-4) になり、オフセットも-05:00から-04:00へ切り替わる。Temporal はこの「消える 1 時間」を自動で処理する。
ネイティブ環境で動かす場合
ポリフィルなしで動かせる環境なら、import を消して同じコードがそのまま動く。
- ブラウザ: Chrome 144+ / Firefox 139+ / Edge 144+ では
Temporalがグローバルに存在する。DevTools のコンソールにTemporal.Now.plainDateISO().toString()と打てば確認できる。 - Deno 2.7 以降: フラグ不要で
Temporalが使える。上記コードのimport行を削除し、deno run demo.jsで実行できる。
Node.js の安定版にネイティブ実装が入るまでの間は、temporal-polyfill を使えばコードを一切変えずに移行できる。将来 Node がネイティブ対応したら import を外すだけでよい。
まとめ
Temporal は「9年越し」で JavaScript の日付処理を作り直した API で、2026年に ES2026 として正式に標準入りした。不変性・型による意図の明示・DST を含むタイムゾーンの自動処理という 3 点で、Date の代表的な落とし穴をまとめて解消する。ネイティブ対応が広がるまでもポリフィルで今日から書き始められるので、新規コードでは Date の代わりに Temporal を選ぶ価値がある。