トップ «前の日記(2009-02-18) 最新 次の日記(2009-02-22)» 編集

Ussy Diary


2009-02-21

[Java]複数の日付パターンに厳密に対応する日付のパース

yyyyMM または yyyyMMdd という文字列形式からでも java.util.Date 型に変換するコードを書きました。年月日のフィールドが結合している場合を想定しています。

元は Commons Lang の DateUtils#parseDate(String str, String[] parsePatterns) です。 SimpleDateFormat クラスはマルチスレッドに対応していないのですが Commons Lang ではメソッド内でインスタンスを生成し、 applyPattern でフォーマットを切り替えることで回避していました。

実装
    public static Date parseDate(String str, String[] parsePatterns) throws ParseException {
        if (str == null || parsePatterns == null) {
            throw new IllegalArgumentException("Date and Patterns must not be null");
        }

        SimpleDateFormat parser = null;
        ParsePosition pos = new ParsePosition(0);
        for (int i = 0; i < parsePatterns.length; i++) {
            String parsePattern = parsePatterns[i];
            if (i == 0) {
                parser = new SimpleDateFormat(parsePattern);
                parser.setLenient(false);
            } else {
                parser.applyPattern(parsePattern);
            }
            pos.setIndex(0);
            Date date = parser.parse(str, pos);
            if (date != null && parsePattern.length() == str.length() && pos.getIndex() == str.length()) {
                return date;
            }
        }

        throw new ParseException("Unable to parse the date: " + str, -1);
    }
呼び出し側
    parseDate("20091", new String[] { "yyyy", "yyyyMM", "yyyyMMdd" });

まず元々のコードに setLenient は入っていません。 setLenient を false にするようにしたのですが、 20091 という文字列を渡すと 2009/1/1 が返ってきます。ここから 200911 を渡すと 2009/11 になり、ユーザーは 1/1 を期待してしまうかもしれません。

これを引数の順番が問題と考えれば、引数の順序を入れ替えればよいのかもしれませんが、個人的にあいまいなルールは混乱を招くと思っているので parsePattern.length() == str.length() の文字列長比較を入れてあげることで厳密にチェックを行うようにし、期待する結果を得ることができるようになりました。

このユーティリティメソッドの前には入力チェックが入ると思うので問題にならないかもしれませんが、コストもかからないのでやる価値はあるかなと思います。

しかしコード長いなあ。