JSON等でデータのやりとりをする時には、直接日付型を渡すことができません。文字列で渡す必要があります。
ただし、日付に関しては閏年が存在するため、単純な正規表現では文字列の異常値を検出できません。
今回の記事では、Javaで文字列の日付が存在する日付であることをチェックします。「20220229」は閏年ではないので例外発生することが期待値です。
環境
- Java
- 17
前提
- 日付項目の文字列のフォーマット自体はAPI仕様書等で定義されていること
- yyyyMMdd
- yyyy/MM/dd
- yyyy-MM-dd
- 等々
ユースケース
- JSONやCSVに記載されている日付が存在することをチェックする
対応
日付型のLocalDate
に変換できれば、存在する日付です。
文字列を日付型に変換するため、まずはフォーマットを用意します。DateTimeFormatter
のofPattern
に日付フォーマットを設定します。次にwithResolverStyle(ResolverStyle.STRICT)
を設定します。これにより厳密な変換となり、存在しない日付はLocalDate
に変換できなくなります。
また、withResolverStyle(ResolverStyle.STRICT)
を設定することで、日付フォーマットも厳密なものを要求されます。詳しいことは参考情報を見ていただきたいのですが、Java 8からはyyyy
はuuuu
で設定する必要がありますので、注意してください。
// yyyyMMdd(uuuuMMdd)を厳密に変換する var DATE_FORMATTER = DateTimeFormatter.ofPattern("uuuuMMdd").withResolverStyle(ResolverStyle.STRICT);
用意したフォーマットを使用して、LocalDate
に変換します。ここでLocalDate
に変換できれば日付が存在します。
LocalDate.parse(value, DATE_FORMATTER);
備考
発生するメッセージについて
エラーメッセージを見て気付いたのですが、かなり丁寧なメッセージとなっています。特に「20220229」を渡したときのメッセージが「2022年は閏年ではありません」といったメッセージまで出してくれるのは、非常にわかりやすくてよいですね。
Text '20200230' could not be parsed: Invalid date 'FEBRUARY 30' Text '20220229' could not be parsed: Invalid date 'February 29' as '2022' is not a leap year Text '20220230' could not be parsed: Invalid date 'FEBRUARY 30'
ResolverStyleによる挙動の違いについて
「20220229」を変換したときの挙動を見ていただくと分かりやすいです。
// 動作確認コード @EnumSource(ResolverStyle.class) @ParameterizedTest void test_01(ResolverStyle value) { try { LocalDate uuuuMMdd = LocalDate.parse("20220229", DateTimeFormatter.ofPattern("uuuuMMdd").withResolverStyle(value)); System.out.println(uuuuMMdd); } catch( Exception e){ System.out.println(e); } }
処理結果
ResolverStyle.STRICTの場合 # 定義:Style to resolve dates and times strictly. # 処理結果: java.time.format.DateTimeParseException: Text '20220229' could not be parsed: Invalid date 'February 29' as '2022' is not a leap year # 変換が厳密になる
ResolverStyle.SMARTの場合 # 定義:Style to resolve dates and times in a smart, or intelligent, manner. # 処理結果: 2022-02-28 # いい感じに処理してくれる(期待するのは2月の月末だろうという変換)
ResolverStyle.LENIENTの場合 # 定義:Style to resolve dates and times leniently. # 処理結果 2022-03-01 # 存在する日付(2022-02-28)に対して+-の日付を調整する
ソースコード
終わりに
昔のJavaでもDate
型に変換して存在する日付チェックするのは普通でしたが、Date
はもうDateTimeFormatter
のデフォルトがResolverStyle.SMARTだったせいで、少々調査に時間がかかりました。
DateTimeFormatter#appendStrict
は文字列を厳密にするものでしたし、DateTimeFormatter#toFormatter
はpublicメソッドではないので呼べなかったですし…。
地味に調査がたいへんな割には情報が出てこなかったので、今回ブログとしてまとめられてよかったです。
この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。