それはそうですが、フードの下で行われているいくつかの暗い魔法のハッカーを通してのみ.
を見ると、 、 、DateTimeKind
の 3 つのオプションが表示されます。この情報は、内部 64 ビット表現の 2 ビットにパックされます。2 ビットで表現できる値は 4 つあるため、第 4 の種類の余地があります。Unspecified
Utc
Local
そして、Jon Skeetがこのブログ投稿で明らかにし、説明しているように、実際に隠された第 4 の種類があります。基本的にはありますLocal
が、あいまいな時間を解決する場合は別の方法で処理されます。
もちろん、.Net の外では、とにかくDateTime
with Local
kind は往復しません。Unspecified
別段の指示がない限り、返品時と同じように扱われます。私はこれについてここにブログを書きました。より良い代替手段はDateTimeOffset
です。
もちろん、これはDateTime
.Net の厄介な問題の 1 つにすぎません。Jon Skeet による別の素晴らしい投稿では、それらのいくつかについて説明しています。
最善の解決策は、組み込みの日付と時刻の型の使用をやめ、代わりにNoda Timeに慣れることです。他のシステムとやり取りするときは、引き続きDateTime
またはを使用する必要があるかもしれませんが、内部で Noda Time を使用して、すべての変換を行うことができます。DateTimeOffset
追加情報
ティックや文字列など、別の形式を介したラウンドトリップについて質問しました。
DateTime.Ticks
in .Net は、単一の参照ポイントに準拠していないため、優れたシリアル化形式ではありません。これらは、0001 年 1 月 1 日の午前 0 時からの 100 ナノ秒間隔の整数です。しかし、それらは UTC に対応するものではなく、Kind
使用されている に一致します。言い換えると:
var utcNow = DateTime.UtcNow;
var now = utcNow.ToLocalTime();
var equal = utcNow.Ticks == now.Ticks; // false
これを、1970 年 1 月 1 日の基準点 ( UTC の午前 0 時) を使用する JavaScript と比較してください。のようにティック数を取得するたびに、.getTime()
UTC が反映されます。JavaScript では意味がないため、単純なメソッド呼び出しで実際に現地時間の目盛りを取得することはできません。他の言語も同様に機能します。
また、私たちが使用しているグレゴリオ暦は 1582 年まで有効ではなかったので、0001 年 1 月 1 日が基準点であるというのはおかしいです。1582 年より前の日付は、現在の縮尺では意味がなく、翻訳する必要があります。
文字列は人間が読み取れるため、日付と時刻の値を送信するのに最適な方法です。しかし、あいまいさのない機械可読であることも確認する必要があります。たとえば、 のような値は使用しないで1/4/2013
ください。追加のカルチャ情報がないと、それが 1 月 4 日なのか 4 月 1 日なのかがわからないためです。代わりに、ISO8601 形式のいずれかを使用してください。
これらをで使用する場合、種類を往復できるフォーマット文字列DateTime
を使用できます。種類の 、または種類のローカル オフセットを"o"
追加しZ
ます。Utc
Local
var dt = new DateTime(2013,6,4,8,56,0); // Unspecified Kind
var iso = dt.ToString("o"); // 2013-06-04T08:56:00.0000000
var dt = DateTime.UtcNow; // Utc Kind
var iso = dt.ToString("o"); // 2013-06-04T15:56:00.0000000Z
var dt = DateTime.Now; // Local Kind
var iso = dt.ToString("o"); // 2013-06-04T08:56:00.0000000-07:00
この形式から解析する場合、オフセットがない場合、種類は になりますUnspecified
。しかし、Z
または任意のオフセットがある場合、デフォルトでは種類は になりますLocal
。また、結果が同等の現地時間になるように、指定したオフセットが適用されます。したがって、正しく適用したい場合は、種類を往復するように明示的に指示する必要があります。
var dt = DateTime.Parse("2013-01-04T15:56:00.0000000Z");
var kind = dt.Kind; // Local - incorrect!
var s = dt.ToString("o"); // "2013-01-04T08:56:00.0000000-07:00" (ouch!)
その代わり:
var dt = DateTime.Parse("2013-01-04T15:56:00.0000000Z",
CultureInfo.InvariantCulture,
DateTimeStyles.RoundtripKind);
var kind = dt.Kind; // Utc - that's better.
var s = dt.ToString("o"); // "2013-01-04T15:56:00.0000000Z" (nice!)
もちろん、 で作業する方がはるかに優れていDateTimeOffset
ます。これを ISO8601 形式でシリアル化すると、常に完全な表現が得られます。
var dto = DateTimeOffset.Now;
var iso = dto.ToString("o"); // 2013-06-04T08:56:00.0000000-07:00
この形式は、ISO8601 仕様のこのプロファイルを説明するRFC3339に準拠しており、異なるシステム間でタイムスタンプをシリアル化するための事実上の標準になりつつあります。私見 - 可能な限りこの形式を使用する必要があります。Web で一般的に見られる RFC1123 などの他の形式よりも はるかに優れています。さまざまな日付/時刻形式の詳細を次に示します。
DateTimeOffset
値はすべての関連情報をシリアル化された形式で運ぶため、常にラウンドトリップします。そう意志Unspecified
とUtc
の種類DateTime
。Local
の種類を避けてDateTime
ください。それらはあなたを簡単に困らせるでしょう。
答えてください?
これをもう一度読んで、多くの詳細を提供している間に、あなたの質問に直接答えていないことに気付きました. 入力の種類が既に変換先の最初の種類である場合、テストは失敗します。2 つのテスト条件を見てみましょう。
someDateTime == someDateTime.ToUniversalTime().ToLocalTime()
元の値がすでにUtc
種類のものである場合、これは失敗します。
このテストは、DST スプリング フォワード移行中にローカル タイム ゾーンで元の値が無効な場合にも失敗します。たとえば2013-03-10 02:00:00
、米国太平洋時間には存在しません。ただし、存在しないため、おそらくデータ内で遭遇することはありません。したがって、おそらく有効なテスト条件ではありません。
someDateTime == someDateTime.ToLocalTime().ToUniversalTime()
元の値がすでにLocal
種類のものである場合、これは失敗します。
Kind
プロパティは等価チェックに参加しないことにも注意してください。したがって、これらのいずれかの入力は である可能性がありUnspecified
ますが、テスト 1 の出力には常にLocal
種類があり、テスト 2 の出力には常にUtc
種類がありますが、テストはいずれにせよ合格します。