ADR
日付処理の自前実装
外部日付ライブラリを使わず、自前のDate型ラッパーを実装した理由。
コンテキスト
勤怠管理システムでは日付・時刻の処理が頻繁に発生する(シフト、打刻、勤務時間計算等)。JavaScriptの Date 型はミュータブルであり、タイムゾーンの扱いが暗黙的で、バグの温床になりやすい。
検討した選択肢
- 自前実装 (@bi-shop-it/lib/date) —
DateOnly(日付のみ)とDateTime(日時)のブランド型を定義し、薄いラッパー関数を提供する - date-fns — 関数ベースの日付ユーティリティライブラリ
- Day.js / Luxon — オブジェクト指向ベースの日付ライブラリ
決定
外部日付ライブラリは使用せず、@bi-shop-it/lib/date で DateOnly 型と DateTime 型を自前で定義する。内部実装はネイティブの Date と Intl APIを使用する。
自前実装を選んだ理由
- ブランド型による型安全 —
DateOnly(YYYY-MM-DD形式の文字列)とDateTime(YYYY-MM-DDTHH:mm:ss.000Z形式の文字列)をブランド型として定義することで、生のstringやDateとの混同をコンパイル時に防ぐ - タイムゾーンの明示的な扱い — すべての変換関数でタイムゾーンを引数として要求し、暗黙的なローカルタイムゾーン依存を排除する
- 必要最小限の関数セット — 勤怠管理に必要な関数(日数差分、月末計算、日付加算等)のみを提供し、1〜2行で書ける処理はライブラリに含めない
- 外部依存ゼロ — ネイティブAPIのみで実装することで、バンドルサイズへの影響がない。フォーマットは標準の
Intl.DateTimeFormatを使用する - 文字列ベースの設計 — 日付をISO 8601文字列として扱うことで、DBへの保存、APIでの送受信、
===での比較がすべてシンプルになる
影響
Date型の直接使用を禁止する — ESLintルールでDateのグローバル使用を禁止し、@bi-shop-it/lib/dateの使用を強制する- UTCを正とする —
DateTimeは常にUTC(.000Z終端)で保持し、表示時にのみタイムゾーン変換を行う - 追加基準を設ける — 新しい関数の追加は、(1) 複数アプリで必要、(2) 自前実装するとバグを生みやすい(月末、閏年、TZ等)、(3) 1〜2行で書けない、の3条件を満たす場合のみ許可する