2 ポイント 投稿者 GN⁺ 2025-01-31 | 1件のコメント | WhatsAppで共有
  • JavaScript の新しい Temporal オブジェクトがブラウザの実験的リリースに入り始めており、日付・時刻・期間・カレンダー処理を組み込み API でより正確かつ一貫して扱おうとする変化である
  • 既存の Date は、ユーザーのローカル時刻と UTC を中心にした構造、不安定なパース、mutable な動作のため、タイムゾーン処理とデバッグが難しかった
  • Temporal は Temporal.Instant, Temporal.ZonedDateTime, Temporal.PlainDateTime, Temporal.Duration, Temporal.Now などによって、時点・ローカル時刻・期間を分離し、200 以上のユーティリティメソッドを提供する
  • 安定したクロスブラウザ対応まではまだ時間が必要で、Firefox Nightly では javascript.options.experimental.temporal 設定の背後で Temporal を試せる
  • MDN には今週 270 ページ以上の Temporal ドキュメントが追加され、@js-temporal/polyfill がある TC39 のドキュメントページではフラグ変更なしで例を実行できる

JavaScript Date の長年の限界

  • JavaScript が 1995 年に作られたとき、Date オブジェクトは Java の初期 java.util.Date 実装をコピーしたもので、Java は 1997 年にこれを置き換えたが、JavaScript はほぼ 30 年間同じ API を維持してきた
  • 最大の問題は タイムゾーン対応の不足 と不安定なパース動作である
    • ユーザーのローカル時刻と UTC しかサポートしない
    • Date 自体が mutable なので、追跡しにくいバグを生む可能性がある
    • Daylight Saving Time(DST) や歴史的なカレンダー変更をまたぐ計算も扱いにくい
  • こうした制約のため、多くの開発者はアプリケーションの日付・時刻処理に Moment.jsdate-fns のような専用ライブラリに依存してきた

Temporal が変える日付・時刻モデル

  • Temporal は Date オブジェクトの 完全な代替 として設計されており、日付と時刻の管理をより信頼でき、予測可能にすることを目指している
  • タイムゾーンとカレンダー表現をサポートし、変換・比較・計算・フォーマットのための組み込みメソッドを提供する
  • API 表面には 200 を超えるユーティリティメソッドがあり、MDN の Temporal ドキュメント に説明と例が追加された

Temporal の主要な型

  • Temporal は 固有の時点、地域の壁時計時刻、期間をそれぞれ別の型として区別する
  • 主な構造は次のとおり
    • Temporal.Duration: 2 つの時点の差
    • Temporal.Instant: タイムスタンプで表した固有の時点
    • Temporal.ZonedDateTime: タイムゾーンを含む日付・時刻
    • Temporal.PlainDateTime: タイムゾーンのない完全な日付・時刻
      • Temporal.PlainDate: 日付のみを表現
      • Temporal.PlainYearMonth: 年と月
      • Temporal.PlainMonthDay: 月と日
      • Temporal.PlainTime: 時刻のみを表現
    • Temporal.Now: 現在時刻を複数のクラスインスタンスや特定の形式で取得する API

例で見る使い方

  • 現在の日付と時刻を ISO 文字列で取得でき、複数のメソッドに タイムゾーン を渡せる
    • Temporal.Now.plainDateTimeISO() はシステムタイムゾーンの現在の日付・時刻を返す
    • Temporal.Now.plainDateTimeISO("America/New_York") は New York タイムゾーンの現在の日付・時刻を返す
  • Gregorian 以外のカレンダーも扱え、Hebrew、Chinese、Islamic のようなカレンダー体系の日付を作成できる
    • Chinese カレンダーの M01 1 日を使って次の Chinese New Year を計算する
    • 執筆時点の例の出力では、次の Chinese New Year は 1/29/2025 である
  • Unix Epoch ミリ秒タイムスタンプを Temporal.Instant.fromEpochMilliseconds() で時点に変換し、Temporal.Now.instant() と比較して残り時間を計算できる
    • 例のタイムスタンプ 1851222399924 について、smallestUnit: "hour" で現在からの時間を計算する
    • @js-temporal/polyfill の出力は 31,600 hr、Firefox Nightly の出力は PT31600H である
  • Firefox 実装の toLocaleString は現在 ロケール依存の文字列 を出力しない
    • この挙動は技術的な限界というより、設計上の判断に近い
    • polyfill と Firefox 実装は今後収束する可能性がある
  • compare() メソッドは配列のソートに活用できる
    • durations.sort(Temporal.Duration.compare) を使うと、PT1H, PT1H30M, PT1H45M, PT2H の順にソートされる

