LoginSignup
4
0

More than 5 years have passed since last update.

利用者の誕生日を新規登録/参照する際に、サマータイムに気をつける

Last updated at Posted at 2018-04-11

1. 概要

フロントサイドでミリ秒変換して
バックエンドのAPIに時間の情報を送信する場合、
サマータイムを考慮する必要があります。

Javaのメソッドで変換すればサマータイムを処理してくれますが、
javascriptでは処理してくれないからです。

この記事では、Javaがどのようにサマータイムを処理するか
変換の細かい仕様をお伝えします。

※サマータイムの詳細については以下の記事をご覧ください。
夏時間 - Wikipedia

問題

サマータイム期間中に生まれた人の誕生日が
一日ずれて表示される事象が発生しました。

解決方法

サマータイム期間中は、登録/参照時に1時間分
ミリ秒を足し引きする処理を施す必要があります。

2. 詳細

日本でのサマータイム導入期間は以下です。

開始時刻 終了時刻
1948年(昭和23年) 5月2日(日)午前2時 9月11日(土)午前2時
1949年(昭和24年) 4月3日(日)午前2時 9月10日(土)午前2時
1950年(昭和25年) 5月7日(日)午前2時 9月9日(土)午前2時
1951年(昭和26年) 5月6日(日)午前2時 9月8日(土)午前2時

日時⇒ミリ秒

サマータイム開始

5月2日(月)午前2時になると、サマータイムに突入するため
時間が1時間進められて午前3時とミリ秒が同じになります。

検証用ソース

SummerTime.java
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SummerTime {
    public static void main(String[] args) throws ParseException {
        convertToMilliseconds("1948-05-02 02:00:00");
        convertToMilliseconds("1948-05-02 03:00:00");
    }

    private static void convertToMilliseconds(String dateStr) throws ParseException {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = formatter.parse(dateStr);
        System.out.println(dateStr + " -> " + date.getTime());
    }
}

出力結果

1948-05-02 02:00:00 -> -683794800000
1948-05-02 03:00:00 -> -683794800000

午前2時台の変換は以下のようになっています。

1948-05-02 02:00:00 -> -683794800000
1948-05-02 02:00:01 -> -683794799000
1948-05-02 02:00:02 -> -683794798000
・
・
・
1948-05-02 02:59:58 -> -683791202000
1948-05-02 02:59:59 -> -683791201000
1948-05-02 03:00:00 -> -683794800000

午前2時59分59秒999から午前3時にかけて
補正される仕様がわかりました。
ただ、サマータイム期間中は午前2時台は存在しないため
2時から3時の間のミリ秒変換が使用される想定はありません。

サマータイム終了

9月11日(月)午前1時になると、サマータイムから抜けるため
時間を1時間元に戻さなければなりません。

1948-09-11 00:59:59 -> -672397201000
1948-09-11 01:00:00 -> -672393600000

日時表記では差が1秒ですが、
ミリ秒では補正されて3601000ミリ秒となっています。

ミリ秒⇒日時

サマータイム開始

今度はミリ秒から日時表記に戻してみます。

検証用ソース

SummerTime.java
private static void convertToTimeFormat(Long milliSeconds) throws ParseException {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String dateStr = formatter.format(milliSeconds);
    System.out.println(milliSeconds + " -> " + dateStr);
}

出力結果

-683794801000 -> 1948-05-02 01:59:59
-683794800000 -> 1948-05-02 03:00:00
-683794799000 -> 1948-05-02 03:00:01

サマータイム終了

-672397201000 -> 1948-09-11 00:59:59
-672397200000 -> 1948-09-11 01:00:00
-672397199000 -> 1948-09-11 01:00:01
・
・
・
-672393601000 -> 1948-09-11 01:59:59
-672393600000 -> 1948-09-11 01:00:00
-672393599000 -> 1948-09-11 01:00:01

午前1時台が2回くるような仕様となっています。

3. 対処方法

フロントで変換する際に、該当する期間のミリ秒を補正して
時間の情報をAPIに送るよう修正しました。

dateUtil.ts
    /**
     *サマータイムを考慮した時間を取得(APIからフロントへの表示時に変換)
     */
    export function getSummerTimeDate(birthDate){
        let birth = Number(birthDate)
        if ((birth >= -683794800000 && birth <= -672393600001)
            || (birth >= -654764400000 && birth <= -640944000001)
            || (birth >= -620290800000 && birth <= -609494400001)
            || (birth >= -588841200000 && birth <= -578044800001)) {
            birth += 3600000
        }
        return birth
    }

    /**
     *サマータイムを考慮した時間を代入(フロントからAPIへの登録時に変換)
     */
    export function setSummerTimeDate(birthDate){
        let birth = Number(birthDate)
        if ((birth >= -683791200000 && birth <= -672390000001)
            || (birth >= -654760800000 && birth <= -640940400001)
            || (birth >= -620287200000 && birth <= -609490800001)
            || (birth >= -588837600000 && birth <= -578041200001)) {
            birth -= 3600000
        }
        return birth
    }

4. まとめ

サマータイム導入時期の時間を扱うかどうか、仕様を検討する段階で考慮しておいたほうが良いと思います。
また、タイムゾーンにUTCを使用するなど、回避する方法を別途検討すべきだと思います。

5. 参考

日本のサマータイムとJava
ほんとうにあったサマータイムについて

4
0
1

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
4
0