ページ

2006年7月24日月曜日

Re: 日本における時差のシナリオに関してご意見をお聞かせください

日本における時差のシナリオに関してご意見をお聞かせください より


■ 最初に私が遭遇したことがあるシナリオを、、、
以前に (今も?) 世界中の株価や為替を扱うシステムに関わったことがあります。データベンダーから取得する株価や為替といったデータの日時は内部的には GMT (UTC) で表現されていました。しかし、こういったデータは現地時間も重要なファクターなので、人が参照するときには現地時間に変換してやる必要があります。もちろん、単に時差を変換するだけではなくサマータイムも考慮してやる必要があります。たとえばニューヨーク証券取引所 (NYSE) の取引時間は現地時間で 9:30~16:00 です。これは日本時間の 23:30~06:00 になりますが、ニューヨークがサマータイム期間中の場合は 22:30~05:00 になります。サマータイムを考慮しないと一時間もずれることになるので大事です。また、過去数年間分のチャートを描いたりもしますので、今年だけではなく過去のサマータイムのルール情報も必要です。データベンダーより過去 10年分とか 20年分とかのデータを購入して追加するといったことも考えられるのでそれなりの期間のサマータイムのルール情報が必要となります。


■ 続いてタイムゾーンとサマータイムについて
普通にタイムゾーンとか時差とかと言うと 「日本の JST は GMT に対して +9時間」 とか 「アメリカの EST は GMT に対して -5時間」 とかといった感じでかなり単純です。変換も単純に加減算するだけです。(ちなみに、タイムゾーンは 「GMT に対して -2:30」 とか分刻みのところもあります。1時間単位のみというわけではありません)
しかし、サマータイムはこんなに単純ではありません。たとえば、今のアメリカのサマータイムは 「4月の第一日曜日の午前 2時に時計を 1時間進め、10月の最終日曜日の午前 2時に時計を 1時間戻す」 というルールになっています。これだけの情報があればサマータイムも考慮した時差の変換ができます。しかし、現実には



  • アメリカやカナダなどは州・自治体ごとにサマータイムを導入するかどうかを決められるらしい。

  • アメリカでは 2007年からサマータイムの開始日が 3月の第二日曜日に変わるらしい。

  • アメリカの最近 30年ほどだけを見てもサマータイムの開始日は、1967~1973 は 4月の最終日曜日、1974 は 1/6、1975 は 2/23、1976~1986 は 4月の最終日曜日、1987~ は 4月の第一日曜日、と変わってきている (あってるかどうか確認はしてませんので注意)。

のように、サマータイムのルールは地域ごとにも変わりますし、年ごとにも変わっていきます。ある日時を GMT とローカルタイムとで変換したい場合は、該当地域の該当する年のサマータイムのルールの情報がないと変換することができません。また、サマータイムのルールが変更されることもありますし、サマータイム自体が導入されたり廃止されたりもします。そのため、サマータイムのルール情報もその都度更新する必要があります。


■ Windows でのサマータイム
Windows では GetTimeZoneInformation API を使ってタイムゾーンの情報を取得することができます。ただし、これはコントロールパネルで設定されているタイムゾーンの情報を取得できるだけで、任意のタイムゾーンの情報が取得できるわけではありません。また、サマータイムのルール情報も含まれてはいますが、これも今現在のルール情報だけで過去のサマータイムのルール情報などはありません。
ちなみに、タイムゾーンの情報はレジストリの HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zones に入っているようです。ここの TZI というのはバイナリですが、中身は

    struct {
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
};


と TIME_ZONE_INFORMATION 構造体の一部をそのまま書き出しただけみたいです。
いずれにしろ、上に書いたようなシナリオでは任意の地域の任意の年のサマータイムのルール情報が必要になりますから、Windows の持っている情報はまったく使い物になりません。


