1秒にかける思い


日本時間の2009年1月1日午前8時59分59秒と2009年1月1日午前9時の間にうるう秒が挿入されました。つまり、2009年1月1日午前8時59分59秒の1秒後は2009年1月1日午前8時59分59秒、その1秒後が2009年1月1日午前9時0分0秒になります。

午前9時という平凡な時間だったため大きな混乱はなかったはずですが、グリニッジのような年の変わり目にうるう秒が挿入される地域では、年越しジャンプのタイミングをとるのに失敗して転んでけがをした人がいたとかいないとか。

このような調整が必要になるのは、天体の運行を元に決められる時間と、原子の運動によって決められる時間がずれるためです。

原子の運動によって計る時間の基準は1958年1月1日0時ですが、2つの時間は少しずつずれていて、今日では協定世界時 (UTC) は国際原子時 (TAI)から約33秒遅れています。(参考:国際原子時・協定世界時とうるう秒

計算などの際にはTAIのほうが単純でいいのですが、日々の生活は天体の運行とあっていなければなりませんから、UTCが修正されるのです。相対性理論のことを別にすれば、時間は一定の間隔で刻まれていると思っているかもしれませんが、そう言っていいのはTAIだけなのです。

コンピュータは1970-01-01 00:00:00 +00:00を基準にしたエポック時間(UNIX時間)が用いられますが、POSIX標準のエポック時間はうるう秒を考慮しません。たとえば、みんな大好きMySQLでUNIX時間を調べると、次のようになります。

mysql> SELECT UNIX_TIMESTAMP('2009-01-01 08:59:59');
+---------------------------------------+
| UNIX_TIMESTAMP('2009-01-01 08:59:59') |
+---------------------------------------+
|                            1230767999 |
+---------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT UNIX_TIMESTAMP('2009-01-01 08:59:60');
+---------------------------------------+
| UNIX_TIMESTAMP('2009-01-01 08:59:60') |
+---------------------------------------+
|                                     0 |
+---------------------------------------+
1 row in set, 1 warning (0.01 sec)
mysql> SELECT UNIX_TIMESTAMP('2009-01-01 09:00:00');
+---------------------------------------+
| UNIX_TIMESTAMP('2009-01-01 09:00:00') |
+---------------------------------------+
|                            1230768000 |
+---------------------------------------+
1 row in set (0.00 sec)

UNIX_TIMESTAMP(‘2009-01-01 08:59:60’)の結果は1230768000になるべきなのですが、そうだとしても1230767999 ()と1230768000 (2009-01-01 09:00:00)の差は1(秒)なので、うるう秒の1秒が失われています。(参考:ネットワ-クによる時刻情報提供サービス(NTPサ-ビス)のうるう秒対応

時間を正しく計算したい場合には、D. J. Bernsteinさんのlibtaiを使うのが簡単でしょう。

まず、つぎのように最新のうるう秒のデータを取得します。

cd /etc
sudo wget http://cr.yp.to/libtai/leapsecs.dat
chmod 644 leapsecs.dat

libtaiのソースを展開してmakeしたディレクトリで、次のようなプログラムをビルド(gcc test.c libtai.a)・実行(./a.out)すると、日本時間の2009年1月1日午前8時59分59秒と2009年1月1日午前9時の間隔は、2秒と正しく計算されます。

#include <stdio.h>
#include "tai.h"
#include "caltime.h"

int main()
{
  struct caltime ct1, ct2;
  struct tai tai1, tai2, diff;

  caltime_scan("2009-01-01 08:59:59 +0900", &ct1);
  caltime_scan("2009-01-01 09:00:00 +0900", &ct2);

  caltime_tai(&ct1, &tai1);
  caltime_tai(&ct2, &tai2);

  tai_sub(&diff, &tai2, &tai1);
  printf("%f\n", tai_approx(&diff));
  return 0;
}

参考:時間道、再び –UNIX timeの正体–

【ニコニコ動画】うるう秒のNTT時報

Google内部では閏秒を含まない時刻を採用

うるう秒のあとにMySQLなどのCPU使用率が高騰する件について