世界を動かす技術を、日本語で。

日付は過去、時間は未来へ

2026年1月13日原文(piccalil.li)

概要

  • JavaScriptのDateオブジェクト は多くの混乱と非直感的な挙動を持つ
  • Dateの設計ミス により、日付・時刻処理が一貫性を欠く
  • 不変性の原則 に反し、Dateはミュータブルなオブジェクト
  • Temporal API の登場で、Dateの問題が解消される見込み
  • 今後は Temporal の利用が推奨される流れ

JavaScriptのDateはなぜ嫌われるのか

  • Dateコンストラクタ は月のみ0始まり、年や日は1始まりという直感に反する仕様
  • 文字列パースの挙動が一貫しない
    • 例: "2026-01-02""2026/01/02"で異なる日付解釈
  • 西暦の扱いが曖昧
    • 49 → 2049年、99 → 1999年、100 → 0100年など
  • 内部的にはUNIXタイムスタンプ (ミリ秒単位)で管理
  • タイムゾーンや夏時間のサポートが貧弱
  • グレゴリオ暦のみ対応、他の暦には非対応
  • JavaScriptのDateはJavaのDateを急いでコピーした産物 であり、設計思想の欠如

Dateの本質的な問題点

  • JavaScriptのプリミティブ値は不変(immutable)
    • 例: 3trueは決して他の値にならない
  • Dateはオブジェクト=ミュータブル(mutable)
    • 例: new Date()で生成したオブジェクトは内部状態を変更可能
  • 参照渡しのため、意図せず値が変わるリスク
    • 例: 関数の引数でDateを渡すと、元の値も変化
  • 「1月1日」という絶対的な日付を不変値で表現できない矛盾
  • 現実世界の「日付」概念とプログラム上の実装の乖離

Dateのミュータブル性によるバグ例

  • 関数でDateを受け取り、日付を加算すると 元のDateも書き換わる
    • 例:
      const today = new Date();
      const addDay = theDate => {
        theDate.setDate(theDate.getDate() + 1);
        return theDate;
      };
      console.log(`Tomorrow: ${addDay(today).toLocaleDateString()}`); // 1/1/2026
      console.log(`Today: ${today.toLocaleDateString()}`); // 1/1/2026(本来は12/31/2025であってほしい)
      
  • 参照のコピー=同じオブジェクトを指す ため、予期せぬ副作用が発生

Dateの終焉とTemporalの登場

  • Dateは今後非推奨(deprecate)予定
    • ただし、完全な削除は難しいため「使わないことが推奨」される状態に
  • Temporal API がDateの後継として標準化
  • Temporalはコンストラクタではなく、名前空間オブジェクト
    • 例: Temporal.NowTemporal.PlainDateなどの静的メソッド群
  • Mathオブジェクトと同様の使い勝手
  • より直感的・一貫性のある日付・時刻操作が可能

Temporalの特徴と今後の展望

  • 不変性(immutable)を重視 した設計
  • 多様なカレンダー体系やタイムゾーンに対応
  • 副作用のない日付・時刻演算が可能
  • サードパーティ製ライブラリ不要な時代へ移行
  • 既存コードの移行には注意が必要 だが、新規開発ではTemporal一択

まとめ

  • JavaScriptのDate は設計上のミスやミュータブル性によるバグの温床
  • Temporal API の登場で、日付・時刻処理の新しい標準が到来
  • 今後はTemporalの利用が推奨 され、Dateの時代は終わりを迎える

Hackerたちの意見

ChatGPTの登場で、MDN Date APIのドキュメントへのトラフィックが半分以下になったんじゃないかと思ってる。

正直言うと、もう二度と考えなくて済むなら嬉しいタイプのものだよね。

ユーザートラフィックが半分に減って、クローラートラフィックが10倍に増えるって感じだね。

Date APIはまあまあ良くて、比較的標準化されてるよ。理解しちゃえば、扱うのはすごく簡単。最大の問題は、単純にタイムゾーンをサポートしてないこと。これがTemporalを使う主な理由なんだよね。

ChatGPTを使って、ほぼすべてのmoment/moment-tzに関する質問に答えてもらいながらスケジューラーを作ったよ。長いAPIドキュメントや回答を探すのが得意なんだよね。

このTemporalポリフィルを使ってるんだけど、今のところめっちゃ良い感じだよ: https://github.com/js-temporal/temporal-polyfill

momentや特にluxonと比べてどうなの?

これ51kbあるから、軽量とは言えないね。https://bundlephobia.com/package/@js-temporal/polyfill@0.5.1 それでも、将来の互換性やサーバー用にはいいけど、小さいアプリには結構影響があるかも。

タイポがあるね: // 32から49の間の数値文字列は2000年代だと仮定される: console.log( new Date( "49" ) ); // 結果: Date Fri Jan 01 2049 00:00:00 GMT-0500 (Eastern Standard Time) // 33から99の間の数値文字列は1900年代だと仮定される: console.log( new Date( "99" ) ); // 結果: Date Fri Jan 01 1999 00:00:00 GMT-0500 (Eastern Standard Time) 2番目の区間は33じゃなくて50から始まるべきだね。

APIをチェックしてみたら、Temporal.Durationには年、月、日、...ナノ秒までの多くのパラメータを持つコンストラクタがあるのに、Temporal.Instantは現在の年/月/日から作成する方法が全くないのが驚きだった。ユニックスタイムスタンプ(または文字列)からしか作れないみたい。それって欲しい機能じゃない?それとも、最初に数字を文字列に変換してからTemporal.Instantに戻すつもりなのかな?

コンストラクタのフィールドでローカル値とUTC値を混同してほしくないんじゃないかな(特にローカル値を使うならDSTの遷移も考慮しなきゃいけないし)。

年/月/日だけじゃ、時間の瞬間を一意に特定するには足りないんだよね。でも、その値を使ってTemporal.PlainDate(Time)を作ることはできるし、必要に応じて他のタイプに変換すればいいよ(例えば、タイムゾーンとか)。

Hacker Newsで議論の続きを見る