概要
CSV 取り込みや外部システムとの連携では、日付文字列の形式が統一されていないことが珍しくありません。「2025-03-27」「2025/03/27」「20250327」「令和7年3月27日」のように混在するデータを、ひとつのメソッドで受け付けられるようにしておくと、取り込みロジックが格段にシンプルになります。この記事では、複数の DateTimeFormatter をフォールバックチェーンとして順番に試す方式で、多形式対応のパーサーを実装します。和暦のパースに必要な JapaneseChronology の設定、パース失敗時のエラーハンドリングも含めて整理します。
使いどころ
CSV ファイルの日付カラムが「yyyy-MM-dd」と「yyyy/MM/dd」で混在している場合に統一的にパースする
外部システムから受け取った和暦表記の日付を LocalDate に変換して DB に保存する
ユーザー入力の日付欄で、8桁数字やスラッシュ区切りなど複数の入力形式を許容する
コード例
import java.time.LocalDate;
public class MultiFormatDateParser {
private static final DateTimeFormatter ISO =
DateTimeFormatter.ISO_LOCAL_DATE;
private static final DateTimeFormatter SLASH =
DateTimeFormatter.ofPattern("yyyy/MM/dd");
private static final DateTimeFormatter COMPACT =
DateTimeFormatter.ofPattern("yyyyMMdd");
private static final DateTimeFormatter JAPANESE =
DateTimeFormatter.ofPattern("GGGGy年M月d日")
.withChronology(JapaneseChronology.INSTANCE)
.withLocale(Locale.JAPANESE);
public static LocalDate parse(String input) {
return Stream.of(ISO, SLASH, COMPACT, JAPANESE)
.map(fmt -> tryParse(input, fmt))
.filter(Optional::isPresent)
.findFirst()
.flatMap(opt -> opt)
.orElseThrow(() -> new IllegalArgumentException(
"解析できない日付: " + input));
}
private static Optional<LocalDate> tryParse(
String input, DateTimeFormatter fmt) {
try {
return Optional.of(
LocalDate.from(fmt.parse(input)));
} catch (Exception e) {
return Optional.empty();
}
}
public static void main(String[] args) {
var inputs = new String[]{
"2025-03-27", "2025/03/27",
"20250327", "令和7年3月27日"
};
for (var input : inputs) {
System.out.printf("%-20s -> %s%n",
input, parse(input));
}
}
}Version Coverage
Stream + Optional でフォールバックを関数的に記述できる。var と組み合わせて簡潔に書ける。
// Java 17: Stream + Optional でフォールバック
return Stream.of(ISO, SLASH, COMPACT, ENGLISH, JAPANESE)
.map(fmt -> tryParse(input, fmt))
.filter(Optional::isPresent)
.findFirst()
.flatMap(opt -> opt)
.orElseThrow(() -> new IllegalArgumentException(
"解析不可: " + input));Library Comparison
注意点
フォールバックチェーンの順序が重要。「yyyyMMdd」は「yyyy-MM-dd」のハイフンなし版と誤解されないよう、より厳密な形式を先に試す
和暦パースには JapaneseChronology.INSTANCE と Locale.JAPANESE の両方が必要。片方が欠けると例外になる
DateTimeFormatter.parse() の結果は TemporalAccessor なので、LocalDate.from() で変換する。直接キャストはできない
月や日が1桁の場合(4月1日 vs 04月01日)はパターンの M と MM、d と dd の違いに注意する
FAQ
業務データは不正値を黙って通すとバグの温床になるため、例外で明示的に失敗させるのが安全です。
JapaneseChronology を使えば「令和元年」のパースは自動的に対応されます。フォーマットパターンに GGGG を使います。
例外の生成にコストはありますが、バッチで数万件程度なら実用上の問題にはなりません。大量処理では事前に形式を判定する方法もあります。