/ «2004-10-31 (Sun) ^ 2004-11-17 (Wed)» ?
   西田 亙の本:GNU 開発ツール -- hello.c から a.out が誕生するまで --

Categories Books | Hard | Hardware | Linux | MCU | Misc | Publish | Radio | Repository | Thoughts | Time | UNIX | Writing | プロフィール


2004-11-16 (Tue)

[Time] 時間道、再び --UNIX timeの正体--

The identity of "UNIX epoch"

色々考えた末に、全く新しい観点から「UNIX 入門編」を書いている。世の中には当たり前とされていることが多いけれど、深く考えてみると実は納得がいかないことばかりだ。

UNIX time はそのひとつ。Linux 上の man page は、ctime に関して次のように言っている。

      The ctime(), gmtime() and localtime() functions all take an argument  of
      data type time_t which represents calendar time.  When interpreted as an
      absolute time value, it represents the number of seconds  elapsed  since
      00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).

"UNIX time" が、西暦1970年1月1日の0時を起点(UNIX epoch)にした「経過時間(秒)」であることは、良く知られた事実である。タイムスタンプやオーナーID など、UNIX ファイルのメタデータは、inode 上に記録されるが、Linux の場合、UNIX time は atime/mtime/ctime (この3つの使い分けが、また曲者なのだが)共に、32ビット符号付き整数として取り扱われる。と言ってもこのままでは「絵に描いた餅」に過ぎないから、実際に自分の目で確認してみよう。

$ touch test
$ ls -l test 
-rw-r--r--  1 wataru wataru 0 Nov 17 01:44 test

簡単である。ただ単に、touch コマンドで空のファイルを作成し、そのタイムスタンプを確認しただけである。次に、coreutils パッケージに含まれる stat コマンドに登場願おう。

$ stat test 
  File: `test'
  Size: 0               Blocks: 0          IO Block: 131072 regular empty file
Device: 302h/770d       Inode: 29362       Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/  wataru)   Gid: ( 1000/  wataru)
Access: 2004-11-17 01:44:06.138216672 +0900
Modify: 2004-11-17 01:44:06.138216672 +0900
Change: 2004-11-17 01:44:06.138216672 +0900

察しの良い方は既にお分かりの通り、この情報は inode 上に格納された test ファイルのメタデータを人間様に読みやすい形式で出力したものである。これでは実感が湧かないので、-t (Terse) オプションを指定する。

$ stat -t test
test
0 0 81a4 1000 1000 302 29362 1 0 0 1100623446 1100623446 1100623446 131072

この訳の分からない数値の羅列が「inode」すなわち test の正体である。それぞれの値が一体何を意味するのか?誰しもそう思うだろうが、驚いたことに man 1 stat 中にその答えはない。「ええ加減にせんかい!」とモニターを叱り付けながら、stat.c のソースを紐解くことになる。

その答えは省略するが、数値を見れば容易に予測がつく通り、1100623446 が目指す UNIX time である。根性のある方は、このバイナリー値から時刻表記を導き出してみて欲しい。

UNIX time における2つのまやかし

さて、上記の英語表現の中には、実は2つの「まやかし」が隠されている。1つは、"second" の定義である。古来、数多くの天文学者達がより正確な「秒の定義」を目指して、骨身を削ってきたが、その努力は今も続いている。1秒の定義は時代の変遷と共に、大きく変化しておきており、「1970年の1秒」と「1972年の1秒」は厳密に言えば違うのである。これは 1961年から1971年までが「旧協定世界時 UTC」に基づいているのに対して、1972年以降は「(新)協定世界時 UTC」に基づいているためである。

国際原子時(TAI)という絶対的物差しで見た場合、1972年は1970年よりも約2秒遅れている。よって、UNIX epoch は本来 "TAI - 8 seconds" (国際原子時に遅れること8秒)と表記すべきだと私は思うのだが、世界中どこを探してもそのような記述はない。唯一、あの D. J. Bernstein 氏が次のように述べている

Arthur David Olson's popular time library uses an epoch of 1970-01-01 00:00:10 TAI.

Arthur David Olson 氏は、UNIX おける Time Zone に関して大きな貢献を果たしている一人だが、この "1970-01-01 00:00:10 TAI" という表記は私が下した結論に最も近い。D. J. Bernstein 氏が発表した clockspeed および libtai も含め、さらに深い調査が必要なようだ。「時間道は厳しい」が、時を巡る先人の苦労と知恵を知ることは、何よりも楽しい。

最後に、2つめのまやかしであるが、これは "UNIX time の時系" が明記されていない点にある。"since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC)" という表記を読めば、誰しも UNIX time は UTC に基づく時系だと考えることだろう。私も最初はそう考えた。

しかし、UNIX time と協定世界時 UTC は、全く違う。UTC は閏秒を含むが、UNIX time は閏秒を無視しているからである(POSIX も閏秒は無視すると明記している)。にもかかわらず、UNIX time と UTC が示す時刻は現時点で「一致しているように見える」。

本来両者が一致するはずはないのだが、この矛盾こそが UNIX における時間管理のアキレス腱となっている。Time Zone を適切に設定すれば、glibc は閏秒にも対応可能である。しかしながら、カーネルレベルでは、Linux はもちろん *BSD でさえ、その対応はおぼつかない。「時刻と時間」に関する正しい理解が、今こそ必要なのではないだろうか?