From: Tanaka Akira <akr@...>
Date: 2009-03-28T02:18:08+09:00
Subject: [ruby-dev:38191] big time

思い立って、time_t を越える範囲を Time で扱うことに挑戦して
みました。

http://www.a-k-r.org/tmp/ruby-big-time.patch

内部的には timespec のかわりに VALUE を使ってあります。

% ./ruby -ve 'p Time.utc(5670000000)'  
ruby 1.9.2dev (2009-03-27) [i686-linux]
5670000000-01-01 00:00:00 UTC

% ./ruby -ve 'p Time.now - 12000 * 365 * 24 * 60 * 60'
ruby 1.9.2dev (2009-03-27) [i686-linux]
-9983-03-15 07:44:10 +0900

これを実現するのに問題になるのは、結局、以下の情報をどうやっ
て得るかという点です。これらの情報さえあれば、あとは計算でど
うにかできます。

* システムのタイムゾーンにおける時差
* 閏秒

localtime/gmtime が使えれば、これらの情報を得られるので
すが、time_t の範囲外では使えませんし、範囲内でも使えないこ
とがある、というのが問題です。
(範囲内で使えない例: 64bit time_t では 32bit tm_year が overflow する)

どう対処したかというと、まず閏秒はシステムが知っている範囲だ
けを扱っています。1年後よりも先の閏秒がどうなるかは誰も知ら
ないので、32bit time_t な環境でも 2037年になるまでは、閏秒の
情報はすべて得られます。(もちろん、システムの管理者が閏秒の
情報を使用すると決心して、ちゃんと情報を更新していれば、です
が)

時差は厄介で、特定の時刻におけるタイムゾーンの時差を得る確実
な方法はありません。てきとうに決めています。この処理について
は工夫の余地はあるかもしれません。
(Matz 日記で以前紹介されていた、Perl のはやってません。)

ただ、どうせこれは無理な話なので、プログラムから時差を指定す
る API を用意する方向のほうが適切でしょう。

内部的には UTC からの offset を Time object 毎に保持するデー
タ構造になっているので、API を用意すれば、Time オブジェクト
の時差を自由に設定できるようになります。

あと、グレゴリオ歴しか扱っていないので、いくら過去に遡っても
ユリウス歴にはなりません。(もちろん天保歴にもなりません)

また、Time#subsec を追加してあります。これは内部表現で
Rational を使えるので、nsec では完全な情報を取り出せないから
です。0 <= subsec < 1 の範囲で秒よりも小さな部分を返します。

こうなると DateTime とどう違うのか、という話になる気もします
が、おそらく閏秒が最大の違いになる気がします。実世界の (シス
テムの) 時刻を扱えるという意味では利点であり、n日後とかの計
算を難しくするという意味では欠点でしょうか。

どうでしょう?
-- 
[田中 哲][たなか あきら][Tanaka Akira]