SimpleDateFormatで実在日チェックは出来ない

今まで社内のライブラリを使用していたから知らなかったけど、JavaのSimpleDateFormat.parse()で実在日チェックは出来ないのね。

フォーマットが正しくなければParseExceptionがスローされるからOKだと思いがちだけど、以下のようなコードでもParseExceptionはスローされない。

Date date;
DateFormat fmt = new SimpleDateFormat("yyyy/MM/dd");

date = fmt.parse("2010/01/32");
System.out.println(fmt.format(date)); // 2010/02/01

date = fmt.parse("2000/13/-10");
System.out.println(fmt.format(date)); // 2000/12/21

1/32も13/-10もアプリケーション的にはエラーにしたいケースだろうけど、きちんとパースされる。結果をみる限り、内部的に年月日が加減算されている模様。結局、入力された日付をチェックするなら実在日チェック用のメソッドを呼び出す必要がありそうだ。

実在日チェックのコード

ちょっとぐぐってみたら、実在日チェック処理コードは以下のページのものが非常にクールだった。
http://java-house.jp/ml/archive/j-h-b/017541.html

MLのページなのでもし消えてしまうともったいないと思い、自分でも似たようなクラスを書いてみた。

import java.util.Calendar;
import java.util.GregorianCalendar;

public class DateUtil {

    public static boolean isExistsDate(String yyyy, String mm, String dd) {

        try {
            int year = Integer.parseInt(yyyy);
            int month = Integer.parseInt(mm);
            int day = Integer.parseInt(dd);
            return isExistsDate(year, month, day);
        }
        catch (NumberFormatException e) {
            return false;
        }
    }

    public static boolean isExistsDate(int year, int month, int day) {

        Calendar gc = new GregorianCalendar(year, month - 1, day);

        if (gc.get(Calendar.YEAR) != year ||
            gc.get(Calendar.MONTH) != (month - 1) ||
            gc.get(Calendar.DAY_OF_MONTH) != day) {
            return false;
        }
        else {
            return true;
        }
    }
}

基本的な作りは同じで違いといえば

  • 日本の年月日表記なら、引数の月はそのまま(-1せずに)渡せる
  • 実際のアプリケーションで利用しやすいようにString版も用意

くらい。

すばらしいコードを掲示してくださった丸山さんに敬意を払い、このエントリーを終わらせていただきます。