現在、TimeZoneを意識した方法で.net DateTimesを処理する標準的な方法があります。DateTime
を生成するときはいつでも(たとえばDateTime.UtcNow
を使用して)、それを表示するときはいつでもUTCからユーザーのローカルに変換します。時間。
それはうまくいきますが、私はDateTimeOffset
と、それがオブジェクト自身の中でどのようにローカルとUTC時間を捉えるかについて読んでいます。それで問題は、DateTimeOffset
を使うことの利点と私たちがすでに行っていることの違いは何でしょうか。
DateTimeOffset
は、瞬間的な時間(絶対時間とも呼ばれます)の表現です。それによって、私は誰にとっても普遍的である瞬間を意味します( うるう秒 、または time拡張の相対論的効果 を考慮しない)。瞬間的な時間を表すもう1つの方法は、.Kind
がDateTimeKind.Utc
であるDateTime
を使用することです。
これはカレンダー時間(市民時間としても知られています)とは別のもので、世界中にはさまざまなカレンダーがあります。これらのカレンダーをタイムゾーンと呼びます。カレンダー時間はDateTime
で表されます。ここで、.Kind
はDateTimeKind.Unspecified
、またはDateTimeKind.Local
です。また、.Local
は、結果を使用しているコンピューターがどこに配置されているかについて暗黙のうちに理解しているシナリオでのみ意味があります。 (たとえば、ユーザーのワークステーション)
それでは、なぜUTCのDateTimeOffset
ではなくDateTime
なのでしょうか。 それはすべて展望についてです。 例えてみましょう - 私たちは写真家のふりをします。
あなたがカレンダーのタイムライン上に立っていると想像してください。あなたの前にレイアウトされた瞬間タイムライン上の人にカメラを向けます。あなたはあなたのタイムゾーンの規則に従ってあなたのカメラを並べます - それは夏時間のために、またはあなたのタイムゾーンの法的定義への他の変更のために定期的に変わります。 (手がしっかりしていないので、カメラは揺れています。)
写真の中に立っている人は、あなたのカメラが来た角度を見るでしょう。他の人が写真を撮っているならば、それらは異なる角度からのものかもしれません。これがOffset
のDateTimeOffset
部分が表すものです。
そのため、カメラに「Eastern Time」というラベルを付けた場合、-5から指していることがあり、-4から指していることがあります。世界中にカメラがあり、すべて異なるものにラベルが付けられており、すべて異なる角度から同じ瞬間的なタイムラインを指しています。それらのうちのいくつかはお互いのすぐ隣(または一番上)にあるので、オフセットを知っているだけでは、時間がどのタイムゾーンに関連しているかを判断するのに十分ではありません。
そして、UTCはどうですか?それは、しっかりとした手を持つことが保証されているのは、そこにある1台のカメラです。しっかりと固定された三脚の上にあります。それはどこにも行きません。その視野角をゼロオフセットと呼びます。
それで - このアナロジーは何を教えてくれるのでしょうか?それはいくつかの直感的なガイドラインを提供します。
特に時間を基準にして時間を表している場合は、カレンダー時間でDateTime
を付けて表します。あるカレンダーと別のカレンダーを混同しないように注意してください。 Unspecified
はあなたの仮定です。 Local
はDateTime.Now
から来る場合にのみ役に立ちます。たとえば、DateTime.Now
を取得してデータベースに保存する場合がありますが、取得するときはUnspecified
と想定する必要があります。私のローカルカレンダーが元々のカレンダーと同じであるとは限りません。
あなたがいつもその瞬間を確信していなければならないならば、あなたが瞬間の時間を表していることを確認してください。それを強制するためにDateTimeOffset
を使うか、または慣例によりUTC DateTime
を使います。
あなたが瞬間の瞬間を追跡する必要があるが、あなたは「ユーザーがそれが彼らの地元のカレンダーにあったと何時に考えたか」も知りたいならば。 - あなたはmustDateTimeOffset
を使います。これは、たとえば計時システムにとって非常に重要です。たとえば、技術的にも法的にも重要です。
以前に記録したDateTimeOffset
を修正する必要がある場合は、新しいオフセットがユーザーに関連していることを確認するのに十分な情報がオフセットだけにはありません。あなたはalsoタイムゾーンの識別子を保存する必要があります(位置が変わっても新しい写真を撮ることができるように私はそのカメラの名前が必要だと思います)。
また、 Noda Time にはZonedDateTime
という表現がありますが、.Net基本クラスライブラリにはこれに似たものはありません。 DateTimeOffset
とTimeZoneInfo.Id
の両方の値を格納する必要があります。
時折、「誰でも見ている人」にとってローカルなカレンダー時間を表したいと思うでしょう。たとえば、todayの意味を定義するときなどです。今日は常に真夜中から真夜中までですが、これらは瞬間タイムライン上で無限に近い数の重複する範囲を表しています。 (実際には有限数のタイムゾーンがありますが、オフセットを目盛りまで表現することができます)したがって、このような状況では、「誰が質問しているのか」を制限する方法を理解してください。単一のタイムゾーンに質問するか、必要に応じてそれらを瞬間的な時間に戻すことを処理します。
以下は、この類推を裏付けるDateTimeOffset
に関する他のいくつかのちょっとしたことと、それをまっすぐに保つためのいくつかのヒントです。
2つのDateTimeOffset
値を比較する場合、それらは比較の前に最初にゼロオフセットに正規化されます。言い換えれば、2012-01-01T00:00:00+00:00
と2012-01-01T02:00:00+02:00
は同じ瞬間を指しているので等価です。
単体テストを実行していて、オフセットを確実にする必要がある場合は、both _ DateTimeOffset
値と.Offset
プロパティを別々にテストしてください。
DateTime
を任意のDateTimeOffset
パラメータまたは変数に渡すことを可能にする一方向の暗黙の変換が.Netフレームワークに組み込まれています。そうするとき、.Kind
が重要です 。あなたがUTCの種類を渡す場合、それはゼロオフセットで持ち込みますが、あなたが.Local
か.Unspecified
のどちらかを渡すならば、それは local であると仮定するでしょう。フレームワークは基本的に、「さて、あなたは私にカレンダー時間を瞬間時間に変換するように頼んだが、これがどこから来たのかわからないので、私はただローカルカレンダーを使うつもりだ」と言っている。指定していないDateTime
を別のタイムゾーンのコンピュータにロードした場合、これは大きな問題です。 (私見 - それは例外をスローする必要があります - しかし、それはしません。)
恥知らずなプラグイン:
多くの人がこのアナロジーが非常に有益だと思うことを私と共有してきたので、私はそれを私のPluralsightコース Date and Time Fundamentals に含めました。 2番目のモジュール「Context Matters」には、「Calendar Time vs. Instantaneous Time」というタイトルのクリップで、カメラの類推の段階的なチュートリアルがあります。
マイクロソフトから:
DateTimeOffset値に対するこれらの用途は、DateTime値に対する用途よりはるかに一般的です。その結果、DateTimeOffsetは、アプリケーション開発のデフォルトの日付と時刻の種類と見なす必要があります。
source: "DateTime、DateTimeOffset、TimeSpan、TimeZoneInfoの選択" 、 _ msdn _
私たちのアプリケーションが特定の時点を扱うとき(例えばレコードが作成/更新されたとき)、ほとんどすべてにDateTimeOffset
を使います。補足として、SQL Server 2008でもDATETIMEOFFSET
を使用しています。
日付だけ、時間だけ、あるいはどちらかを一般的な意味で扱いたいときには、DateTime
が役に立つと思います。たとえば、毎日午前7時にオフにしたいというアラームがある場合は、DSTに関係なく午前7時にオフにしたいので、DateTime
のDateTimeKind
を使用してUnspecified
に保存できます。しかし、アラーム発生の履歴を表現したい場合は、DateTimeOffset
を使用します。
DateTimeOffset
とDateTime
を組み合わせて使用するときは特に注意してください。特に型を割り当てて比較するときは注意が必要です。また、DateTime
は比較時にタイムゾーンオフセットを無視するため、同じDateTimeKind
であるDateTime
インスタンスのみを比較します。
DateTimeには、現地時間とUTCの2つの異なる時間しか格納できません。 Kind プロパティはどちらを示します。
DateTimeOffsetは、世界中のどこからでも現地時間を保存できることでこれを拡張します。また、その現地時間とUTCの間の offset も格納されます。そのUTCオフセットを格納するためにクラスに追加のメンバを追加しない限り、DateTimeがこれを行えないことに注意してください。それともUTCでのみ動作します。それ自体それ自体良い考えです。
DateTimeOffset
が意味をなすところがいくつかあります。 1つは、定期的な予定や夏時間に対処しているときです。毎日午前9時にアラームが鳴るように設定したいとしましょう。 「UTCとして保存、現地時間として表示」ルールを使用すると、夏時間が有効になっているときにアラームが 異なる 時刻に発生します。
おそらく他にもありますが、上記の例は実際に私が過去に遭遇したものです(これはBCLにDateTimeOffset
を追加する前のものでした。それと同時にタイムゾーン情報を保存します:基本的にDateTimeOffset
は内部的に何をするか)。
最も重要な違いは、DateTimeはタイムゾーン情報を格納しないのに対し、DateTimeOffsetは格納することです。
DateTimeはUTCとLocalを区別しますが、それに関連する明示的なタイムゾーンオフセットは絶対にありません。何らかのシリアル化または変換を行う場合は、サーバーのタイムゾーンが使用されます。 UTC時間をオフセットするために分を追加して手動で現地時間を作成した場合でも、シリアル化の手順に少し時間がかかることがあります。これは、(DateTimeに明示的なオフセットがないため)サーバーのタイムゾーンオフセットを使用するためです。
たとえば、Json.NetとISOの日付形式を使用してKind = LocalでDateTime値をシリアル化すると、2015-08-05T07:00:00-04
のような文字列が得られます。最後の部分(-04)は、DateTimeや計算に使用したオフセットとは関係がないことに注意してください。これは、純粋にサーバーのタイムゾーンオフセットです。
一方、DateTimeOffsetは明示的にオフセットを含みます。それはタイムゾーンの名前を含まないかもしれません、しかし、少なくともそれはオフセットを含みます、そしてあなたがそれをシリアル化するならば、あなたはサーバの現地時間が何であるかの代わりにあなたの値に明示的に含まれるオフセットを得ることになるでしょう。
答えのほとんどは良いですが、私は詳細についてはMSDNのいくつかのリンクを追加することを考えました
大きな違いは、現在のタイムゾーン以外のタイムゾーンで現地時間に変換するためにDateTimeOffset
をTimeZoneInfo
と組み合わせて使用できることです。
これは、さまざまなタイムゾーンのユーザーがアクセスするサーバーアプリケーション(ASP.NETなど)で役立ちます。
Microsoft からのこのコードはすべてを説明しています。
// Find difference between Date.Now and Date.UtcNow
date1 = DateTime.Now;
date2 = DateTime.UtcNow;
difference = date1 - date2;
Console.WriteLine("{0} - {1} = {2}", date1, date2, difference);
// Find difference between Now and UtcNow using DateTimeOffset
dateOffset1 = DateTimeOffset.Now;
dateOffset2 = DateTimeOffset.UtcNow;
difference = dateOffset1 - dateOffset2;
Console.WriteLine("{0} - {1} = {2}",
dateOffset1, dateOffset2, difference);
// If run in the Pacific Standard time zone on 4/2/2007, the example
// displays the following output to the console:
// 4/2/2007 7:23:57 PM - 4/3/2007 2:23:57 AM = -07:00:00
// 4/2/2007 7:23:57 PM -07:00 - 4/3/2007 2:23:57 AM +00:00 = 00:00:00
DateTimeOffsetの唯一のマイナス面は、MicrosoftがXmlSerializerクラスでそれをサポートすることを(設計上)忘れていたことです。しかし、それはその後XmlConvertユーティリティクラスに追加されました。
すべての利点から、DateTimeOffsetとTimeZoneInfoを使用してください。XMLとの間でシリアル化される、またはXMLからシリアル化される可能性のあるエンティティを作成するときは注意してください。