モーグルとカバとパウダーの日記

モーグルやカバ(EXカービング)山スキー(BC)などがメインの日記でした。今は仕事のコンピュータ系のネタが主になっています。以前はスパム対策関連が多かったのですが最近はディープラーニング関連が多めです。

JavaのSimpleDateFormatで多桁のミリ秒読み込みの謎

CSVで渡されてくるデータの中に、日時データにミリ秒以下のものが付いているものがあり、そのパースをするとなんか読み込んだ時刻が微妙にずれてしまう、という問題が起こりました。


日付が「2015-05-08 00:00:00.123456789」のような形式でわたされるため、

String dateStr = "2015-05-08 00:00:00.123456789";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");
Date date = dateFormat.parse(dateStr);

のようにしてパースさせたのですが、9時間ズレるとかではなく中途半端に時間がずれてしまうのです。


なんでこうなるんだろう?と思ってちょっとテストコードを書いてみました。

out.println(testCalendar("2015-05-08 00:00:00",          "yyyy-MM-dd HH:mm:ss"));
out.println(testCalendar("2015-05-08 00:00:00.123",      "yyyy-MM-dd HH:mm:ss.SSS"));
out.println(testCalendar("2015-05-08 00:00:00.1234",     "yyyy-MM-dd HH:mm:ss.SSSS"));
out.println(testCalendar("2015-05-08 00:00:00.12345",    "yyyy-MM-dd HH:mm:ss.SSSSS"));
out.println(testCalendar("2015-05-08 00:00:00.123456",   "yyyy-MM-dd HH:mm:ss.SSSSSS"));
out.println(testCalendar("2015-05-08 00:00:00.1234567",  "yyyy-MM-dd HH:mm:ss.SSSSSSS"));
out.println(testCalendar("2015-05-08 00:00:00.12345678", "yyyy-MM-dd HH:mm:ss.SSSSSSSS"));
out.println(testCalendar("2015-05-08 00:00:00.123456789","yyyy-MM-dd HH:mm:ss.SSSSSSSSS"));

public String testCalendar(String dateStr, String formatStr) throws ParseException {
    String out = "";
    SimpleDateFormat dateFormat = new SimpleDateFormat(formatStr);
    Date date = dateFormat.parse(dateStr);
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    date = cal.getTime();
    out += "parsed date: " + dateFormat.format(date);
    return out;
}


結果

parsed date: 2015-05-08 00:00:00
parsed date: 2015-05-08 00:00:00.123
parsed date: 2015-05-08 00:00:01.0234
parsed date: 2015-05-08 00:00:12.00345
parsed date: 2015-05-08 00:02:03.000456
parsed date: 2015-05-08 00:20:34.0000567
parsed date: 2015-05-08 03:25:45.00000678
parsed date: 2015-05-09 10:17:36.000000789

ということで、3桁までならば正常にパースされているようです。


ぐぐってみると、下記ページを見てなるほど、となりました。

[Seasar-user:8699] Re: Timestamp型の秒以下の桁を取得するには

TimestampConverter は SimpleDateFormat を使っており,
SimpleDateFormat のパターン文字 S は「ミリ秒」なので
4 桁以上指定しても意味はありません.

ミリ秒だから本来なら3桁までのはずなので、それよりでかい桁については律儀にミリ秒を秒や分、日付に計算しなおしてくれてたのですね。


ここで渡されてくる9桁の「ミリ秒」はそういう意図のデータではないし、まあミリ秒以下のデータは不要であったため、前処理でカットしてパースするようにしました。

自分のように、DBから渡されたデータを、そのまま多桁の「S」を使ったフォーマットで読み込むと、この問題にあたりますので気をつけて。