Slide 1

Slide 1 text

It’s “Time” to use Temporal #temporal_study Saji (Ryusei Sajiki) / @sajikix

Slide 2

Slide 2 text

Congrats for Stage4 🎉

Slide 3

Slide 3 text

⚠ 注意 ⚠ ● 個人の主張・予測・思想がそれなりに含まれます ○ みんなの意見もぜひ聞きたい ● Temporalの全体像を話すというわけではない ○ 自分が大事だと思ってるところ(好きな所)をひたすら話す感じです ● Temporalの基礎的な話はスキップしてる ○ ほぼ英語版しかないけどMDNがすごく詳しい(読み物としても👍)

Slide 4

Slide 4 text

多分みんなが知ってる ?こと JSのDateがつらい ● 日付処理を書いた人は一度は苦しめられたことでしょう TemporalはDateのつらみを解決する ● Dateがつらすぎて作られた仕様なんでしょう? 長い時を経て最近Stage4になったらしい ● なんか先週あたりタイムラインが盛り上がってたよね

Slide 5

Slide 5 text

復習も兼ねて

Slide 6

Slide 6 text

JSのDateがつらいこと ● システム以外のタイムゾーン(サマータイム)サポートがない ○ UTC ↔ システムタイムゾーン しかできない (しかも変換が暗黙的) ● 日時文字列のパース動作の信頼性が低い ○ 渡す文字列によってタイムゾーン解釈が変わる・そもそも環境差がある ● 日付オブジェクトの可変性の問題 ○ set系のメソッドで書き換えられてしまう ● 日時計算が行いづらい ○ 加減算や比較ですら一旦値を取り出す + Setし直しという工程が発生

Slide 7

Slide 7 text

Temporalで解決すること ● IANAで管理されているタイムゾーン全てに対応 ○ システムのタイムゾーン以外でも自由に変換できる ● パースできる文字列・数値を厳密化 ○ RFC3339 とその拡張であるIXDTFだけに限定 ● immutableなAPI ○ 計算後は新しいTemporalのオブジェクトを返す ● 基礎的な日時計算メソッドの搭載 ○ 日時の加減算や比較などは標準で搭載するようになった

Slide 8

Slide 8 text

…みたいな話はもう結構ある

Slide 9

Slide 9 text

多分みんなが気になってること 私たちのコードベースはどう変わるの? ● 結局本番でどう使っていけばいいのか 別に今でもDate直接触らずライブラリ使ってるじゃん ● どうせライブラリ使う自分たちにどこまで関係があるのか Temporalで全てが解決するんか? ● 日時にまつわる大変さはたくさんあるけれど...

Slide 10

Slide 10 text

It’s “Time” to use Temporal

Slide 11

Slide 11 text

今回のテーマ Temporalで ● 変わること / 変わらないこと ● Scopeじゃないこと / まだできないこと を考えることで Temporal Native 時代の解像度が上がる そこから自ずとどう使っていくべき か、どう移行する かが分かってくる

Slide 12

Slide 12 text

「日時」って曖昧すぎない?

Slide 13

Slide 13 text

“日時”と言う曖昧なものの解体 そもそもみんなが「日時」と気軽に言う”それ”は同じもの? ● 使う場所・コンテキストで微妙に違うものじゃないですか? 以下は全部同じデータ? ● ドキュメントの更新日時 ● タスクの期限日 ● 決算月 ● 毎日のリマインダーの時刻設定

Slide 14

Slide 14 text

あなた(or ユーザー)が「日時」と言った時それはどちらを意味してる? WallClockTime vs ExactTime WallClockTime 特定のロケール・暦における日時の表現 例 ● 2026年3月16日 19時 ● 2026年正月28 (中国暦) ExactTime 世界共通のある一瞬 例 ● 2026-03-16T10:00:00.000Z ● 1773655200000 (Unix Epoch)

Slide 15

Slide 15 text

