PONOS Advent Calendar 2020の13日目の記事です。
昨日は@honeniqさんでした。
サマータイムについて
サマータイム(Daylight Saving Time=DST)といえば以前少し日本でも話題になりましたが、主に日照時間の長い夏の時期に時間を1時間進めるというイメージがあります(私は)。
逆にいえば時間に変動が生じるものであるため、プログラマーとしては本能的に危険を感じるのではないでしょうかw
例えば日本時間を例にしてみます。
日本時間は通常UTC+09:00であり、JST(Japan Standard Time)、つまり日本標準時と呼ばれるものです。
現在の日本にサマータイムはありませんが、1948年から1951年にかけて、日本でもサマータイムが実施されていたこともあるそうです。
サマータイムが適用される場合、**JDT(Japan Daylight-saving Time)**という時刻になり、UTC+10:00ということになるようです。
日本だけでなく、標準時は"S"、サマータイム時は"D"になるというのは世界的に見られるもので、後述のtz databazeからもそれがわかります。
UTC: 2020/12/01 00:00:00
JST: 2020/12/01 09:00:00
JDT: 2020/12/01 10:00:00
※この時刻は例です。実際に日本でこの日時はサマータイムではありません。
つまりUTC自体は当然変更されることはありませんが、タイミングによってJSTが適用されるかJDTが適用されるかで、実際の現地時間が変わると言う考え方です。
プログラムにおいてはUTCを基準に処理していれば時間が逆戻りするということはないということですね。
momentでサマータイムを処理する
プログラムでタイムゾーンを扱いたい場合、大抵ライブラリなどを使用すると思います。
例えばJavaScriptではご存知moment-timezoneを使うととっても日付の扱いが楽になります。
サマータイムも自動的に処理してくれます。
実際に確認してる
太平洋標準時(PST)を例にしてmoment-timezoneで確認してみます。
PSTが適用されるタイムゾーンは米国ロサンゼルスであり、次のサマータイムはPST 2021/03/14 02:00:00からだそうです。
PSTはUTC-08:00であることから、つまりUTC 2021/03/14 10:00:00からサマータイムになることになります。
const moment = require('moment-timezone');
console.log(moment('2021-03-14T09:00:00.000Z').tz("America/Los_Angeles").format());
> 2021-03-14T01:00:00-08:00
console.log(moment('2021-03-14T10:00:00.000Z').tz("America/Los_Angeles").format());
> 2021-03-14T03:00:00-07:00
UTC 2021/03/14 09:00:00の時は2021/03/14 01:00:00であることから、UTC-08:00になっている一方で、
UTC 2021/03/14 10:00:00の時は2021/03/14 03:00:00であることから、UTC-07:00になっており、1時間進んだサマータイムが適用されていることがわかります。
サマータイムなのかどうか
上記のコードでは特に意識することなくサマータイムが反映された時刻が取得されています。
この時間がサマータイムなのかどうなのかを知るためには、moment-timezoneでは**isDST()**関数が用意されています。
console.log(moment('2021-03-14T09:00:00.000Z').tz("America/Los_Angeles").isDST());
> false
console.log(moment('2021-03-14T10:00:00.000Z').tz("America/Los_Angeles").isDST());
> true
この関数の中身の実装は次のようになっていました。
proto.isDST = isDaylightSavingTime;
〜(省略)〜
function isDaylightSavingTime() {
return (
this.utcOffset() > this.clone().month(0).utcOffset() ||
this.utcOffset() > this.clone().month(5).utcOffset()
);
}
現在のUTCオフセットと、1月のオフセットもしくは6月のオフセットを比較し、どちらかのケースで大きければサマータイムということのようです。
これはつまり
- サマータイムの場合はオフセットが大きくなる
- 1月と6月の両方がサマータイムであることはない
という前提に立っているように思えます。
たしかに1月も6月もサマータイムを実施している(一年の半分以上)国はなさそうですね。。。
ではサマータイムが適用されなかった場合の時間はどうやって求めればいいのだろうか?とふと思います。
上記の内容からすれば、少ないほうのUTCオフセットを取得して、それをUTC時間に適用した時間ということにすれば解決しそうです。
サマータイムは1時間進むというわけではない
もっと楽をするなら、**isDST()==trueだったら1時間引くじゃだめなの?**とも思います。
そこでサマータイムのWikipediaを見てみました。
~引用~
『標準時を1時間進める制度またはその進められた時刻のこと。ただし、オーストラリアのロード・ハウ島では夏時間と通常の時間の差が30分であるなど一律ではない。』
でた!例外あるある!
オーストラリアのタイムゾーン
ということでオーストラリアのタイムゾーンを調べてみたところ、大雑把にいうと次のようになっているようです。
- 東部時間(AEST/AEDT)、中部時時間(ACST/ACDT)、西部時間(AWST/AWDT)の3つのタイムゾーンに区分される。
- サマータイムは州ごとに採用ありなしが決められており、これらのタイムゾーンの中でもサマータイム適用ありなしの地区が存在する。
- 中西部時間(UTC+8:45)という、ユークラという一部地域のみ使用されている、公式ではないタイムゾーンが存在する。
- ロード・ハウ時間(UTC+10:30)が存在する。夏時間は+00:30である。
なんとややこしい!うっ・・頭がっ・・
tz databaseを読み解く
中部時間の中でのサマータイムの有無
moment-timezone(や多くのライブラリ)はタイムゾーンを扱うための情報としてtz databaseを使用しています。
その中身を読み解いてオーストラリアのタイムゾーンがどういう設定になっているか実際にみてみたいと思います。
今回は上記のサイトからtzdata2020d.tar.gzをダウンロードしました。
その中にあるたくさんのファイルから、オーストラリアが定義されているaustralasiaを開いてみます。
オーストラリア中部時間に属するダーウィンを具体例にとって見てみます。関係する箇所は下記の部分です。
# Zone NAME STDOFF RULES FORMAT [UNTIL]
# Northern Territory
Zone Australia/Darwin 8:43:20 - LMT 1895 Feb
9:00 - ACST 1899 May
9:30 Aus AC%sT
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
〜(省略)〜
Rule Aus 1943 1944 - Mar lastSun 2:00 0 S
Rule Aus 1943 only - Oct 3 2:00 1:00 D
Zoneから始まる行は、そのタイムゾーンに対する設定になっています。
Ruleから始まる行は、時間の変更がある場合のルールになっており、ZoneにおけるRULESで指定されているルールが適用される関係性のようです。
また、気になるFORMATの部分ですが、AC%sTとなっている部分は、RuleにおけるLETTER/Sの項目が適用されるようです。
ですので、SであればACST(標準時間)、DであればACDT(夏時間)ということになります。
また、SAVEに1:00となっている部分は、標準時間のUTCオフセット(STDOFF)に対して、さらにずれる部分のようです。夏時間は1時間ずれることを意味しています。
また、それぞれのZoneには名前がつけられています。この名前こそがよく見るAsia/Tokyoという部分です。
このような関係性であるため、このZoneにつけられた名称、Australia/Darwinと**オーストラリア中部標準時時(ACST)**とは必ずしも1:1の関係ではないことがわかります。
具体的には、オーストラリアのアデレードもオーストラリア中部時間に属しています。
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Australia/Adelaide 9:14:20 - LMT 1895 Feb
9:00 - ACST 1899 May
9:30 Aus AC%sT 1971
9:30 AS AC%sT
ここで注目なのは適用されているルールが異なる点です。
**[UNTIL]**の設定はそのルールが適用される期限を意味しています。
ダーウィンでは現在は"Aus"が適用されており、アデレードでは現在"AS"という設定が適用されています。
"Aus"の設定は前述の通りですが、FROM、TOを見るとわかる通り、現在適用される変則ルールはありませんのでサマータイムはなさそうです。
一方の"AS"は下記の通りです。
# South Australia
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
〜(省略)〜
Rule AS 2008 max - Apr Sun>=1 2:00s 0 S
Rule AS 2008 max - Oct Sun>=1 2:00s 1:00 D
ここからみるとアデレードでは2008年以降、サマータイムが設定されています。
このようにして、同じオーストラリア中部時間に属する場所でも、サマータイムの適用の有無が変わるようになっているようです。
ユークラという公式ではないが実際に使われているタイムゾーンはどうなっているのか
探してみました。
# Zone NAME STDOFF RULES FORMAT [UNTIL]
〜(省略)〜
Zone Australia/Eucla 8:35:28 - LMT 1895 Dec
8:45 Aus +0845/+0945 1943 Jul
8:45 AW +0845/+0945
FORMATの部分がACSTなどの3種類のオーストラリア時間のいずれでもなく、直接UTCオフセットが書き込まれています。
「どうやら標準時はUTC+08:45であり、夏時間はUTC+09:45っていうタイムゾーンですよ」ってことを表している名前???
むむむ。
ちなみにユークラに適用されているAWルールは次の通りで、
# Western Australia
#
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
〜(省略)〜
Rule AW 2007 2009 - Mar lastSun 2:00s 0 S
Rule AW 2007 2008 - Oct lastSun 2:00s 1:00 D
現在サマータイムはなさそうです。
ロード・ハウ島はどうなっているのか
ここまでくるともう探すだけです。
# Zone NAME STDOFF RULES FORMAT [UNTIL]
〜(省略)〜
Zone Australia/Lord_Howe 10:36:20 - LMT 1895 Feb
10:00 - AEST 1981 Mar
10:30 LH +1030/+1130 1985 Jul
10:30 LH +1030/+11
# Lord Howe Island
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
〜(省略)〜
Rule LH 2008 max - Apr Sun>=1 2:00 0 -
Rule LH 2008 max - Oct Sun>=1 2:00 0:30 -
なるほど、1981年まではAESTだったようですね。
現在は独自の時間になっていて、標準時がUTC+10:30、夏時間がUTC+11:00であることがわかります。
確かにWikipediaにある通り、ロード・ハウ島のサマータイムは30分だけ早くなるようですね。
まとめ
サマータイムだからといって1時間ずれると思ったら大間違いやで!というのと、いかに世界のタイムゾーンが複雑であるかというのを肌で感じました。
日本に住んでいるとタイムゾーンって一つですからねー。
ここまできたらロード・ハウ島にいってサマータイムを実際に感じてみたいですね!
明日は@FW14Bさんです!