Jdk1.5と1.6でTimestampクラスのvalueOfメソッドの挙動が違う件 | ヘビィ・SMD!

ヘビィ・SMD!

そんな・・・バナナ!!
1. 甘え度+10 恐れ度+10 寿命-1週間
2. 甘え度+10 恐れ度-10
3. 甘え度-10 恐れ度-10 寿命+1週間

お客さんに「とある機能が利用できません。」と調査依頼されました。
「○○○○年○○月○○日」のフィールドを 年/月/日 それぞれプルダウンで選択し、
値をポストしても機能が動かないとのこと。
色々とハマってしまい、冷静になって解決できたので議事録として残しておきます。

上記年月日のデータは、システム側でjava.sql.Timestampクラスに変換していました。
以下に簡単なテストクラスを用意します。


import java.sql.Timestamp;

/**
* java.sql.Timestampクラスのバージョンによる振る舞い確認クラス
*
* @author smdbanana
*/

public class TestTimestamp {

public static void main(String[] args) {
try {
// 第一引数を利用してインスタンスをつくる
Timestamp ts = Timestamp.valueOf(args[0]);
// 標準出力
System.out.println(ts.toString());
}
catch (Exception e) {
// とりあえずprintStackTrace
e.printStackTrace();
}
}
}

非常にシンプルなクラスです。
で、java.sql.TimestampクラスのvalueOfメソッドですが、javadocではjdk1.6と1.5でそれぞれ以下のように記述されています。

jdk1.6
例外: IllegalArgumentException - 指定された引数が yyyy-mm-dd hh:mm:ss[.f...] 形式ではない場合

jdk1.5
例外: IllegalArgumentException - 指定された引数が yyyy-mm-dd hh:mm:ss.fffffffff 形式ではない場合

小数点以下の秒数の取り扱いに違いはあれど、それ以外の値は同一のチェックのように見えます。
次にこのクラスを実行してみます。このとき、パラメータは "2011-1-23 12:34:56" とします。


C:\>"C:\Program Files\Java\jdk1.6.0_23\bin\javac.exe" TestTimestamp.java

C:\>"C:\Program Files\Java\jdk1.6.0_23\bin\java.exe" TestTimestamp "2011-1-23 12:34:56"
java.lang.IllegalArgumentException: Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
at java.sql.Timestamp.valueOf(Timestamp.java:194)
at TestTimestamp.main(TestTimestamp.java:8)


C:\>"C:\Program Files\Java\jdk1.5.0_11\bin\javac.exe" TestTimestamp.java

C:\>"C:\Program Files\Java\jdk1.5.0_11\bin\java.exe" TestTimestamp "2011-1-23 12:34:56"
2011-01-23 12:34:56.0



jdk1.6の場合、IllegalArgumentExceptionが出ました。
月の部分がmmのフォーマットになっていないためです。
でも、1.5の場合では実行できています。
1.5のチェックが甘いというか、親切心で補完してくれているとでもいうのか。
いずれにせよドキュメントに書かれた事には違反しています。
(ビルドバージョンまで関係しているかもしれませんが、調べてないよ。)

実はこのお客さん、数年前にサーバを新調し、色々とミドルウェアを新しくしていました。
あまり使わない機能だったため、今まで発覚しなかったようです。

回避策としては「年月日のパラメータ生成は必ずフォーマットと一致するように補完する」とかかい?
いやはや。なんともダサいシステムです。

お客さんに説明しづれぇなぁ。