WallClockTime ↔ ExactTimeの変換にはタイムゾーン(+暦)のデータが不可欠 ExactTime → WallClockTImeは基本決定的 ● 未来の日時以外で言えば一意に決定できる ● (未来は「タイムゾーン自体の変更が...」とか考えだすとややこしい) WallClockTime → ExactTimeは曖昧性を持つ ● サマータイム開始・終了時などで曖昧な期間がどうしても発生する WallClockTime ↔ ExactTime

Slide 16

Slide 16 text

タイムゾーンのデータはExactTimeとWallClockTime の橋渡しで必要 ● 逆にそこの橋渡しをしない場合は必ずしも必要ないなる 暦の指定は WallClockTime を明確化するのに必要 ● 暦が確定できないと WallClockTime で示しているデータの意味が変わる ● 例 : 2026年1月28日 ○ 中国で使われる旧暦の場合 → 2026年3月16日(グレゴリオ暦) タイムゾーンと暦を指定するところ

Slide 17

Slide 17 text

自分たちが「日時」を指定する時、必ず「あるー瞬」を指してるわけではない ● 誕生日 : ローカルなカレンダーにおける「日」自体しか定義していない ● 今月 : 特定のカレンダーの特定の「月」だけを表してる ここでいう「日時」は点だったりだったり期間だったりする 日時は必ずしも「ある一瞬」ではない ある一瞬 月 日

Slide 18

Slide 18 text

Dateのつらさの正体 (の1つ) 日時と言う曖昧な概念を1つのClassで扱うことに限界があった のでは? いろんな意味の「日時」を1つに詰め込んだ歪みとして ● ローカルの計算だけでいいのにタイムゾーンを持ってる ● 時刻は関係ないのに時刻まで内部的に持つ ● etc… そりゃあ、挙動が暗黙的になったり、想定してないものが返るように見える...

Slide 19

Slide 19 text

Temporalによる整理 Temporalは型レベルでこれらの「日時」を厳密に区別 する ● WallClockTime vs ExactTime の分離 ● PlainDateTime以外にもPlain〇〇がそれぞれある これがTemporalのAPI設計における本質であり重要な部分(だと思ってる)

Slide 20

Slide 20 text

Temporalによる整理 WallClockTime vs ExactTime の分離 ● WallClockTIme : Teporal.PlainXX という名前のクラス ● ExactTime : Temporal.Instant これらの2つは Temporal.ZonedDateTimeで橋渡しされ、変換できる

Slide 21

Slide 21 text

Temporalによる整理 PlainXX という命名のClassたち = WallClock Time ● PlainDateTime : 特定の年月日時刻のデータ ● PlainDate : 時刻のデータを持たない日付だけのデータ ● PlainTime : 日付のデータを持たない時刻だけのデータ ● PlainMonthDay : 特定の月と日だけのデータ ● PlainYearMonth : 特定の月(年と月だけ)のデータ

Slide 22

Slide 22 text

型 = 意味 = 振る舞い が違う もちろんデータの型と意味が違うと言うことは ● 生えているプロパティ・メソッドも違う ○ 例 : PlainMonthDay にyearは生えていない ● 生成時や変換時の制約も違う ○ 生成に必要なデータ・補完されるデータもが違う ○ 変換で求められるデータ・補完されるデータが違う

Slide 23

Slide 23 text

データと振る舞いが変わる ↓ 使い方も変わる

Slide 24

Slide 24 text

日時の扱いを考え直すところから 「日時」と言う安易な一言で本来異なるものを一括りにしてなかったか? ● Dateを使ってる限りは「日時? → Dateにしとけ」で済ましていた ○ (で、痛い目を見ていた ) ● 違うものを一緒にしてたらそりゃうまくいかない この考え方を変えない限りTemporalにする旨みは活かせない ● TemporalでどのObject / API を使うべきか選べない

Slide 25

Slide 25 text

もう一度コードを見直そう コードにはいろんなタイプの「日時」が溢れている ● データベースに保存する更新タイムスタンプ ● ユーザーの生年月日 ● カレンダー表示する月のデータ ● 日時入力フォームに入力された日時

Slide 26

Slide 26 text

