現在、DateTime
TimeZone対応の方法で.NETを処理する標準的な方法がありますDateTime
。UTCで作成する場合は常に(たとえば、を使用してDateTime.UtcNow
)、表示する場合は常にUTCからユーザーの現地時間に変換し直します。 。
これは問題なく機能DateTimeOffset
しますが、オブジェクト自体でローカル時間とUTC時間をキャプチャする方法について読んでいます。だから問題は、DateTimeOffset
私たちがすでに行っていることと比較して、使用することの利点は何でしょうか?
DateTimeOffset
瞬間時間(絶対時間とも呼ばれます)の表現です。つまり、すべての人にとって普遍的な瞬間を意味します(うるう秒、または時間の遅れの相対論的効果を考慮していません)。瞬間的な時間を表現する別の方法は、Aであるです。DateTime
.Kind
DateTimeKind.Utc
これは、誰かのカレンダー上の位置であるカレンダー時間(常用時とも呼ばれます)とは異なり、世界中にさまざまなカレンダーがあります。これらのカレンダーをタイムゾーンと呼びます。カレンダー時間が表されDateTime
どこ.Kind
ですDateTimeKind.Unspecified
かDateTimeKind.Local
。そして.Local
、結果を使用しているコンピューターがどこに配置されているかを暗黙的に理解しているシナリオでのみ意味があります。(たとえば、ユーザーのワークステーション)
では、なぜDateTimeOffset
UTCの代わりにDateTime
?それはすべて視点についてです。例えを使ってみましょう-私たちは写真家のふりをします。
あなたがカレンダーのタイムラインに立って、目の前に配置された瞬間のタイムライン上の人物にカメラを向けていると想像してみてください。タイムゾーンのルールに従ってカメラを並べます。これは、夏時間のため、またはタイムゾーンの法的な定義に対するその他の変更のために定期的に変更されます。(手が安定していないため、カメラが不安定です。)
写真に立っている人は、あなたのカメラがどこから来たのかを見るでしょう。他の人が写真を撮っている場合、彼らは異なる角度からである可能性があります。これは、のOffset
一部がDateTimeOffset
表すものです。
したがって、カメラに「東部標準時」というラベルを付けると、-5から指している場合もあれば、-4から指している場合もあります。世界中にカメラがあり、すべて異なるものにラベルが付けられており、すべてが異なる角度から同じ瞬間のタイムラインを指しています。それらのいくつかは互いに隣接している(または上にある)ため、オフセットを知っているだけでは、時間がどのタイムゾーンに関連しているかを判断するのに十分ではありません。
そしてUTCはどうですか?まあ、それは安定した手を持っていることが保証されているそこにある唯一のカメラです。三脚の上にあり、地面にしっかりと固定されています。それはどこにも行きません。その遠近法をゼロオフセットと呼びます。
それで、このアナロジーは私たちに何を教えていますか?それはいくつかの直感的なガイドラインを提供します-
If you are representing time relative to some place in particular, represent it in calendar time with a DateTime
. Just be sure you don't ever confuse one calendar with another. Unspecified
should be your assumption. Local
is only useful coming from DateTime.Now
. For example, I might get DateTime.Now
and save it in a database - but when I retrieve it, I have to assume that it is Unspecified
. I can't rely that my local calendar is the same calendar that it was originally taken from.
If you must always be certain of the moment, make sure you are representing instantaneous time. Use DateTimeOffset
to enforce it, or use UTC DateTime
by convention.
If you need to track a moment of instantaneous time, but you want to also know "What time did the user think it was on their local calendar?" - then you must use a DateTimeOffset
. This is very important for timekeeping systems, for example - both for technical and legal concerns.
If you ever need to modify a previously recorded DateTimeOffset
- you don't have enough information in the offset alone to ensure that the new offset is still relevant for the user. You must also store a timezone identifier (think - I need the name of that camera so I can take a new picture even if the position has changed).
It should also be pointed out that Noda Time has a representation called ZonedDateTime
for this, while the .Net base class library does not have anything similar. You would need to store both a DateTimeOffset
and a TimeZoneInfo.Id
value.
Occasionally, you will want to represent a calendar time that is local to "whomever is looking at it". For example, when defining what today means. Today is always midnight to midnight, but these represent a near-infinite number of overlapping ranges on the instantaneous timeline. (In practice we have a finite number of timezones, but you can express offsets down to the tick) So in these situations, make sure you understand how to either limit the "who's asking?" question down to a single time zone, or deal with translating them back to instantaneous time as appropriate.
Here are a few other little bits about DateTimeOffset
that back up this analogy, and some tips for keeping it straight:
If you compare two DateTimeOffset
values, they are first normalized to zero offset before comparing. In other words, 2012-01-01T00:00:00+00:00
and 2012-01-01T02:00:00+02:00
refer to the same instantaneous moment, and are therefore equivalent.
If you are doing any unit testing and need to be certain of the offset, test both the DateTimeOffset
value, and the .Offset
property separately.
There is a one-way implicit conversion built in to the .Net framework that lets you pass a DateTime
into any DateTimeOffset
parameter or variable. When doing so, the .Kind
matters. If you pass a UTC kind, it will carry in with a zero offset, but if you pass either .Local
or .Unspecified
, it will assume to be local. The framework is basically saying, "Well, you asked me to convert calendar time to instantaneous time, but I have no idea where this came from, so I'm just going to use the local calendar." This is a huge gotcha if you load up an unspecified DateTime
on a computer with a different timezone. (IMHO - that should throw an exception - but it doesn't.)
Shameless Plug:
多くの人がこのアナロジーが非常に価値があると私と共有しているので、私はそれを私のPluralsightコースのDate and TimeFundamentalsに含めました。2番目のモジュール「ContextMatters」の「CalendarTimevs。Instantaneous Time」というタイトルのクリップで、カメラのアナロジーのステップバイステップのウォークスルーを見つけることができます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加