mktimeのGMT(UTC)版はなぜないのかでは閏秒無視した。これを「さぁ閏秒に対応しよう」と考え始めた途端に「日付時刻システムコール群のダークサイド」に突入する。
正解はおそらく以下しかない:
1 #include <ctime>
2
3 time_t mkgmtime(
4 short year, short mon, short day,
5 short hour=0, short min=0, short sec=0)
6 {
7 time_t knwn = 24*60*60;
8 time_t offset = 0;
9 time_t res = 0;
10 std::tm inp = {0};
11 inp.tm_year = year - 1900;
12 inp.tm_mon = mon - 1;
13 inp.tm_mday = day;
14 inp.tm_hour = hour;
15 inp.tm_min = min;
16 inp.tm_sec = sec;
17 offset = std::mktime(std::localtime(&knwn)) - std::mktime(std::gmtime(&knwn));
18 res = std::mktime(&inp);
19 if (res == -1) {
20 return 0.0; // somewhat that means error
21 }
22 return (double)(res + offset) + msec / 1000.0;
23 }
これはもちろんmktimeのGMT(UTC)版はなぜないのかでの版より確実に遅いし、また、スレッドセーフでない。というかね、この「スレッドセーフでない」というか、グローバル変数を「更新」してしまうのを避けたくてmktimeのGMT(UTC)版はなぜないのかを考えたのだけれどね。
が、/usr/share/zoneinfo とシステムコールに追従しないことには、ダブルスタンダードの time_t が出来上がってしまうことになる。自力でまかなうならば結局 /usr/share/zoneinfo を読み解く必要があり、性能問題はシステムコールと同等になるであろうし、システムワイドな情報アクセスになるので同じようにリエントラント問題に取り組むことになるであろう。だからあげたコードが唯一の正解だ、多分。(といいつついまひとつ自信がない。大丈夫? これ?)
linuxでの閏秒対応の現状、は正直あまり把握できていないが、「システムの保守とともに」あるべきだ。自分の処理だけで頑張ってもダメだし自分の処理だけサボるのもダメ。今年6月末で 24秒違ってくる。シビアなシステムではこれは全然ダメ。
ところで、なんたる偶然というか。Python3.x でも「真面目に閏秒扱いやがれ」という issue が議論されている。