日時の妥当性チェックしなくていいの?
ユーザーから送られてきたリクエストボディの日時のフォーマットを正規表現を用いてチェックする機能を追加したときに頂いたコメント。
確かに正規表現だけだと2月30日
をfalse
にすることやうるう年の判断が出来ない。
機能を追加しなければ!と調べた結果を忘れないようにまとめます。
前提
今回のリクエストボディはYYYY/MM/DD HH:mm
の形式で送られてきます。
そのため日付のフォーマットが/
区切りになっているかの判断も重要になってきます。
※今回実装している機能がリクエストボディの日付を外部APIに連携し、外部APIの指定で/
区切りとなっているためリクエスト時点でこのフォーマットにして欲しいためフォーマットも固定にしております。
Moment.isValid()
まず最初に見つけた方法は、moment
パッケージに規定されているisValid関数を用いた方法。
参考にさせていただいたstackOverflow
しかし上記のサイトでも記載があるようにmoment.isValid()
は24:00
でも妥当(true
)と返してしまう。
そのため、2024/01/01 24:00
と01/02 00:00
がどちらもtrue
になるが同じ日時を表すのに複数の表現があるのはよくない。
そのため、moment.isValid()
を呼び出す前にフォーマットチェックと日時チェックを行うように実装。
let result;
const pattern = /^[0-9]{4}\/(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01]) ([01][0-9]|2[0-3]):[0-5][0-9]$/g;
// フォーマットチェック
result = pattern.test(文字列の日付);
// 妥当性チェック
result = moment(文字列の日付, 'YYYY/MM/DD HH:mm', true).isValid();
妥当性チェックの部分でもフォーマットチェックを行っているため冗長。
24:00
の動きを伝えても違う方法を見つけるようにとコメントをいただき再検討。
MomentでMomentオブジェクトに変換して再度文字列に変換した結果の比較
隣の方に相談すると昔こんな感じで実装したよと天の一声。
const result = moment(文字列の日付, 'YYYY/MM/DD HH:mm').format('YYYY/MM/DD HH:mm') === 文字列の日付;
これで動かすと2024/01/01 24:00
はコンストラクタの時点で2024/01/02 00:00
と同じ値に変換され、format
で変換すると2024/01/02 00:00
と表示される!
うるう年も対応してるし、元々の文字列の日付と比較するため元々の日付が2024-01-01 12:00
だったらmoment
で変換した後の日付2024/01/01 12:00
と一致しないためフォーマットチェックもできる!
(momentを使うのは非推奨だけど既存実装で使ってるからええやろ。。。)
もろたで工藤!!
「Moment使うのいまいち」
。。。
次!!(泣)
(MomentはMoment自体が使用しないようにホームページにも載せているため新規実装の場合は選んだらいけなさそう
https://momentjs.com/)
date-and-time.isValid()
最終的に教えていただいたのはdate-and-time
パッケージのisValid()
。
(date-and-time)
実装コードは以下になりました。
const date = require('date-and-time');
const result = data.isValid(文字列の日付, 'YYYY/MM/DD HH:mm');
これで非推奨パッケージを利用せずフォーマットチェックと妥当性チェックを1行で行えるようになりました。
他記事でよく見た実装
今回の対応で調べているときに妥当性チェックとタイトルにあっても実際はフォーマットチェックしかしていなかったり、isNaN
を用いてる実装を見ましたがうまく動くものがなくて困っていたため記事にしてみました。
日々学びがある毎日に感謝しながらも毎回頭を抱える日々笑