目次
記事の概要
Temporal API
Dateオブジェクトが抱える問題
TemporalはDateの問題をどう解決するのか
イミュータブルで安全な日時操作
タイムゾーンを含めた日時管理ができる
月が 1 始まりで直感的に
曜日の扱いも直感的に
ローカライズされた曜日を簡単に取得できる
Temporalの重要な考え方
Temporalの核となるコンセプト
まとめ
参考情報
プロポーザルドキュメント
標準化状況
ブラウザでの利用状況
Polyfill
記事の概要
JavaScriptの新しい日付操作APIであるTemporalは、将来的に標準化(ES2027予定)される機能です。
本記事では、すでに主要ブラウザで試せる状態にあるTemporalを実際に触りながら、その特徴を簡単に紹介したいと思います。
Temporal API
JavaScriptには標準でDateオブジェクトが存在しており、簡単な日付の操作が可能です。しかし、いくつかの扱いづらい点があるため、実務では外部ライブラリ(例:dayjsやdate-fnsなど)が利用されるケースが多いのが現状です。
Dateオブジェクトの課題を解決するために提案され、ES2027での標準化が予定されているAPIがTemporalです。
Dateオブジェクトが抱える問題
Dateオブジェクトには、主に以下のような問題点があります。
- ミュータブル(変更可能)で意図せず値が変わる
- タイムゾーンの扱いが限定的
- 月が 0 始まりで直感的ではない
- 曜日の取得が面倒
- 日付計算や比較が複雑
TemporalはDateの問題をどう解決するのか
イミュータブルで安全な日時操作
Dateオブジェクトはミュータブルであるため、参照を共有していると意図せず値が変更されることがあります。
const baseDate = new Date(2026, 3, 17);
console.log(baseDate.toLocaleDateString()); // → "2026/04/17"
const sameRefDate = baseDate; // 参照コピー
sameRefDate.setDate(sameRefDate.getDate() + 1);
console.log(sameRefDate.toLocaleDateString()); // → "2026/04/18"
console.log(baseDate.toLocaleDateString()); // → baseDateも"2026/04/18"に変わってしまう上記を解消するためには、以下のように別オブジェクトを作成した上で日時を操作することになります。
const baseDate = new Date(2026, 3, 17);
const copyDate = new Date(baseDate); // 値コピー
copyDate.setDate(copyDate.getDate() + 1);
console.log(baseDate.toLocaleDateString()); // → "2026/04/17"
console.log(copyDate.toLocaleDateString()); // → "2026/04/18"Temporalはイミュータブルであるため、日時の操作を行なっても元のオブジェクトの値が変わることがありません。
const baseDate = Temporal.PlainDate.from("2026-04-17");
const addDate = baseDate.add({ days: 1 })
console.log(baseDate.toString()); // → "2026-04-17" 値は元のまま
console.log(addDate.toString()); // → "2026-04-18" タイムゾーンを含めた日時管理ができる
DateオブジェクトではUTCとシステムのタイムゾーンのみのサポートとなっています。
文字列に変換する際に特定のタイムゾーンに変換して表示するということは可能ですが、タイムゾーンを指定した値を保持することはできません。
// オブジェクト作成時のタイムゾーンはシステムのタイムゾーンになる
const date = new Date();
console.log(date.toLocaleString()); // ローカルシステムのタイムゾーン
console.log(date.toISOString()); // UTC
// タイムゾーンを指定した日時を文字列として表示は可能
console.log(date.toLocaleString("ja-JP", {timeZone: "Europe/London",}));TemporalはDateオブジェクトと異なり、UTCやシステムのタイムゾーン以外のタイムゾーンをサポートしています。
// 任意のタイムゾーンをサポート
const system = Temporal.Now.zonedDateTimeISO();
const london = system.withTimeZone("Europe/London"); // 2026-04-17T10:45:55.146070068+01:00[Europe/London]
console.log(system.toString());
// → "2026-04-17T18:45:55.146070068+09:00[Asia/Tokyo]"
console.log(london.toString());
// → "2026-04-17T10:45:55.146070068+01:00[Europe/London]"月が 1 始まりで直感的に
Dateオブジェクトでは月が 0 始まりで扱われるため、直感的ではありません。
// 直感的に指定してしまうと
const aprilFool = new Date(2027, 4, 1);
console.log(aprilFool.toLocaleString());
// → "2027/5/1 0:00:00"と翌月の日付になってしまう
const newYearDate = new Date("2027/01/01");
console.log(newYearDate.getMonth());
// → 0 が取得されるTemporalでは月は 1 始まりのため、直感的に指定可能です。
const aprilFoolTemporal = Temporal.PlainDate.from({
year: 2027,
month: 4,
day: 1,
});
console.log(aprilFoolTemporal.toString());
// → "2027-04-01" と指定した数値と月が同じ
const newYearTemporalDate = Temporal.PlainDate.from("2027-01-01");
console.log(newYearTemporalDate.month);
// → 1 が取得される曜日の扱いも直感的に
Dateオブジェクトでは、曜日は日曜日を 0、土曜日を 6 とする 0〜6 の数値で表現されます。
const mondayDate = new Date(2026, 3, 6);
console.log(mondayDate.getDay()); // → 1 が月曜日として取得される
const saturdayDate = new Date(2026, 3, 11);
console.log(saturdayDate.getDay()); // → 6 が土曜日として取得される
const sundayDate = new Date(2026, 3, 12);
console.log(sundayDate.getDay()); // → 0 が日曜日として取得されるTemporalでは月曜日を 1、日曜日を 7 とする 1〜7の数値で表現されます。これはISO8601 の曜日定義とも一致しています。
const mondayDateTemporal = Temporal.PlainDate.from("2026-04-06");
console.log(mondayDateTemporal.dayOfWeek); // → 1 が月曜日として取得される
const saturdayDateTemporal = Temporal.PlainDate.from("2026-04-11");
console.log(saturdayDateTemporal.dayOfWeek); // → 6 が土曜日として取得される
const sundayDateTemporal = Temporal.PlainDate.from("2026-04-12");
console.log(sundayDateTemporal.dayOfWeek); // → 7 が日曜日として取得されるローカライズされた曜日を簡単に取得できる
Dateオブジェクトで曜日を文字列として扱う場合は、曜日の対応表を自前で用意し変換する必要があります。
// 曜日配列作成
const weekDay = ["日", "月", "火", "水", "木", "金", "土"];
const fridayDate = new Date(2026, 3, 10);
console.log(weekDay[fridayDate.getDay()]); // → 金
console.log(weekDay[fridayDate.getDay()] + "曜日"); // → 金曜日また、多言語対応を行う場合は言語ごとに対応表を用意する必要があります。
// 英語の曜日配列作成
const weekDayEn = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const fridayDate = new Date(2026, 3, 10);
console.log(weekDayEn[fridayDate.getDay()]); // → FriTemporalでは toLocaleString() を利用することで、ロケールに応じた曜日表記を簡単に取得できます。
const fridayTemporal = Temporal.PlainDate.from("2026-04-10");
console.log(fridayTemporal.toLocaleString("ja", { weekday: "long" })); // → 金曜日
console.log(fridayTemporal.toLocaleString("ja", { weekday: "short" })); // → 金
console.log(fridayTemporal.toLocaleString("ja", { weekday: "narrow" })); // → 金
英語表記も同様です。
const fridayEnTemporal = Temporal.PlainDate.from("2026-04-10");
console.log(fridayEnTemporal.toLocaleString("en", { weekday: "long" })); // → Friday
console.log(fridayEnTemporal.toLocaleString("en", { weekday: "short" })); // → Fri
console.log(fridayEnTemporal.toLocaleString("en", { weekday: "narrow" })); // → Fロケールを変更するだけで各言語に応じた曜日表記を取得できるため、多言語対応も容易になります。
日付計算・比較が扱いやすいAPIに
Dateオブジェクトでは日時を加減算や比較を行うための処理が用意されていません。
加減算したい場合はsetメソッドを利用して指定し直すという対応になります。
比較に関しても、getTime()で取得した時間での比較や、toDateString()で文字列に変換した上での比較という対応になります。
// 日付を加算したい場合はsetメソッドで対応
const baseDate = new Date(2026, 0, 1);
baseDate.setMonth(baseDate.getMonth() + 2);
baseDate.setDate(baseDate.getDate() + 1);
console.log(baseDate.toLocaleString()); // → "2026/3/2 0:00:00"// 日時の比較
const compareDate1 = new Date(2026, 0, 1);
const compareDate2 = new Date(2026, 0, 1);
console.log(compareDate1 === compareDate2);
// → オブジェクトとしての比較:false
console.log(compareDate1.getTime() === compareDate2.getTime());
// → タイムスタンプに変換:true
console.log(compareDate1.toDateString() === compareDate2.toDateString());
// → 文字列へ変換:trueTemporalでは加減算を行うための add / subtract メソッドや、日付を比較する equals / compare メソッドが用意されており、計算や比較が行いやすくなっています。
// 日付の加算はaddメソッドで加算したい値を指定することで可能
const date4 = Temporal.PlainDate.from("2026-01-01");
console.log(date4.add({ months: 2, days: 1 }).toString());
// → "2026-03-02"
console.log(date4.add({ weeks: 2 }).toString());
// → "2026-01-15" 2週間加算という計算も可能// 日時の比較はequalsを利用することで、変換を行わなくても比較が可能
const compareDate1 = Temporal.PlainDate.from("2026-01-01");
const compareDate2 = Temporal.PlainDate.from({ year: 2026, month: 1, day: 1 });
console.log(compareDate1.equals(compareDate2)); // → true
const compareDate3 = Temporal.PlainDate.from("2026-01-02");
console.log(compareDate1.equals(compareDate3)); // → falseTemporalの重要な考え方
Temporalの核となるコンセプト
Temporalでは wall-clock time と exact time が明確に区別されます。
wall-clock timeとexact timeについては、Temporal におけるタイムゾーンとサマータイム、曖昧性の解決 で次のように説明されています。
wall-clock time:ローカル時間 や clock time とも呼ばれる、タイムゾーンに依存した時刻
exact time:UTC 時間 とも呼ばれる、地球上のどこでも同じ時刻
Dateオブジェクトではこれらの概念が混在しているため、タイムゾーンやサマータイムを考慮した処理が複雑になりがちです。Temporalでは両者を別の概念として扱うことで、日時の意味をより明確に表現できるようになっています。
Temporalオブジェクト間の関係図
下図はプロポーザルドキュメントから引用したオブジェクトの関係図です。
この図で表現されるように、exact time と wall-clock time はAPIで明確に区別されています。
exact time を表現するAPIは Instant、wall-clock time を表現するAPIは Plain から始まるクラス群、両者を併せ持つAPIである ZonedDateTime の3つのAPIにより構成されています。
実際にそれぞれのAPIが文字列表現としてどの部分を意味しているかを表現しているのが、プロポーザルドキュメントから引用した下図になります。
Plain系クラスはオフセットやタイムゾーンの情報を持ちません。
Instant はオフセットを含むISO 8601/RFC 3339形式で表現されますが、タイムゾーンの情報は持ちません。
そして、ZonedDateTime はオフセットとタイムゾーンの両方を含む、完全な日時情報を持つことがわかります。
まとめ
Temporalについて、Dateオブジェクトとの比較を通して、その特徴を紹介しました。
現時点ではNode.js環境での利用にはPolyfillが必要ですが、標準化後は広く普及していくことが期待されます。
本記事ではDateオブジェクトとの基本的な違いを中心に紹介しましたが、実務で扱うにはタイムゾーンや各オブジェクトの役割の理解が重要になってきます、今後の実務での利用を見据えて、早めにキャッチアップしておく価値はあるのではないでしょうか。
参考情報
プロポーザルドキュメント
https://tc39.es/proposal-temporal/
標準化状況
https://github.com/tc39/proposals/blob/main/finished-proposals.md
ブラウザでの利用状況
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Temporal#ブラウザーの互換性
Polyfill
https://www.npmjs.com/package/temporal-polyfill
https://www.npmjs.com/package/@js-temporal/polyfill