ブラウザ対応と試す方法

1件のコメント

 
GN⁺ 2025-01-31
Hacker News の意見
  • Temporal は素晴らしい。しばらく polyfill を本番環境で使ってきたが、従来の Date() API で遭遇していた多くの問題を解決してくれる
    Rust の chrono、Java の Joda Time のような優れた日時ライブラリに着想を得て、使いやすい API にうまくまとめた感じだ
    単純な時刻、時点、タイムゾーン付き時刻を分けているので少し複雑ではあるが、時間そのものが複雑なので、開発者はその複雑さに向き合う必要がある
    「このタイムスタンプに 1 日を足す」といった処理でも、特定のタイムゾーン基準なのかを必ず決めなければならず、そうしないと夏時間やユーザーのタイムゾーン変更、サーバーのタイムゾーン差によってバグが残る
    固定オフセットのタイムスタンプ(2025-01-01T00:00+02:00)と特定のタイムゾーン(Europe/Paris)のシリアライズ上の違いも解決してくれる

    • Rust クレートの Jiff も見る価値がある。Temporal に着想を得ており、chrono を含む他の日付・時刻クレートとの比較もまとめている: https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
      個人的には、Temporal が Rust の chrono から大きく影響を受けたとは見ておらず、Joda や最近の java.time、そして date-fns や Moment.js のような JavaScript の日付・時刻ライブラリからの影響のほうが大きいように見える
    • ある程度年齢を重ねた人たちは、本番環境で現地時間の 01:00〜03:00 の間に cron ジョブを予約してはいけないということを昔から学んでいる
    • その通りだが、ひとつ障害がある。実際の場所を指すタイムゾーン名を使うのを嫌がる人が多い
      Europe/Paris がフランスの広い地域を指すのかパリだけを意味するのか調べようとせず、自分がパリに住んでいると言っているように感じて苛立つ
      オンラインコミュニティのイベントなどを予約するとき、この点はつらかった。人々は固定オフセットを使いたがり、CET/CEST のような表現は許容するが、Europe/yourexactcoordinates のようなものは嫌がる
      実際にはほとんどのタイムゾーンがかなり広い地域だということは分かっているが、プライバシーを気にしつつも「時刻」と「タイムゾーン」の違いを調べる気はない人が多い
    • chrono と Joda Time は別の時代のものだ。Rust 1.0 が出たとき、Java はすでに標準ライブラリの java.time でこのアプローチを実装しており、この用途にサードパーティライブラリは不要だった
    • 固定オフセットのタイムスタンプと特定のタイムゾーンのタイムスタンプのシリアライズ上の違いがどんな問題なのか、もっと詳しく知りたい
  • ドキュメントで見つけるのにかなり時間がかかったが、Temporal.ZonedDateTime.prototype.withTimeZone() があるタイムゾーンから別のタイムゾーンへ変換してくれる: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
    Temporal.ZonedDateTime.from("2021-08-01T12:00[America/New_York]") で作った値を withTimeZone("Europe/Paris") に渡すと、2021-08-01T18:00:00+02:00[Europe/Paris] になる
    タイムゾーン変換と期間の扱いはライブラリが必ず扱うべき中核機能だが、Temporal がその両方を扱ってくれるのはよい。ただし Temporal.Duration には、duration.format('H\hmm\m') のように 1h03m にするカスタム format 関数はなさそうだ

    • DurationFormatter 提案は、どの単位をどの省略レベルでフォーマットするかを制御できるようにしてくれる。たとえば 1 yr3 hours3m のような形だ: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
      icu4x ライブラリに一部実装したことがある
  • 現在 TC39 には熟しつつある提案がかなり多く、Temporal は早くリリースされてほしいものの一つ
    主要ブラウザが新機能をサポートするには、v8、JSC、SpiderMonkey のエンジニアたちの開発努力が必要だということは分かっているので、進展を生み出している人たちには本当に感謝している
    せっかちな立場としては「Records と Tuples も早くやって! Pipe 構文戦争は飛ばしてもいいから!」と応援したくなる

    • Records と Tuples はリリースされる可能性が低いか、少なくとも人々が望む等価性セマンティクスでリリースされる可能性は非常に低そうに見える
      ブラウザベンダーは、この時点で新しいプリミティブ型を追加することに、すでにかなり強く反対していると理解している。等価性がどう動作すべきかという難点は、このスレッドを見ると説明がある: https://github.com/tc39/proposal-record-tuple/issues/387
      BigInt が当初ここまで多く使われるとは期待していなかったが、実際には 構造的等価性 を持つコレクションが必要になる場面が何度も出てきて惜しく感じる
      新しいプリミティブ型の追加への反対がパターンマッチングにも影響していると理解しているが、その理由はよく分からず、その提案はあまり追えていない
    • Temporal が来るのは良いし、他にも良い機能は多いだろうが、言及された特定の提案については楽観しにくい
      パイプライン、パターンマッチング、Records+Tuples はどれも 4 年以上作業中で、まだステージ 1〜2 にとどまっている。この 1 年で意味のある進展は、パターンマッチング程度を除けばほとんどなさそうに見える
      Records と Tuples のリポジトリの issue によると、値セマンティクス、つまり === が動作し得るかについて反発があった。値セマンティクスを抜くと、提案の有用性は大きく下がる
      これらがステージ 3 に到達するには少なくともさらに 1〜2 年かかるか、そもそも到達できないかもしれない。それでも配列のグループ化関数はできた
    • 欲しいのは パターンマッチング: https://tc39.es/proposal-pattern-matching/
      まだステージ 1 なので、悲しいことにあと数年はかかりそう
    • Records ということなら、Dictionary が ECMA5 の提案だったのか、それとも AS3 の独特な機能だったのか気になる
      AS3 の Dictionary は、オブジェクト参照、文字列、数値を一意なキーとして使い、任意の型の値を紐づけられる型だった。ディクショナリオブジェクト自体が生きている間は弱参照にならないよう、ガベージコレクションも処理されていた
      JavaScript の Set を思い浮かべつつ、指定したどんな種類のキーでも強い型で使い、コンパイル時にエラーを投げる形だった。あの言語が懐かしい
    • 主要ブラウザ制作者だけの問題ではない。ES6 が出たばかりのころ JavaScript 推論エンジンを作っていて、ES5 はかなりサポートしていたが、構文アップデートが押し寄せてきて諦めざるを得なかった
      そのアップデート群が事実上 JavaScript エコシステムを壊し、今では JS にコンパイルされる言語だけが残ったように感じる。いまだにバニラ JS を使っている人は、アセンブリでアプリを作っている人のようだ
  • Temporal の中で各タイムゾーンデータがどのように最新に保たれるのか気になる
    TC39 がどこかでデータを更新し、各ブラウザがそのデータを内部にコピーして新バージョンを出す構造なのか、ユーザーが新しいデータの入ったブラウザへ更新していなければ、自分のウェブサイトで誤った時刻を見ることになるのか気になる
    例えばメキシコは 2022 年に 夏時間 を廃止した: https://en.wikipedia.org/wiki/Time_in_Mexico
    pytzmoment-timezone のようなサードパーティライブラリならサーバー側ライブラリを更新すればよく、標準ライブラリで処理するなら言語バージョンを上げればよいが、Temporal と訪問者のブラウザではどうなるのか分からない

    • 提案文書の “Time Zones and Resolving Ambiguity” を読んでみると、IANA タイムゾーンデータベース を使うとだけあり、いつ更新するのかは書かれていない: https://tc39.es/proposal-temporal/docs/timezone.html
      ユーザーのコンピュータもこのデータベースにアクセスでき、更新を処理しそうだが確信はない。ブラウザが別途このデータベースをダウンロードして維持することもあり得るし、実装によって異なりそう
      関連データベース: https://en.wikipedia.org/wiki/Tz_database
    • ブラウザに組み込まれる機能なので、おそらく ブラウザ更新 の際に更新されるのだと思う。ブラウザベンダーは高速な更新のために多くの作業をしてきており、平均的なユーザーのコンピュータではブラウザが最も最新に保たれているソフトウェアである可能性が高い
    • ブラウザはすでに Intl ヘルパーのためにロケールも最新に保つ必要があるので、そちらを調べれば似た答えが得られる可能性が高い
    • Unix 系システム、つまり Linux ディストリビューション、macOS、Android にはシステムレベルの IANA Time Zone Database のコピーがある。ブラウザがそれを使うのか独自のコピーを積むのかはよく分からないが、Temporal は確実に IANA tzdb を使う
      ユーザーが新しいタイムゾーンデータを反映するようブラウザやシステムを更新していなければ、誤った時刻を見る可能性があるというのは概ね正しそう。ただし、これは現在の一般的な慣行と大きく異なるものではない
      代替案は、すべてのアプリケーションが独自の tzdb を積むことだが、一部のアプリはそうしており、正当な場合もある。しかし一般的には無駄であり、アプリケーション間の不一致を増やし、更新周期もより遅くなる可能性が高い
    • 実装とシステムによって異なるだろう。実装は最終的に タイムゾーンプロバイダー を定義する必要があり、それはエンジン実装が Temporal に提供するホスト/システムフックから提供される可能性が高い
  • 誰かがタイムゾーンを変更したときに検知するイベントを追加する必要がある
    「プログラマーが時間について信じている誤り」のリストに、「プログラマーは使用中にタイムゾーンが固定されていると信じている」も入れられる。現実には毎日何百万人もの人がタイムゾーンを移動している

    • そういうイベントは、人をフィンガープリント追跡し、位置を推定するための別のデータポイントとして使われそう
    • このイベントがどこで使われるのかよく分からない。時間にある程度依存する大半のWebアプリケーションにとって、影響を受けるユーザー数を考えたとき、直接このイベントを購読して反応するほどの価値があるかは疑問
      もちろんライブラリには役立つかもしれない
    • クライアントのタイムゾーンはクライアントにとってだけ重要であるべき。タイムスタンプをユーザーの現地時刻で表示する利便性以外に、クライアントの時計が重要になる条件はない。ユーザーがタイムゾーンを変えても気にする理由はない
    • タイムゾーン変更を追跡するには状態が必要なので、開発者がどのように追跡して反応するかを定義するほうが正しそう。Temporalがその処理方法に意見を持つことは望まないかもしれない
    • 飛行機やISSにいると、そもそもタイムゾーンがない状態かもしれない
  • .Nowが関数呼び出しではない点が非常に残念。プロパティであるべきではない
    ループ内で.Nowを複数回評価すると、毎回異なる値が出る可能性があり、これは予想外
    C#も同じ選択をしたが、そうすべきではなかった: https://ericlippert.com/2014/05/19/when-should-i-write-a-pro...

    • 誤解しているように思う。Nowは実際の値を取得する関数群の名前空間で、たとえばNow.instant()のような形
  • Temporal提案は本当に気に入っているが、1つ引っかかる点がある。比較で参照同一性に依存していること
    つまりTemporal.Instant.from('2020-01-01') != Temporal.Instant.from('2020-01-01')になる
    本質的に悪いわけではないが、これらのオブジェクトをMapのキーにしたりSetに集めたりする能力を事実上なくしてしまう。なぜそう決めたのかは分かるが、できないのは残念
    いつかRecordsとTuplesが実際に出てくれば、それを基盤にしたバージョンが生まれるかもしれない

    • JavaScriptの非プリミティブ型の中で、==で参照ではない同等性を持つものはある?
    • 状況によっては、.epochMillisecondsMap/Setのキーや要素として使えそう。ただしMapでタイムゾーンデータの保持やInstantの追加演算が必要なら、値側に元のInstantを保持する必要がある
      Setの場合はMap{v.epochMilliseconds:v}のようにしてInstantを保持できる
      素晴らしいわけではないが、これはTemporalよりもJavaScript自体を責めるべき問題。ランタイムに処理させることもできるだろうが、そうするとポリフィルが失敗する
    • 仮にできたとしても、こういう値をMap/Setで使うのは浮動小数点数をMap/Setで使うのに似ていて、普通は悪い考え
    • JavaScriptでは不可能だからでは? Javaの定数文字列プールのように、使われるすべてのオブジェクトが全体のオブジェクトプール内の値への参照になるようにする奇妙なハックを除けば難しい
    • 単にString()'2020-01-01'を使えばいいのでは?
  • 既存APIと一緒に動作する比較関数を提供するのは堅実な設計。新しい型を追加するときの必須条件にしてもいいくらい
    たとえばTemporal.Duration.comparesortに渡せば、PT1HPT1H30MPT1H45MPT2Hの順にソートできる。設計者たちに拍手を送りたい

  • Temporalという名前がMomentやDatetimeのような一般的な時間オブジェクトとの衝突を避ける意図なのは分かるが、名前はいまいち
    初めて見たときはガベージコレクション制御のようなものかと思った

    • temporalには、永遠性と対比される時間に関するもの、文法上の時制や時間区分に関するもの、空間と区別される時間に関するもの、という意味がある: https://www.merriam-webster.com/dictionary/temporal
      悪くない名前に聞こえる
    • ブラウザ内のワークフローエンジンTemporalのことかと思った
    • すでに下された決定について自転車置き場の色を議論しているようなものだが、本当に悪い名前だという点には同意する
      まずTemporalは名詞ではなく形容詞。時間に関係はあるが、直感的にしっくりこない
      より重要なのは、古いコードとの衝突確率を下げるために変な名前を選ぶと、時間が経つほど言語がより不透明になるという点
      PromiseGeneratorを追加したときも、そのオブジェクト名を使うライブラリは多かった。予約語と違って組み込みオブジェクトは上書きできるので、大きな問題ではなかった
      標準委員会は言語の長期的な健全性のために、ぶれずに**Time**を使うべきだと思う
      もちろんこの議論はもう過ぎたことで、ブラウザベンダーとJSエンジン実装者たちは合意しているのだろう
    • 私もひどい名前だと思う。むしろDateV2のほうがまし
      後方互換性が必要なのは分かるが、Temporalは響きがいまいち。DateTimeはJS標準にある名前でもないのに、なぜそれを使わなかったのか分からない。それまではconst DateTime = Temporalとしておくつもり
  • 関連記事:
    JavaScript Temporal Is Coming - https://news.ycombinator.com/item?id=42809834 - 2025年1月、コメント18件
    Mozilla: Temporal (Limited Availability) - https://news.ycombinator.com/item?id=42776548 - 2025年1月、コメント1件
    Is It Time for the JavaScript Temporal API? - https://news.ycombinator.com/item?id=29712118 - 2021年12月、コメント100件
    Temporal: Getting started with JavaScript's new date time API - https://news.ycombinator.com/item?id=27661667 - 2021年6月、コメント194件
    JavaScript Temporal - https://news.ycombinator.com/item?id=27395236 - 2021年6月、コメント1件
    JavaScript Proposal Temporal for Dates is now stage 3 - https://news.ycombinator.com/item?id=26432346 - 2021年3月、コメント1件

    • 最近のWeb APIが本当に素晴らしくなってきたのはありがたいが、この話題の最初の投稿がほぼ4年前の2021年3月だったというのは、やはり残念
      大きな船は舵を切るのに時間がかかる、という言葉がまさに当てはまる