BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース Noda Time: 先進的な.NETの日付/時間ライブラリ

Noda Time: 先進的な.NETの日付/時間ライブラリ

原文(投稿日:2011/08/26)へのリンク

率直に言って、.NETの日付/時間ライブラリには欠点がある。始めからタイムゾーンや協定世界時の概念が無かったので、ほとんどのライブラリがDateTimeクラスに適合するようにつくられてしまい、その結果、開発者は値がローカルの時間なのかUTCなのか推測しなければならなかった。クライアントサーバ型のアプリケーションではさらに悪いことにローカルがクライアントのローカル時間とサーバ側のローカル時間のどちらかを指す可能性があった。

.NET 2.0ではDateTime.Kindプロパティが導入された。これによって、すべての問題が解決しなかったが、既存のアプリケーションをより適したかたちにすることができた。しかし、今日に至るまでほとんどの開発者はKindプロパティを正しく設定していないか、このプロパティに注意も払っていない。Jon Skeet氏が言うには

このプロパティの値は様々な方法による様々な処理に影響を受けます。例えば、プロパティの値が"Unspecified"であるDateTimeにToUniversalTime()を適用した場合、適用前の値はローカル時間だと仮定されています。一方、ToLocalTime()を適用した場合、適用前の値はUTC時間と見なされます。これがひとつのモデルの振る舞いなのです。

.NET 3.5ではついにDateTimeOffsetタイプが導入された。これによって、UTCからの相対値など、どのような状況でも使える方法が導入されたが、新しい問題も生まれてしまった。例えばKindプロパティがデフォルト値のUnspecifiedであっても、DateTimeはDateTimeOffsetに暗黙に型変換できてしまう。したがって、このような型変換を見たら常にバグを疑わなければなりません。しかしもっと重要なのは、DateTimeOffsetが実際にはタイムゾーンを表現していないことです。再び、Jon Skeet氏によれば、

あるローカルの日時が特定のタイムゾーンに結びついていないとします。このとき、この日時は"10pm on August 20th 2011"の前なのか後なのか。これはどこにいるかに依存します(ここではISOではないカレンダーは無視します)。つまり、DateTimeOffsetはタイムゾーンとは独立した部分("10pm on ..."の部分)を含み、かつUTCからの相対値を含みます。つまり、この値は想像上の時系列の上のある瞬間に変換できます。相対性を無視すれば、地球上のすべての人はある瞬間を同時に経験しています。私がクリック(とてもすばやく)するとき、地球上ではこのクリックの瞬間の直前と直後に何らかの出来事が起きています。特定のタイムゾーン内にいたかどうかは関係ありません。この観点から考えられるある瞬間は、ある特定の瞬間に観察されるローカルの日時と比べると世界的に通用します。

[…]

また、DateTimeOffset特定のタイムゾーンと結びつけて日時を扱いたい場合にも不便です。そのタイムゾーンに関連する相対値を与える方法がないからです。.NET 3.5にはTimeZoneInfoクラスというこの用途に使えそうなクラスがありますが、"あると規定のタイムゾーンの特定のローカル時間"を提供する型はありません。従ってDateTimeOffsetを使えば、タイムゾーンが決まっていない状態での特定の時間はわかりますが、その1分後のローカル時間が何時なのかはわかりません。タイムゾーンの相対値が変わってしまう(たいていはサマータイムのため)かもしれないからです。

すべてではないにしろ、このような多くの問題に対処するためJon Skeet氏はJoda Time の移植ライブラリNoda Timeを実装している。ライブラリの特徴は下記の通り。

  • Instant: UNIX時間を基準にした場合の当該瞬間の値。.NETではタイマ刻み数、Javaではミリ秒で表される。1ミリ秒はタイマ刻み数で10,000。基準の時間は異なるものの、このクラスは相対値が0のDateTimeOffsetに該当する。
  • Partial: ローカルの日時の一部を表す。特定の瞬間を表すのには十分ではない。例えば、サブクラスのMonthDayで年次の出来事、例えば誕生日を表すことができる。またサブクラスのYearMonthはクレジットカードの有効期限に使えます。月の最初の日や最後の日で有効期限が切れるのは不自然かもしれないからです。日付だけの時間と日時を混ぜることで問題が起きているのなら、LocalDate型を使うのはいいでしょう。
  • Interval: 時間の間隔を表します。TimeSpanとは違い、開始と終了の瞬間を保持します。“2011年1月1日の午前8時から午後12時まで”というような概念を表すのに使えるでしょう。
  • Duration: これは基本的にはTimeSpanクラスです。単位は.NETの場合、タイマ刻み数でJavaの場合はミリ秒です。
  • Period: “Joda-Timeでは各フィールドの値を元に定義した時間間隔を表します。例えば、3年5ヶ月と2日と7時間という具合です。Durationと違うのはミリ秒の世界で見ると正確ではない点です。Periodから正確なミリ秒を得るには特定の関連する瞬間(DateTimeZoneとChronologyを含む)が必要です。”
  • DateTimeZoneとChronology: DateTimeZoneにはタイムゾーンを表すのに必要な情報をほとんど含んでいます。単独で使うのではなく、特定のCalendarSystemオブジェクトと組み合わせてChronologyという型にします。
  • ZonedDateTime: ローカルの日時と特定のタイムゾーンとカレンダーシステムを組み合わせるのに使います。UTCの相対値を使うよりもこの方法の方がエラーが少ないです。

この記事に星をつける

おすすめ度
スタイル

BT