Slide 1

Slide 1 text

本当はややこしい 日時のFormatPattern Nihonbashi.js #11 Saji (Ryusei Sajiki) / @sajikix

Slide 2

Slide 2 text

(主に国際化の文脈で ) 日時のフォーマットは 結構面倒くさい

Slide 3

Slide 3 text

⚠ 今回しない話 󰢃 ● タイムゾーンの話 ○ 時差の変換とかサマータイムとか ● 暦にまつわる話 ○ 非グレゴリウス暦の面倒なところなど ● 人間の可読性特に考えなくて良い or 厳密なもの ○ ISO8601 / RFC3339 とかの話

Slide 4

Slide 4 text

先に結論とその上で話すこと Intl.DateTimeFormatでいいならそれに越したことない ● 今回のトークの結論としては実質これが全て ● このあと話す「めんどくささ」を極力踏まないようにできる Intl.DateTimeFormatを使わないで頑張ろうとした時... ● パンドラの箱は開かれます この辺りについて何が面倒なんだ?という話をします

Slide 5

Slide 5 text

みんながよくやりがち 大体以下の2択になると思ってる ● FormatPattern文字列を使ってフォーマット ● 一から全部自分で組み立てる 一から全部組み立てるのは国際化とかまで考えると大変 ● 使い回しがしづらい / ロケールデータをどこから持ってくる? …etc → なのでFormatPattern文字列を使いがち(今回はこの話)

Slide 6

Slide 6 text

FormatPattern文字列 「YYYY年MM月DD日」みたいなPatternを渡してフォーマットする方法 ● 対応するSymbolのまとまり (YYYYなど) に実際の日時を入れてくれる ● 大抵の日時系ライブラリでサポートされている方式 ● (言語・ライブラリによっては実際の値を渡すものもある) 自由度が高いため、webフロントエンドでも各所で使われているが...

Slide 7

Slide 7 text

問題1: ライブラリ間互換性 ライブラリごとにFormatPatternのルールが微妙に違う JSのライブラリでは大きく2系統ある ● Moment.js / Day.js 系 : YYYY/MM/DD パターン ● date-fns / Luxion 系 : yyyy/MM/dd パターン ただ同じ系統でもそれぞれ微妙に受け入れるsymbolやパターンに差がある → 相互運用性に問題がある

Slide 8

Slide 8 text

どれが正解なのか? (正解はある? ) 一番ちゃんとしていそうな仕様としてUnicodeのLDMLがある ● Table: Date Field Symbol Table ● ※ Unicodeは国際化における多くの仕様やデータセット・ライブラリを持つ date-fns はこのLDMLに準拠することを明言している > Format of the string is based on Unicode Technical Standard #35

Slide 9

Slide 9 text

じゃあ、date-fns 使えば解決?

Slide 10

Slide 10 text

