Dateの問題点5:グレゴリオ暦しか使えない
グレゴリオ暦とは、日本を含む世界中で広く使われている太陽暦です。一般的な暦ですが、Dateオブジェクトはこのグレゴリオ暦しか使えないといった問題点があります。
しかし、世界にはヘブライ暦、中国の旧暦、日本の和暦といった別の暦体系もあります。Dateオブジェクトはこれらをサポートしないので、使いたい場合にはこれらの暦体系とグレゴリオ暦の間で何らかの変換処理を挟まねばなりません。
Temporalでは多様な暦を扱える
一方、Temporalでは、既定であるグレゴリオ暦に加えて、多様な暦を扱うことができます。
以下のリストは、和暦(japanese)を指定してPlainDateオブジェクトを生成し、西暦(ISO 8559形式)で年月日を表示する例です。
const date = Temporal.PlainDate.from({calendar: "japanese",
era: "reiwa", eraYear: 3, month: 6, day: 2});
console.log(date.toString()); // 2021-06-02[u-ca=japanese]
暦を指定するには、fromメソッドに与える日時オブジェクトに、calendarキーでカレンダーを指定します。あるいは、日時文字列に"[u-ca=japanese]"などと指定します。暦の種類は、Intl.supportedValuesOf("calendar")で返される暦の一覧のいずれかを指定します。
以下は、その代表的なものです。
- japanese:日本の和暦
- chinese:中国の旧暦
- dangi:韓国の旧暦
- gregory:グレゴリオ暦
- hebrew:ヘブライの旧暦
- iso8601:ISOカレンダー
暦を指定する場合、eraとeraYearで時代とその中での年を指定します。eraとeraYearはペアであり、どちらかを省略することはできません。
Dateの問題点6:精度がミリ秒単位なので、より高い精度が求められるユースケースには向かない
Dateでは日時をミリ秒までの精度で扱います。一般的な日時表現ではあまり問題になることはありませんが、より高い精度が必要な場合には力不足です。以下のリストでは、Dateオブジェクトをミリ秒を指定して生成し、その後getTimeメソッドでタイムスタンプを取得し、1000による剰余を求めてミリ秒を取得しています。
const date = new Date(2026, 5, 2, 12, 34, 56, 789); const milis = date.getTime() % 1000; console.log(date); // Tue Jun 02 2026 12:34:56 GMT+0900 (Japan Standard Time) console.log(milis); // 789
Temporalではマイクロ秒、ナノ秒の精度で日時情報を扱える
これに対しTemporalでは、マイクロ秒(1/1,000,000秒)どころかナノ秒(1/1,000,000,000秒)の精度で日時情報を扱えます。
以下のリストでは、3回にわたり現在日時を取得していますが、2番目と3番目はミリ秒単位では同一タイムスタンプになるところを、マイクロ秒以下まで保持できることで異なったタイムスタンプと見なすことができます。
const instant = Temporal.Now.instant(); console.log(instant.toString()); // 2026-06-14T03:59:44.783937988Z const zdt = Temporal.Now.zonedDateTimeISO(); console.log(zdt.toString()); // 2026-06-14T12:59:44.787541992+09:00[Asia/Tokyo] const dt = Temporal.Now.plainDateTimeISO(); console.log(dt.toString()); // 2026-06-14T12:59:44.787760986
クラウドでのサービス提供や、分散処理が導入されている場合など、正確な日時演算が求められるケースでは、ナノ秒の精度を持つTemporalが威力を発揮するでしょう。
Dateの問題点7:複雑な日時計算に不向きで、他ライブラリへの依存が増える
Dateは、日時の差を求めるなどの演算機能を提供しません。このため、差分が欲しい、7日後、1時間前などの日時情報が欲しければ、独自に計算のためのコードを組む必要がありました。以下のリストでは、日付を2つ用意して、差の日数を計算しています。
const date1 = new Date('2026-06-02');
const date2 = new Date('2026-07-20');
const diffMs = date2.getTime() - date1.getTime();
const diffDays = diffMs / (1000 * 60 * 60 * 24);
console.log(`差は ${diffDays} 日あります。`); // 差は 48 日あります。
なおDateでは、setDate、setMonthなどのメソッドを使うと、getDateメソッド、getMonthメソッドと組み合わせて○○日後、○○か月後などの計算ができます。
以下のリストでは、現在日時から○○後/○○前を計算しています。
var dt = new Date();
dt.setMonth(dt.getMonth() + 2);
console.log('2ヶ月後: ' + dt.toLocaleString()); // 2ヶ月後: 8/14/2026, 1:45:03 PM
dt.setMonth(dt.getMonth() - 10);
console.log('10ヶ月前: ' + dt.toLocaleString()); // 10ヶ月前: 10/14/2025, 1:45:03 PM
dt.setDate(dt.getDate() + 25);
console.log('25日後: ' + dt.toLocaleString()); // 25日後: 11/8/2025, 1:45:03 PM
dt.setHours(dt.getHours() + 20);
console.log('20時間後: ' + dt.toLocaleString()); // 20時間後: 11/9/2025, 9:45:03 AM
dt.setMinutes(dt.getMinutes() + 59);
console.log('59分後: ' + dt.toLocaleString()); // 59分後: 11/9/2025, 10:44:03 AM
年や日をまたいで計算してくれるのですが、記述が冗長になりがちです。また、○○営業日後といった計算には、さらに複雑なコードが必要です。このため、計算についてはLuxonなどの外部ライブラリを使う方が便利であり、結果的に外部ライブラリへの依存度が高まるといった問題を抱えることになりました。
Temporalでは日時計算が単純化される
Temporalでは、日時の計算がaddメソッド、subtractメソッドなどで単純化されています。それぞれの引数に差分を指定する日時オブジェクトを与えるだけで、必要な計算を行ってくれます。
const date1 = Temporal.PlainDate.from('2026-06-02');
const date2 = Temporal.PlainDate.from('2026-07-20');
const diffDays = date1.until(date2).days;
console.log(`差は ${diffDays} 日あります。`);
let dt = Temporal.Now.plainDateTimeISO();
dt = dt.add({ months: 2 });
console.log('2ヶ月後: ' + dt.toString());
dt = dt.subtract({ months: 10 });
console.log('10ヶ月前: ' + dt.toString());
dt = dt.add({ days: 25 });
console.log('25日後: ' + dt.toString());
dt = dt.add({ hours: 20 });
console.log('20時間後: ' + dt.toString());
dt = dt.add({ minutes: 59 });
console.log('59分後: ' + dt.toString());
加減算の結果、月またぎなどになる場合でも、自動的に補正されるので便利です。
また、Durationクラスを使うと、期間を指定して日時を補正するといったことが簡単にできます。
const duration = Temporal.Duration.from({ months: 1, days: 10, hours: 5, minutes: 30 });
const dtWithDuration = Temporal.Now.plainDateTimeISO().add(duration);
console.log('1ヶ月10日5時間30分後: ' + dtWithDuration.toString());
const durationDiff = Temporal.PlainDate.from('2026-12-31')
.since(Temporal.PlainDate.from('2026-01-01'));
console.log('2026年の期間: ' + durationDiff.toString()); // P364D
最後の例の出力に注目です。P364Dと出力されていますが、これはISO 8601期間形式と呼ばれるものです。
期間を表すフォーマットで、以下の書式となります(便宜上空白を設けていますが、実際には連続した文字列です)。
±P nY nM nW nD T nH nM nS
先頭の±Pは期間を表す接頭辞で、必要に応じて符号を付けることができます。後は、年月日、時分秒を表すのは特に説明は不要でしょう(Wは曜日)。
これを踏まえると、上記の出力は「364日」となります。これは、2026/1/1から2026/12/31の間の日数を意味しますが、これには2026/1/1を含めないためです。
さらに、演算とは少し違いますが、日時を丸めることも可能です。丸めには、roundメソッドを使います。
const str = "2026-06-02T12:34:56.789000000+09:00[Asia/Tokyo]";
const date = Temporal.ZonedDateTime.from(str);
console.log(date.round('hour').toString()); // 2026-06-02T13:00:00+09:00[Asia/Tokyo]
console.log(date.round({ roundingMode: 'floor', smallestUnit: 'hour' }).toString()); // 2026-06-02T12:00:00+09:00[Asia/Tokyo]
console.log(date.round({ roundingMode: 'halfExpand', smallestUnit: 'minute', roundingIncrement: 15 }).toString()); // 2026-06-02T12:30:00+09:00[Asia/Tokyo]
引数のオプションに、roundingModeで丸める方法を、smallestUnitで丸める単位を、roundingIncrementで丸める間隔を指定できます。
このようにTemporalでは外部ライブラリに頼らずに複雑な日時演算が可能です。フロントエンドとバックエンドでJavaScriptコードを共通化することも多い昨今、外部ライブラリへの依存を減らしたいときに、Temporalの多機能さが活きてくるでしょう。