■ tzdata
Unix では tzdata というタイムゾーンデータが使われているようです。
ftp://elsie.nci.nih.gov/pub/
こちらで配布されています。
この ftp サイトを見ると今だと tzdata2006g.tar.gz というファイルがありますが、これをダウンロードして解凍すると northamerica といった拡張子なしのファイルが出てきます。これはテキストファイルですので適当なテキストエディタで見ることができます。見てみると多数のコメントと共に様々な地域のタイムゾーンとサマータイムのルール情報が記述されていることがわかると思います。また、asia ファイルの Japan のところを見ると 1948~1951 の間に日本にもサマータイムがあったことも記述されています。
このように tzdata は地域別・年別のタイムゾーン・サマータイムのルール情報がわかっている範囲ですべて格納されているデータベースとなっています。


tzcode の方にソースが含まれているのですが zic を使うと tzdata をテキストからバイナリに変換することができます。そして、これも tzcode にソースが含まれていますが tzdata 対応版の localtime を使うとサマータイムを厳密に考慮した時差の変換ができます (実際には zic でバイナリ化した tzdata が使われます。これはパフォーマンスなどのためじゃないかと思います)。上述のようにサマータイムを考慮しようとすると JST、EST といった大雑把なゾーンの指定では足りません。なので、tzdata では Asia/Tokyo、America/New_York というような名称で指定するようになっています。アメリカのインディアナ州などでは自治体ごとにサマータイムの対応が異なったそうですが、これもちゃんと America/Indianapolis、America/Indiana/Indianapolis、America/Indiana/Marengo、America/Indiana/Knox、America/Indiana/Vevay といった名称で指定できるようになっています。


実際に tzdata 対応 localtime で "1974/01/05 09:00:00" を GMT から America/New_York に変換すると "1974/01/05 04:00:00" になります。"1974/01/06 09:00:00" を GMT から America/New_York に変換すると "1974/01/06 05:00:00" になります。これはアメリカのサマータイムは 1974年だけは 1月 6日の午前 2時から始まっているからです。
このように tzdata を使うと世界中の地域の過去から未来までの日時を正しくサマータイムを考慮しつつ変換することができます。確かソースは含まれていなかったように思いますが、反対に現地時間を GMT に変換する関数も実装できるでしょうし (というかしました)、それらがあればあるローカルタイムから他のローカルタイムに変換することも可能になります。
ちなみに tzdata にはうるう秒の情報も入っているようです。


ところで、tzdata は tzdata2006g.tar.gz といったファイル名になっていますが、これは 2006年にリリースされた g 番目のデータということを意味します。更新があると末尾のアルファベットがインクリメントされていきます (次は tzdata2006h )。あらたなサマータイムのルール情報が追加されるだけではなく、過去の情報が修正されることもあるようです。たとえば 「内戦状態にある時はよくわからなかったけど、あとで調べてみたら実はその国ではサマータイムが導入されていた」 なんてこともあったりするようです。


また、http://www.w3.org/TR/timezone/ こことかにも tzdata のことは出てきたりします ("Olson time zone database" としても知られる [tzinfo] 、といった感じで書かれてます)。なので、tzdata 自体は結構メジャーな存在なのだと思いますが、標準化という意味で tzdata がどのような位置づけにあるのかなどは私はまったく知りません。たとえば、Asia/Tokyo といった表記法は RFC などにまとめられているのか、それとも tzdata 独自のオレオレ仕様なのか、とかそういったことはよくわかりません。