問題2 : 参照するロケールデータの違い そのライブラリがどんなロケールデータ(月名とか)を参照してるか ● 多くのフォーマット機能を持つ日時系ライブラリは独自にデータを持つ ● date-fnsの例 const dayValues = { narrow: ["d", "l", "m", "m", "j", "v", "s"], short: ["do", "lu", "ma", "mi", "ju", "vi", "sá"], abbreviated: ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"], //… }

Slide 11

Slide 11 text

Intl (ICU) の大元はCLDR Unicodeは様々なロケール固有の情報をCLDRという名前で公開・管理してる ● 日時のフォーマット以外にも言語名や数値の形式など... ● 全世界で最も統一的で網羅的なデータセットと言える OSの国際化機能やJSエンジンもライブラリ(ICU)を経てこのCLDRを利用してる → CLDRのデータを利用するのが一番確実と言えそう (ちなみにdate-fnsのデータは結構CLDRとズレてたりする。多分他も...)

Slide 12

Slide 12 text

LDML準拠 + CLDRがデータソース なら解決?

Slide 13

Slide 13 text

問題3 : CLDRにもバージョンがある CLDRは常にupdateされている ● 近年だと4月頃と10月頃にメジャーアップデートがある データ更新がある = フォーマット結果は変わりうる ● (メジャーなロケールではそこまで多くないけど) すごく厳密なことを言い出すと... ● 「OSやBEと参照しているCLDRを揃えたい」みたいなことになる

Slide 14

Slide 14 text

つまり理想的には ... 以下があれば良い ● pattern symbolはLDML準拠 ● ロケールデータのデータソースはCLDR ● ソースのCLDRバージョンは選べる ● (とはいえサイズが大きすぎるのは勘弁....) そんなライブラリあるのか .....? (パッと調べた感じなかなかなさそう )

Slide 15

Slide 15 text

じゃあ、作るっきゃない

Slide 16

Slide 16 text

作ってみた https://github.com/sajikix/datetime-pattern-formatter

Slide 17

Slide 17 text

作ったライブラリの特徴 ● formatPatternはLDMLに完全に準拠 (一部未サポートSymbolあり) ● 参照するのはCLDRそのままのデータ ● ロケールを明示的に指定するのでそこまでバンドルサイズは肥大しない ● 参照するCLDRのバージョンも明示的に指定できる import { en } from "@sajikix/datetime-pattern-formatter/cldr/48_2/en"; format(new Date(), "yyyy-MM-dd", en, { inUTC: true}); // → 2026-07-08

Slide 18

Slide 18 text

これで全て解決?

Slide 19

Slide 19 text

突然ですがクイズです

Slide 20

Slide 20 text

以下の指定は正しい? 例: 「September」のような省略しない表記で月選択のDropDownUIを作った ● 月だから使うシンボルはMで wide形式だから MMMM だな! ● const label = format(date,”MMMM”) ● 英語ではちゃんと 「September」になった!ヨシ 👈

Slide 21

Slide 21 text

A. 月名が格変化する言語がある 日付の一部として表記する時と単体で表記する時で変化する 例 : チェコ語で 1月と1月5日をそれぞれ表記する場合 ● 1月 : leden ● 1月5日 : 5. ledna MMMMの場合は ledna の方になってしまい、好ましくない....

Slide 22

Slide 22 text

月のformatSymbol : MとL このような月の格変化に対応するため月を表すsymbolには ● M : 日付のの中の月名を示す時 ● L : 月名単体を表記する時 (standalone 形式と呼ばれる) の2種類が定義されている。 今回の例の場合 : ● より広いロケールに対応することを見越すならLLLLの方が良い

Slide 23

Slide 23 text

そもそもFormat Pattern自体が難しい そもそも皆さんはFormatPattern文字列をうまく扱えているのか? ● LとMを使い分けられていますか? ● yとYの違いは説明できますか? ● etc… 文法・文化慣習的に不自然なフォーマットを作ることが容易 → 自由度が高い分、こういった部分は自力で使いこなすしかない

Slide 24

Slide 24 text

大いなる力には、 大いなる責任が伴う

Slide 25

Slide 25 text

Intl.DateTimeFormatなら Intl.DateTimeFormat では文法・慣習的に間違ったフォーマットはされづらい ● 今回の例でもちゃんと格変化を踏まえてフォーマットされる Intl.DateTimeFormatの挙動 ● ロケールごとに文法・慣習的に正しいPattern集みたいなものを持ってる ● 渡されたオプションから一番近いものを選択する

Slide 26

Slide 26 text

● よく使われるformatPatternによる日時フォーマットは難点が多い ● 一応公式と言えそうな仕様とメジャーなロケールデータセットはある ○ LDMLとCLDR ● 可能な限りLDMLとCLDRに準拠したフォーマットライブラリを作ってみた ● それでも根底にあるformatPattern自体が難しいという問題は残る ● ここら辺を気にしたくないなら、Intl.DateTimeFormat が安全 ● formatPatternのご利用は計画的に まとめ

Slide 27

Slide 27 text

終わり