サマータイム(夏時間)について勉強する
※整理しきれていないため、修正・加筆予定です。
サマータイム(夏時間、Daylight saving time)は、日本ではあまり馴染みがありませんが、欧米で採用されている仕組みです。
簡単に言うと、地域全体で時計をずらし、太陽が出ている時間帯を有効に使おうというものです。
この仕組み自体はコンピューターの仕組みというわけではなく、地域全体で時計をずらすため、その地域で動作するコンピューターでもその仕組みに合わせて時計を動かさなきゃね、というものになります。
ここにものすごくわかりやすい説明がありましたのでリンクを貼っておきます。
http://gakusyu.shizuoka-c.ed.jp/shakai/jisa/009%20sama-taimutte.htm
さらに詳細を知りたい方はWikipediaなんかを参照してください。
OSの夏時間の設定
プログラム(C/C++)で夏時間を扱う
ここでは年月日や時刻(YYYYMMDD等)、人が通常使う表記のことをISO 8601形式と呼ぶことにします。
UNIX時間からISO 8601形式への変換
注意書きだらけですみません。
UNIX時間(UNIX時刻,POSIX時刻)というのは1970年1月1日午前0時0分0秒からの経過秒数です。 うるう秒は加味せず、8時59分59秒→8時59分60秒といった瞬間も+1されています。
現在時刻から、UNIX時間を算出してくれるWebシステムがありますが、 そのWebシステムが「現在時刻」をローカル時刻とみなすかどうかを意識してください。 例えば https://url-c.com/tc/ こちらのサイトでは、現在時刻がローカル時刻になっています。 https://www.epochconverter.com/ こちらのサイトでは、現在時刻をGMTかLocal timeから選択できるようになっています。 ここを間違えると、日本の場合9時間分がズレてしまいますのでご注意ください。
夏時間がいつから開始(時計が一定時間スキップされます0:59:59→2:00:00)され、 終了(時計が一定時間同じ時間を繰り返します1:59:59→1:00:00)するかは、地域によって異なります。 2019年の「(UTC+00:00) ダブリン、エジンバラ、リスボン、ロンドン」では以下の通り。 開始:2019/03/31(日) 02:00 (OS上は03/31 0:59:59→1秒後 03/31 2:00:00) 終了:2019/10/27(日) 01:00 (OS上は10/27 1:59:59→1秒後 10/27 1:00:00) このあたりから確認できます。 https://www.jisakeisan.com/summertime/london/
以下のようなソースコードで検証を行ってみます。
夏時間が有効な地域かどうかわからないため、夏時間フラグは手動で入力しています。
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <errno.h> void settm(struct tm* ptm, int year, int mon, int day, int hour, int min, int sec, int isdst) { ptm->tm_year = year - 1900; ptm->tm_mon = mon - 1; ptm->tm_mday = day; ptm->tm_hour = hour; ptm->tm_min = min; ptm->tm_sec = sec; ptm->tm_isdst = isdst; } int main() { struct tm tmp = {0}; time_t tt = 0; // TODO:ここでtmpに年月日と時刻を入れます。夏時間が有効であればisdstに1、そうでなければ0を入れます。 // 例 settm(&tmp, 2019, 12, 15, 3, 0, 0, 1); tt = mktime(&tmp); printf("%lld %d/%d/%d %d:%d:%d\n", tt, tmp.tm_year + 1900, tmp.tm_mon + 1, tmp.tm_mday, tmp.tm_hour, tmp.tm_min, tmp.tm_sec); }
自分が検証したかったのは、OSの「自動的に夏時間を調整する」の設定有無と、プログラム上のtm_isdstフラグの組み合わせで、サマータイム開始・終了時間帯を指定した場合どういう結果になるのか?ということです。
今回の入力は「2019/03/31 01:30(UNIX時間:1553995800)」です。
この2019/03/31 01:30は日本時間(ローカル時刻)ではなく、GMTであることを意識してください。
間違えると日本時間はGMTから+9時間のズレがあるので、その分UNIX時間もズレてしまいます。
結果。
# | OSの夏時間設定 | struct tmのtm_isdst | mktimeの結果 | mktimeの結果のISO 8601形式表記 |
① | なし | 0 | 1553995800 | 2019/03/31 01:30:00 |
② | あり | 0 | 1553995800 | 2019/03/31 02:30:00 |
③ | なし | 1 | 1553995800 | 2019/03/31 01:30:00 |
④ | あり | 1 | 1553992200 | 2019/03/31 00:30:00 |
で、なぜこの結果になるのかを考えてみました。
①のケース(OSの夏時間設定:なし tm_isdst:0)
夏時間に合わせて時刻を合わせないし、サマータイム影響中でもない。つまり渡された時刻をそのまま処理する。だから結果は3/31 1:30のままとなる。
②のケース(OSの夏時間設定:あり tm_isdst:0)
このケースは、OSは夏時間に合わせて時刻をずらす気まんまんだが、tm_isdstではサマータイム影響中でないことになっている。
そう言われたらOSはそれを信じるしかないので、純粋に3/31 1:30を処理しようとする。
しかし夏時間としては、3/31 1:30は存在しない時刻なので、夏時間の定義に従って2:30に補正する。
重要なのは見た目の時間が変わっているだけであって、通算秒は変わっていない点。
③のケース(OSの夏時間設定:なし tm_isdst:1)
3/31 1:30はサマータイム影響中と主張しているが、OSとしては夏時間に合わせて時刻をずらさないので、渡されたものをそのまま処理する。夏時間影響であれば本当は存在しない時間だが、OSは夏時間を認識してないので、そのまま表示できる。ということで3/31 1:30のまま処理をしている。
④のケース(OSの夏時間設定:あり tm_isdst:1)
このケースは時刻がすでにサマータイム影響下である。
OSは夏時間に合わせて時刻を調整しなければならないので、3/31 1:30は1時間進められた時間と認識するため、(ここの理解が曖昧です…)まず時間を1時間戻す。
戻した時間が本来の時間であり、ここは実際に1時間前の時刻としてみなされるので、実際にUNIX時間が1時間ずれる。
3/31 0:30は、夏時間としてはそのまま表示できる時間なので、そのまま出力されている。
ちなみに、2019/03/31 2:30(UNIX時間:1553999400)で、上記④のケースを適用してみると、結果はUNIX時間1553995800、ISO 8601形式では2019/03/31 02:30:00だった。
動作としては、まず03/31 2:30が渡される。これはサマータイム影響中であり本当の時間は1時間前である。だから1時間ずらす(3/31 1:30)。実際UNIX時間が戻されている。
3/31 1:30は夏時間としては存在しない時間であり、夏時間表示としては2:30になる。