■ ディベロッパー製品開発統括部 Blog に答えてみる
と、前置きが異常に長くなっちゃいましたが(^^; トラックバック先の 「日本における時差のシナリオに関してご意見をお聞かせください」 に答えてみます。


まず、「歴史的経緯からサマータイムのルールが年毎に変わっていたもののサポート」 というのは、上記の tzdata がサポートしているような年ごとのサマータイムの状況をサポートするということだと理解しました。この場合に気になるのは、この場合 "JST"、"EST" というような表記法では範囲が広すぎて役に立たないことです。上述したようにアメリカやカナダなど州・自治体でサマータイムのルールが違うことも多くあるため、tzdata のような Asia/Tokyo、America/New_York、America/Indiana/Marengo といった表記法が必要になると思います。ちなみに、ニューヨークもインディアナ州も EST (EDT) みたいです。しかし、インディアナ州は自治体ごとにサマータイムを導入するかどうかが違ったため 「EST を使っているところの 1980年のサマータイムの開始日は?」 と聞かれても答えは出ません。「インディアナ州 Marengo の」 というところまで地域を限定してやる必要があります。tzdata の America/Indiana/Marengo という表記法ならばこのようなものにも対応できます。


> 1. ユーザー設定のタイム ゾーンの作成(時間帯を、既定でコンピュータ上にあるもの以外に、プログラム的に作成すること)
> 2. ユーザー設定のタイム ゾーンを恒久的にコンピュータ上に書き戻す


ここでいうタイムゾーンとはサマータイムのルール情報も含んだものでしょうか?そうであれば、サマータイムのルール情報に更新があった場合にたとえば Windows Update / Microsoft Update などを通じて速やかにデータが更新されないと困ったことになる可能性があります。しかし、現実にはマイクロソフト社側で即座に対応するのは難しい場合もあると思いますので、独自にタイムゾーンを作成できたほうが望ましいと思います。また、北海道サマータイム に対応した Asia/Hokkaido タイムゾーンを作りたいというような人もいるかもしれません。


> 3. サマー タイム設定を切った状態でタイム ゾーンのコピーの作成


これは 「ほんとうはサマータイムが実施されているけど、実施されていないと仮定した場合のタイムゾーンを作成する」 ということでしょうか?そうであれば、あまり必要ないような気がします。


> 4. サマー タイムのため、時計の時刻を前後した際のローカル タイムが未確定、もしくは曖昧/重複にあるかどうかを問い合わせる関数


いちおう確認させて頂くと、たとえば、アメリカのサマータイム開始日は午前 2時に時計が 1時間進むので午前 2時は存在しないことになります。サマータイム終了日は午前 2時に時計が 1時間戻るので午前 1時台が 2回あることになります。このような 「存在しない時刻」 と 「2回同じ時刻があるため曖昧になる時刻」 を判定する関数ということでいいでしょうか?
このような関数が必要になる場面は少ないかもしれませんが、あったほうがいいように思います。
また、ローカルタイムから GMT に変換する関数がある場合は、この関数に 「存在しない時刻」 や 「曖昧な時刻」 を渡した場合の挙動も明確にする必要があると思います。個人的には例外でこけるのではなく、それなりの時刻に変換してもらったほうがいいように思います (曖昧な場合はより小さい時刻として扱う、などのルール付けは必要ですが)。


> 5. 歴史的経緯によって基本になる差分が年毎に変わったタイムゾーン(前述の夏時刻法に対する実装が関係してきます)


サマータイムにきちんと対応する場合はとうぜん必要です。というか、対応して欲しいです。


> 6. 稼動中のコンピューターの現在のタイム ゾーンの変更


あまり必要性は感じません。


> 7. スレッド単位、もしくはプロセス単位のアンビエント タイム ゾーン*1の設定


あまり必要性は感じません。


> 8. 一行で簡潔に変換を可能にするヘルパ関数(TimeZone.Convert(time, "Pacific", "Eastern") のような)


あると便利だと思いますが、なくても Pacific → GMT → Eastern と変換すれば済むことだと思います。


> 9. "PST", "EDT"といったタイム ゾーンを組み込んだDateTimeに対する解析と書式設定


RFC や W3C などにあるような書式には対応して欲しいです。
また、サマータイムのために Asia/Tokyo のような表記法を導入する場合は、それにも対応して欲しいです。

0 件のコメント:

コメントを投稿