「秒は、セシウム 133 原子の基底状態の 2 つの超微細準位(F=4, M=0 および F=3, M=0)の間の遷移に対応する放射の 9 192 631 770 周期の継続時間」原子時計は、この 1 秒を積算することによって、時刻を決定する。
この定義が制定されたのは 1967 年で、 それ以前は地球の運動に基づいた定義だった。
$ date +%s 1136073600とすればよい。 表示された値が UNIX time である。 UTC を UNIX time に変換するには、
$ TZ=GMT perl -e 'use POSIX; printf("%d\n", mktime(0, 0, 0, 1, 0, 106));'
1136073600
$ perl -e 'use POSIX; printf("%d\n", mktime(0, 0, 9, 1, 0, 106));' # こちらは JST から UNIX time に変換
1136073600
とする。
mktime() の引数は、秒、分、時、日、月-1、年-1900 である。
UNIX time から UTC に変換するには、
$ perl -e 'use POSIX; printf(strftime("%Y/%m/%d %H:%M:%S\n", gmtime(1136073600)));'
2006/01/01 00:00:00
$ perl -e 'use POSIX; printf(strftime("%Y/%m/%d %H:%M:%S\n", localtime(1136073600)));' # こちらは UNIX time から JST に変換
2006/01/01 09:00:00
NTP time = (うるう秒を考慮しない UNIX time) + 2208988800となる。(25567 * 24 * 60 * 60 = 2208988800)
UTC が TAI - 10 で開始以来 1998/12/31 23:59:60 まで、 うるう秒調整は 22 回あった(すべて挿入)。 この時点で UTC = TAI - 32。 2005/12/31 23:59:60 のうるう秒挿入後は、 UTC = TAI - 33 となる。
例えば 2006/01/01 09:00:00 (JST) は、 うるう秒考慮なしのシステムでは
$ perl -e 'use POSIX; printf("%d\n", mktime(0, 0, 9, 1, 0, 106));'
1136073600
となり、うるう秒考慮ありのシステムでは、
$ perl -e 'use POSIX; printf("%d\n", mktime(0, 0, 9, 1, 0, 106));'
1136073623
となる。
2006/01/01 JST のうるう秒挿入前後の JST と UNIX time の動きを、 時系列で表すと次のようになる。
| 日付(JST) | UNIX time (うるう秒考慮なし) | UNIX time (うるう秒考慮あり |
| 2006/01/01 08:59:59 | 1136073599 | 1136073621 |
| 2006/01/01 08:59:60 | 1136073600 | 1136073622 |
| 2006/01/01 09:00:00 | 1136073600 | 1136073623 |
| 2006/01/01 09:00:01 | 1136073601 | 1136073624 |
うるう秒考慮のないシステムの場合、 08:59:60 と 09:00:00 で同じ値を返すのが正しい(POSIX 仕様)。 つまりうるう秒挿入直後、UNIX time を 1 秒戻さなければならない (削除の場合は 1 秒進める)。
ではだれが UNIX time を調整するのか? カーネルがうるう秒のスケジュールを知っていて、調整してくれるなら問題ない。 そうでなければ誰かが(NTP を使うなり手作業でやるなり)調整する必要がある。
(例えば FreeBSD のカーネルはうるう秒など知らないし、 FreeBSD の ntpd も、うるう秒操作時に UNIX time を調整してくれない (そもそも NTP time のうるう秒調整すらしない)。 従って、FreeBSD の場合、09:00:00 に、 ntpdate なり date コマンドなりでカーネルの UNIX time を調整する必要がある。)
参考:
うるう秒調整がある場合、 その日(6 月 30 日か、12 月 31 日)の NTP パケットには、 Leap Indicator (LI) が特別な値になる。 通常 LI の値は 00(2進数)だが、 うるう秒挿入の日は 01 (2進数)に、 うるう秒削除の日は 10 (2進数)にセットされる。 これによって、NTP はうるう秒調整を知ることができる。
2006/01/01 JST のうるう秒挿入前後の JST と NTP time の動きを、 時系列で表すと次のようになる。 うるう秒と次の秒で、NTP time が同じ値を示すことに注意!
| 日付(JST) | NTP time | LI |
| 2006/01/01 08:59:59 | 3345062399 | 01 |
| 2006/01/01 08:59:60 | 3345062400 | 01 |
| 2006/01/01 09:00:00 | 3345062400 | 00 |
NTP は、うるう秒調整日には、 パケットの Leap Indicator (LI) をセットすることになっている。 上位の NTP サーバが LI をセットすれば、 下位の NTP サーバは、それを順次伝えて行く。 では最上位 (Stratum 1) の NTP サーバに、うるう秒調整日を教えるのはだれか? 実は、それも各参照クロックドライバにまかされているのである。 そして、LI を設定する参照クロックドライバは、全体の 3 割だけだ。 7 割のドライバは LI をセットしない(うるう秒に対応していない)。 (例えば GPS で一般的に使われている NMEA フォーマット用のドライバは、 LI をセットしない。) また、たとえドライバがうるう秒に対応していたとしても、 接続する機器がうるう秒を正しく処理するかどうかはわからない。
LI がセットされていたとして、 それを理解して、内部に保持する NTP time を調整するのは NTP クライアントの仕事である。 実はStratum 1 の NTP サーバも、 参照クロックドライバから疑似的な NTP パケットを受信する NTP クライアントである。 ところが、ntpd 4.2.0 には LI を処理するコードが含まれていない。 従ってドライバが LI を設定しても、 ntpd の保持する NTP time には、うるう秒調節が行われない。
ドライバが LI を設定しない場合どうなるか。
# cd /usr/src/share/zoneinfo/ # vi leapseconds Leap 1998 の行の下に、以下の行を挿入する。 Leap 2005 Dec 31 23:59:60 + S # make clean # make LEAPSECONDS=yes install # cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtimeとする。 元に戻すには
# cd /usr/src/share/zoneinfo/ # make clean # make install # cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtimeとする。 これは mktime(3), gmtime(3) 等の関数の動作に影響を与えるが、 各コマンドは起動時に /etc/localtime を参照するだけなので、 動作中のコマンドには影響しない。
うるう秒を考慮する /etc/localtime と、考慮しない /etc/localtime をそれぞれ /etc/localtime.with_leap (73 バイト), /etc/localtime.without_leap (257 バイト) などとコピーしておけば、
# cp /etc/localtime.with_leap /etc/localtime (うるう秒を考慮するようにする。) # cp /etc/localtime.without_leap /etc/localtime (うるう秒を考慮しないようにする。)のように簡単に切替えが可能になる。