Division By Zero

ゼロで割る

うるう秒について少しだけ調べてみた

2012年7月1日(日本時間)にうるう秒が予定されている。

国際機関の決定に基づき、世界で一斉に実施。日本では、7月1日午前8時59分59秒と午前9時の間に「午前8時59分60秒」を入れ、1日を1秒長くする。電波時計などに時刻情報を提供する標準電波などを通じて修正する

http://sankei.jp.msn.com/life/news/120131/trd12013119190010-n1.htm

うるう年のような規則性を持たないことから、システムへの対応などで悩ましい思いをすることもある。特にUNIX時間との関係。

UNIX時間(ユニックスじかん)またはUNIX時刻(ユニックスじこく、UNIX time、POSIX time)とはコンピューターシステム上で日時を表す単位。UTCでの1970年1月1日真夜中(0時0分0秒)からの経過秒数(閏秒を加味しない)で表される。

http://ja.wikipedia.org/wiki/UNIX%E6%99%82%E9%96%93

うるう秒を加味しないということは、簡単な計算でUTCUNIX時間の相互変換が可能だということ。UTCで2012-01-01 00:00:00はUNIX時間で

((42[年] x 365[日/年]) + 10[日]) x 86400[秒/日]=1325376000

となり、この変換に過去に何度うるう秒があったかを加味する必要がない。(10日は1970年1月から2012年1月までのうるう日の数)

便利な反面、

2012-06-30 23:59:59
2012-06-30 23:59:60
2012-06-30 24:00:00

というUTCで記した時間は、UNIX時間では、

1341100799
1341100799
1341100800

となり、gettimeofday()で時間差を求めているアプリケーションでは、うるう秒をまたぐことの考慮が必要だ。また、gettimeofday()はマイクロ秒まで取得できるが、1秒以下の部分では逆転が発生してしまうことが問題を引き起こすかもしれない。以下は、kernel/timer.cのsecond_overflow()の抜粋。time_stateは、NTPサーバーからLIビットを受け取ることで変化する。

	/*
	 * Leap second processing. If in leap-insert state at the end of the
	 * day, the system clock is set back one second; if in leap-delete
	 * state, the system clock is set ahead one second. The microtime()
	 * routine or external clock driver will insure that reported time is
	 * always monotonic. The ugly divides should be replaced.
	 */
	switch (time_state) {
	case TIME_OK:
		if (time_status & STA_INS)
			time_state = TIME_INS;
		else if (time_status & STA_DEL)
			time_state = TIME_DEL;
		break;
	case TIME_INS:
		if (xtime.tv_sec % 86400 == 0) {
			xtime.tv_sec--;
			wall_to_monotonic.tv_sec++;
			/*
			 * The timer interpolator will make time change
			 * gradually instead of an immediate jump by one second
			 */
			time_interpolator_update(-NSEC_PER_SEC);
			time_state = TIME_OOP;
			clock_was_set();
			printk(KERN_NOTICE "Clock: inserting leap second "
					"23:59:60 UTC\n");
		}
		break;

うるう秒についてかつて調べたことを思い出せたので、終了!