JavaScriptの日付型の罠
この記事は「つながる勉強会 Advent Calendar 2022」の12日目の記事です。
こんにちは!
スマレジ テックファームのMichiです!
今回はJavaScriptの日付型でハマった罠についてご紹介します。
やりたいこと
とある勤怠管理システムで、検索の対象範囲として「開始日」と「終了日」を入力できるものとする。
画面から入力された日付の範囲に応じて、表示される勤怠情報を絞り込みたい。
変数の説明
fromDate
... 検索開始日。YYYY-MM-DD形式。例)2022-12-01
toDate
... 検索終了日。YYYY-MM-DD形式。例)2022-12-31
attendances
... 各日付ごとの勤怠情報を格納する配列。配列の各要素はオブジェクトになっており、{targetDate: 'YYYY/MM/DD'}
の形式で対象日付を指定している。 例){targetDate: '2022/12/12'}
コード
// 入力された値をDateオブジェクトに変換する const parsedFromDate = new Date(fromDate) const parsedToDate = new Date(toDate) // targetDateで検索対象のattendanceを絞り込む const targetAttendances = attendances.filter((attendance) => { const targetDate = new Date(attendance.targetDate) return targetDate >= parsedFromDate && targetDate <= parsedToDate })
問題
期待される結果に対して、検索後のデータが1つ足りないときがある。
どうやら、検索開始日に該当する勤怠情報が取得できていないっぽい。
調査
ということで、Dateオブジェクトの中身をすべてconsole.log
して調べてみた。
console.log(parsedFromDate) // 結果:Thu Dec 01 2022 09:00:00 GMT+0900 (GMT+09:00) console.log(parsedToDate) // 結果:Thu Dec 01 2022 09:00:00 GMT+0900 (GMT+09:00) console.log(targetDate); // 結果:Mon Dec 01 2022 00:00:00 GMT+0900 (GMT+09:00) // 結果:Mon Dec 02 2022 00:00:00 GMT+0900 (GMT+09:00) // 結果:Mon Dec 03 2022 00:00:00 GMT+0900 (GMT+09:00) // 結果:...
なんと、画面からの入力値と検索対象のオブジェクトで、日付の時間が違うじゃありませんか!
詳しく調べてみると、こういうことらしい。
-
YYYY/MM/DD
形式 → UTC(世界標準時)。時刻を指定しない場合はすべて00:00:00
となる。 -
YYYY-MM-DD
形式 → ローカル時間。今回はJST(日本標準時)で解釈されたため、UTCからの時差+9時間が加算された。
解決策
つまり、YYYY/MM/DD
形式 と YYYY-MM-DD
形式 を比較していることが根本要因なので、どちらかに統一してやる必要がある。問題はどちらに統一するべきかということだが、MDNのリファレンスにはこんな記載があった。
メモ: Date コンストラクター(および Date.parse と同等)で日付文字列を解釈する際には、常に入力が ISO 8601 形式 (YYYY-MM-DDTHH:mm:ss.sssZ) であることを確認してください。他の形式で解釈した場合には、その挙動は実装によって定義されていて、すべてのブラウザーで動くとは限りません。多数の異なる形式に対応するためには、ライブラリーが役に立ちます。
というわけで、仰せの通りYYYY-MM-DD
形式に統一する。
// 入力された値をDateオブジェクトに変換する const parsedFromDate = new Date(fromDate) const parsedToDate = new Date(toDate) // targetDateで検索対象のattendanceを絞り込む const targetAttendances = attendances.filter((attendance) => { // スラッシュをハイフンに変換してから、Dateオブジェクト化する const targetDate = new Date(attendance.targetDate.replace('/', '-')) return targetDate >= parsedFromDate && targetDate <= parsedToDate })
これで期待通りの結果が抽出できた。
まとめ
今回はJavaScriptの日付型の罠について解説しました。
上の例では対処療法的にreplace
で表示形式を変更しましたが、そもそも設計段階で、APIから返却する値をすべてYYYY-MM-DD
形式しておくのが良いと思います。