AIによる要約
LocalDate.parse(text)は標準でISO形式のyyyy-MM-ddを期待します。2026/08/14や20260814を解析するにはDateTimeFormatterを指定します。形式が合っていても2月30日など存在しない日付はDateTimeParseExceptionです。表示用と入力用のフォーマッター、暦年のuuuuと年のyyyy、厳密な解析、空入力の扱いを分け、外部インターフェースごとに契約を固定します。


画面、CSV、外部APIでは日付の表記が異なります。JavaのLocalDateへ変換する際、見た目が日付でも期待形式と違えばDateTimeParseExceptionになります。
例外を避けるため複数形式を何でも受け付けると、入力ミスまで自動解釈してしまいます。連携仕様では形式を1つに固定し、利用者入力では許可形式とエラーメッセージを明確にします。
この記事のポイント
- LocalDate.parse(text)は標準でyyyy-MM-ddを期待する
- 別形式にはDateTimeFormatterを指定する
- 存在しない日付もDateTimeParseExceptionになる
- 厳密な解析ではuuuuとResolverStyleを確認する
- 空入力と形式不正を別のエラーとして扱う
標準のparseはISO_LOCAL_DATE
Formatterを省略したLocalDate.parseは、DateTimeFormatter.ISO_LOCAL_DATEを使います。代表的な形式は2026-08-14です。
LocalDate ok = LocalDate.parse("2026-08-14");
LocalDate ng = LocalDate.parse("2026/08/14");
// DateTimeParseExceptionスラッシュとハイフンは見た目が似ていますが別文字です。入力値をreplaceして合わせるより、許可形式に対応したFormatterを使います。
DateTimeFormatterで形式を指定する
スラッシュ区切りや区切りなしの日付は、パターンを指定して解析します。Formatterは不変でスレッドセーフなので定数として再利用できます。
private static final DateTimeFormatter SLASH_DATE =
DateTimeFormatter.ofPattern("uuuu/MM/dd");
LocalDate date = LocalDate.parse("2026/08/14", SLASH_DATE);大文字小文字にも意味があります。MMは月、mmは分です。日付でmmを書いて解析できない、または意図しない値になるミスをレビューで確認します。
yyyyとuuuuの違い
yyyyは紀元と組み合わせて使う「年(year-of-era)」、uuuuは紀元をまたいで連続する「通年(year)」を表します。一般的な業務日付をResolverStyle.STRICTで解析する場合は、uuuuを使うと年を単独で確定できます。yyyyだけでは紀元が決まらず、解析に失敗するケースがあります。
DateTimeFormatter strict = new DateTimeFormatterBuilder()
.appendPattern("uuuu/MM/dd")
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
LocalDate date = LocalDate.parse("2026/08/14", strict);案件のJavaバージョンと既存Formatterを確認し、パターン文字を雰囲気で書き換えないでください。和暦を扱う場合はさらに別の暦と仕様が必要です。
存在しない日付は解析できない
形式が正しくても、2026-02-30や平年の2月29日は存在しないため例外です。これは文字列形式ではなく日付妥当性のエラーです。
LocalDate.parse("2026-02-30"); // 例外
LocalDate.parse("2024-02-29"); // 正常
LocalDate.parse("2026-13-01"); // 例外存在日チェックを自前の月別日数if文で書かず、日付APIへ任せます。利用者向けには『形式はyyyy-MM-ddです』と『存在する日付を入力してください』を分けると親切です。
空入力をparseへ渡さない
任意の日付項目ではnullや空文字が来ます。これらをDateTimeParseExceptionとして扱うと、未入力と形式不正のエラーを区別できません。
LocalDate parseOptionalDate(String value) {
if (value == null || value.isBlank()) {
return null;
}
return LocalDate.parse(value, SLASH_DATE);
}内部へnullを返すかOptionalにするか、FormのBindingでLocalDateへ変換するかはプロジェクト方針に合わせます。必須なら未入力段階でエラーにします。
複数形式を受け付ける場合の判断
利用者の手入力で2026/8/4と2026/08/04の両方を許可することはあります。一方、システム間連携で複数形式を許可すると送信側の不具合を隠します。
DateTimeFormatter flexible = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('/')
.appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NOT_NEGATIVE)
.appendLiteral('/')
.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE)
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);何でもtryして成功した形式を採用する実装は、曖昧な値を誤解釈しやすくなります。許可形式を列挙し、優先順位とエラーを決めます。
Springで日付を受け取る場合
Spring MVCでは、FormフィールドをLocalDateにし、@DateTimeFormatで形式を指定できます。変換失敗はBindingResultへ入るため、画面へ入力エラーを返します。
class SearchForm {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate fromDate;
}JSONの@RequestBodyではJacksonの設定や@JsonFormatが関係します。画面FormとJSON APIで同じアノテーションが効くとは限らないため、入口ごとの変換方式を確認します。
JUnitで形式と存在日を確認する
正常形式、区切り違い、桁不足、存在しない日、うるう日、空入力をテストします。外部連携では仕様外形式を拒否することも重要な期待値です。
@ParameterizedTest
@ValueSource(strings = {"2026/02/30", "2026-02-28", "", "2026/13/01"})
void 仕様外または存在しない日付を拒否する(String input) {
assertThrows(InvalidInputException.class,
() -> parser.parseSlashDate(input));
}現場レビューでよくある指摘
日付解析のレビューでは、パターン文字、許可形式、存在日、空入力、画面とAPIの変換経路を確認します。
// レビューコメント例
LocalDate.parse(text)はyyyy-MM-ddを期待します。
入力仕様がyyyy/MM/ddなのでDateTimeFormatterを指定してください。
// レビューコメント例
パターンのmmは分です。月はMMを使い、厳密解析のテストを追加してください。
// レビューコメント例
外部連携の形式は1つに固定されています。複数形式を自動受理すると
送信側の不具合を隠すため、仕様外はエラーにしてください。DateTimeParseExceptionをcatchして現在日へ置き換えるような修正はしません。元の入力、期待形式、入口を確認し、利用者エラーか連携異常かを分けます。
提出前のセルフチェック
レビュー前に確認すること
- 入力形式がyyyy-MM-ddか確認したか
- 別形式へFormatterを指定したか
- MMとmmを間違えていないか
- yyyyとuuuuの用途を確認したか
- 存在しない日付をテストしたか
- 空入力と形式不正を分けたか
- 複数形式を許可する根拠があるか
- Spring FormとJSONの変換経路を区別したか
Javaの日付APIを体系的に学ぶ参考書
LocalDate、DateTimeFormatter、例外の扱いをまとめて確認すると、画面と外部連携で異なる日付契約を安全に実装できます。
スッキリわかるJava入門 実践編 第5版
基礎文法の次に必要な、現場寄りのJava知識を補う。
コレクション、ジェネリクス、ラムダ式、ストリームなど、業務コードで出会いやすい機能を入門編の次に学べます。
- Java基礎の次に何を学ぶか迷っている
- コレクションやStreamを整理したい
当サイトはAmazonアソシエイト・プログラムの参加者です。価格・在庫・配送条件はAmazonでご確認ください。
この記事とあわせて読みたい
まとめ
LocalDate.parse(text)は標準でyyyy-MM-dd形式を期待します。スラッシュ区切りや区切りなしにはDateTimeFormatterを指定し、存在しない日付はDateTimeParseExceptionとして扱います。
空入力、形式不正、存在しない日を分け、画面と外部連携で許可形式を決めます。パターン文字と厳密解析をテストし、失敗時に現在日などへ黙って置き換えないでください。
