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時とミリ秒が同じになります。
検証用ソース
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ミリ秒となっています。
ミリ秒⇒日時
サマータイム開始
今度はミリ秒から日時表記に戻してみます。
検証用ソース
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に送るよう修正しました。
/**
*サマータイムを考慮した時間を取得(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を使用するなど、回避する方法を別途検討すべきだと思います。