Temporalだと... それぞれ ● データベースに保存する更新タイムスタンプ → Instant / Now ● 日時入力フォームに入力された日時 → PlainDateTime ● ユーザーの生年月日 → PlainDate ● カレンダーで表示してる月 → PlainYearMonth

Slide 27

Slide 27 text

入力と保持するデータ ユーザーが入力するのは基本WallClockTime (= PlainXX) 保存する時を考えると... ● 一瞬を表しているもの → タイムスタンプで保存したい ○ 大抵 PlainDateTime → ZonendDateTime → timestampに変換でいける ○ 逆もまた然り ● 特定の一瞬を表してない → 別途違うフォーマットで保存する ○ PlainDate / PlainYearMonth → 文字列に変換とかが安全そう

Slide 28

Slide 28 text

よくありそうなデータの変換 ローカルな日時 → タイムスタンプで保存したい const inputDateTime = Temporal.PlainDateTime.from('2026-03-16T10:00:00'); const zonedDateTime = inputDateTime.toZonedDateTime('Asia/Tokyo'); const instant = zonedDateTime.toInstant(); console.log(instant.epochMilliseconds); // 1773622800000

Slide 29

Slide 29 text

よくありそうなデータの変換 タイムスタンプ → ローカルでの解釈 const savedTimestamp = 1773622800000; const instant = Temporal.Instant.fromEpochMilliseconds(savedTimestamp); const zonedDateTime = instant.toZonedDateTimeISO('Asia/Tokyo'); const plainDateTime = zonedDateTime.toPlainDateTime();

Slide 30

Slide 30 text

特定の一瞬じゃないデータの保存 案 : そのまま toStringした値を保存してしまうのが良い? ● 変にInstant化するとタイムゾーンでずれるミスをしたりすることも ● どうせTemporal の fromメソッドで復元できる const birthday = Temporal.PlainDate.from('1988-09-20'); const dbValue = birthday.toString(); console.log(dbValue); // '1988-09-20' const restored = Temporal.PlainDate.from(dbValue);

Slide 31

Slide 31 text

閑話 : 自分のいるチームでは Dateでの値の保存・やり取りを非推奨にしてる (内部的には仕方ないが) 代わりに文字列ベース(+それらを持つObject)のBrandedTypesを導入 ● 基本の4つ : UTCISOString / DateString / TimeString / LocalDateTimeObject ● それぞれ Temporalの Instant / PlainDate / PlainTime / PlainDateTime に対応 常に「この日時データはどれ?」ということを意識させる

Slide 32

Slide 32 text

閑話 : 自分のいるチームでは 詳しくはTS Kaigi Hokuriku で発表したLT資料を見てね!

Slide 33

Slide 33 text

Temporalだけだと 難しいこと

Slide 34

Slide 34 text

有名な日時ライブラリと比べると計算メソッドが抱負とまでは言えない ● addBusinessDays / isWeekend / isSameDay とかは流石にない とはいえ、基本的な計算メソッドはカバーしてる ● add / subtract / since / until … どこまで求めるかになってきそう 計算メソッドは最低限 ?

Slide 35

Slide 35 text

Temporalが受け入れる文字列は厳密にRFC3339/IXDTFとその部分文字列だけ ● 2026-03-16T19:00:00.00000000+09:00[Asia/Tokyo] Dateのように曖昧なParse挙動はなくなった ● Parseできない : “Mon Mar 16 2026 19:00:00”,“2026/3/16 19:00” 逆にいうとParseの機能は限定的になったとも言える ● ややこしいよりは全然いいけど parseはより厳密で限定的に

Slide 36

Slide 36 text

Temporalのformat機能は基本Intlに乗っかっている ● toLocaleStringはIntlをそのまま使ってる Intlのインスタンス生成コストはバカにならないので素直にIntlから使うのが良い ● TemporalサポートでIntlもTemporalの各インスタンスが受け取れる 多く日時ライブラリの持つようなformatPatternによるformatは使えない ● formatPatternによるformat : “yyyy/MM/dd” みたいな文字列でのformat format機能はIntl頼り

Slide 37

Slide 37 text

