LoginSignup
1
2

More than 3 years have passed since last update.

Java 日付の厳密なチェックとサマータイムの関係

Posted at

日付の厳密なチェック

DateFormat#setLenient(boolean) は、緩やかな日付チェックに関する設定です。SimpleDateFormatなどで使います。デフォルトはtrueで厳密なチェックをしない。
厳密なチェックをしたい場合は、falseをセットする。1
参考:Open JDK DateFormat#setLenient(boolean), Oracle Java DateFormat#setLenient(boolean)

厳密なチェックをしない場合は、うるう年でないときに2/29としても3/1で解釈してくれます。

サマータイム (daylight saving time)

日本ではあまり馴染みはありませんが、夏の時期だけ1時間早めるというアレ2です。
ニューヨークのある東部時間(EST/EDT)ですと、3月の第2日曜日午前2時から11月の第1日曜日午前2時まで3を夏時間とし、この間を1時間進めます。

2020/03/08 02:00:00がエラーになる

private static void datechange(String date) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    sdf.setLenient(false); // ゆるくない
    sdf.setTimeZone(TimeZone.getTimeZone("US/Eastern")); // EDT
    try {
        Date pdate = sdf.parse(date);
        System.out.println(sdf.format(pdate));
    } catch (Exception e) {
        e.printStackTrace();
    }
}
// 夏時間の開始(1時間進める)
datechange("2020/03/08 01:59:59"); // OK
datechange("2020/03/08 02:00:00"); // NG: ParseException
datechange("2020/03/08 02:00:01"); // NG: ParseException
datechange("2020/03/08 02:59:59"); // NG: ParseException
datechange("2020/03/08 03:00:00"); // OK

// 夏時間の終わり(1時間戻す)
datechange("2020/11/01 01:59:59"); // OK
datechange("2020/11/01 02:00:00"); // OK
datechange("2020/11/01 02:00:01"); // OK
datechange("2020/11/01 02:59:59"); // OK
datechange("2020/11/01 03:00:00"); // OK
java.text.ParseException: Unparseable date: "2020/03/08 02:00:00"
    at java.text.DateFormat.parse(DateFormat.java:357)

厳密なチェックをしているsetLenient(false) かつ、タイムゾーンがEDTの場合、2020/03/08 02:00:00をparseするとjava.text.ParseExceptionが発生します。

EDTでなくても、夏時間を考慮するタイムゾーンで「夏時間になる際にスキップされる1時間」を指定した場合はエラーになります。

つまり、EDTには2020/03/08 02:00:00は存在しないということ。

1時間戻る際の場合はエラーになりません。
今回の話から少しそれますが、1時間戻る場合に日付をユニークキーにしていたら一意制約違反になります。ソートも期待通りになりません。4

根本原因

存在しない時間を指定しているので、問題があるのは上記の仕様ではなく元データと取込処理の方です。
今回の例ですと、元データがEDTではなくJSTだったというのが原因でした。(元データ[JST]と取込処理[EDT]のタイムゾーン不整合)


  1. 厳密でないの反対なのだ(捉え方によってbooleanの値が逆向きになってしまう設計はやめましょう)。緩やかでない。ゆるくない。ガチで。ちなみにJava 8からはDateTimeFormatter列挙型ResolverStyleが使えるようになっています。 

  2. サマータイムだったり、夏時間だったり、デイライト・セービング・タイムだったり、サンマータイムだったり。日本でも昔あったみたいです。地域によっては1時間ではなく30分などの場合もあります。 

  3. 2006年までは4月の第1日曜日午前2時から10月の最終日曜日午前2時でした。法律により変わります。 

  4. 現地時間を文字列でDBに登録しているシステムなどないことを祈ろう。(普通はタイムゾーンの影響がない日付型か、文字列だとしてもUTC、あるいは数値でエポック秒ですね) 

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2