Temporalの話とはズレるので深入りしないが、日時のformatはとてもややこしい ● 日時表記として / 文法的におかしい形にformatできてしまう可能性 ● ライブラリでformatPattern文字列の仕様あんまり守れてない問題 ● そもそも formatPatternを正しく使うの知識いる問題 ● 参照するデータが現地のユースケースをちゃんと反映できるか問題... ↑ みたいなことを考え出すと本当に面倒なのでIntlで済むならそれが一番 日時のformatって複雑で ...(早口)

Slide 38

Slide 38 text

日時ライブラリは?

Slide 39

Slide 39 text

ライブラリあれば Temporal不要論 a.k.a 「Temporal複雑すぎて直接使うやついないだろ論」 それは本当に「Temporalによる複雑さ」なのか? ● 日時そのものの扱いの難しさだったのでは? ○ TC39が意地悪して使いづらくしてるわけはあるまいて 日時の難しさはTemporalを使わなかろうが一生付き纏う問題 ● 今までDateとライブラリでうまく誤魔化せていただけの可能性がある ○ or 単にそこまで複雑な日時計算してなかった可能性

Slide 40

Slide 40 text

確かにTemporalになくて有名ライブラリにある機能はまだまだある ● parse / format周りの機能 ● 便利な計算メソッド なので「Temporalあれば日時ライブラリ不要!」とまでは言えないかも 一方でTemporalのサポートでライブラリ側も変更は余儀なくされるはず ● = 扱うデータ型によって機能を調整しなくてはいけない問題 日時ライブラリの役割と今後

Slide 41

Slide 41 text

Temporalのインスタンスによって持っている情報に差がある ● eg. PlainDateTimeはTimeZone情報を持たない 単に「Temporalのインスタンスを受け取れる」って言っても... ライブラリの Temporalサポート(想像) Before dateX(date).toUTCString() After dateX(date).toUTCString() Date String Date String PlainDT throw error?

Slide 42

Slide 42 text

メソッドチェーン形式だと大変そう ● dateX(date).setYear(2026).toUTCString() date-fns みたいな関数合成パターン + 各関数の引数の型を制限 / 引数から推論 ● formatUTCISOString(addDays(plainMonthDay,9)) ライブラリの Temporalサポート(想像) PlainMonthDay だったら? TimeZone情報 まだないよ? PlainMonthDayは 渡せないよ!

Slide 43

Slide 43 text

どちらにせよインスタンスによってできる操作が違う ことは意識する必要がある ● → Temporalの知識が無駄になることはないんじゃないか 全部ZonedDateTimeで無理やり持つみたいなことすれば意識しづらいかも ● 一旦の移行戦略としてはある ● 個人的には「なんのためにTemporalにしてるんだ!」と思ってしまうが... ライブラリの Temporalサポート(想像)

Slide 44

Slide 44 text

まだ広く使えると言える(=Baselineに入る)まで時間はある 今のうちから備えられそうなこと ● コードで日時を表してる所を洗い出して「Temporalで言うどれ?」をやる ○ → ついでに無闇にDateでやってるとこを減らせると良い ● RFC3339/タイムスタンプ以外のフォーマットでやり取りしてないか点検 ○ → やめといた方が良い(そんなことしてるとこ少なそうだけど) ● 個人のものとかならPolyfill入れてみて操作感を覚える 今からできること

Slide 45

Slide 45 text

軽量でいい Polyfillがあるらしいよ!

Slide 46

Slide 46 text

● Temporal は今ままで暗黙的だった「日時という何か」を解体し明示的にする ○ Temporalを使いこなすことは暗黙的だった部分を照らし直すこと ● Temporalは日時処理の特効薬ではないし、人によっては苦い薬かもしれない ○ でも使うことで日時の難しさに正しく向き合って良くしていける良薬 ○ Temporalがくる前から向き合わなくてはいけなかった問題でもある ● Temporalだけで解決しないこともあるが、前進はする ● Temporalが来る今こそ、日時ともう一回真剣に向き合う時 ! まとめ

Slide 47

Slide 47 text

It’s “Time” to use Temporal and rethink DateTime !