3

次のコード スニペットで、私が主張しているラウンドトリップ プロパティは、任意のDateTime値に対して保持されることが保証されていますか?

DateTime input = GetAnyDateTime();
DateTime roundtripped = input.ToUniversalTime().ToLocalTime();
Assert.IsTrue(input == roundtripped);

このアサーションは、逆の種類の往復 ( input.ToLocalTime().ToUniversalTime()) にも適用されますか?

考えられるエッジ ケースは、タイム ゾーン、夏時間、うるう秒、表現できない、またはあいまいな現地時間などです。

4

2 に答える 2

7

それはそうですが、フードの下で行われているいくつかの暗い魔法のハッカーを通してのみ.

を見ると、 、 、DateTimeKindの 3 つのオプションが表示されます。この情報は、内部 64 ビット表現の 2 ビットにパックされます。2 ビットで表現できる値は 4 つあるため、第 4 の種類の余地があります。UnspecifiedUtcLocal

そして、Jon Skeetがこのブログ投稿で明らかにし、説明しているように、実際に隠された第 4 の種類があります。基本的にはありますLocalが、あいまいな時間を解決する場合は別の方法で処理されます。

もちろん、.Net の外では、とにかくDateTimewith Localkind は往復しません。Unspecified別段の指示がない限り、返品時と同じように扱われます。私はこれについてここにブログを書きました。より良い代替手段はDateTimeOffsetです。

もちろん、これはDateTime.Net の厄介な問題の 1 つにすぎません。Jon Skeet による別の素晴らしい投稿では、それらのいくつかについて説明しています

最善の解決策は、組み込みの日付と時刻の型の使用をやめ、代わりにNoda Timeに慣れることです。他のシステムとやり取りするときは、引き続きDateTimeまたはを使用する必要があるかもしれませんが、内部で Noda Time を使用して、すべての変換を行うことができます。DateTimeOffset

追加情報

ティックや文字列など、別の形式を介したラウンドトリップについて質問しました。

  • DateTime.Ticksin .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ます。UtcLocal

    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値はすべての関連情報をシリアル化された形式で運ぶため、常にラウンドトリップします。そう意志UnspecifiedUtcの種類DateTimeLocalの種類を避けて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種類がありますが、テストはいずれにせよ合格します

于 2013-06-04T15:31:36.123 に答える
1

参照元は、Jon Skeet の説明にリンクする受け入れられた回答を確認しており、興味深いかもしれません。

//
// There is also 4th state stored that is a special type of Local value that
// is used to avoid data loss when round-tripping between local and UTC time.
// See below for more information on this 4th state, although it is 
// effectively hidden from most users, who just see the 3-state DateTimeKind
// enumeration.
[...]
// For a description of various calendar issues, look at
// 
// Calendar Studies web site, at 

// http://serendipity.nofadz.com/hermetic/cal_stud.htm.

[...]
    // The data is stored as an unsigned 64-bit integeter
    //   Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value
    //               12/31/9999 23:59:59.9999999
    //   Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd
    //               value for the rare case where the date time is local, but is in an overlapped daylight
    //               savings time hour and it is in daylight savings time. This allows distinction of these
    //               otherwise ambiguous local times and prevents data loss when round tripping from Local to
    //               UTC time.
    private UInt64 dateData;
于 2015-10-09T05:41:26.740 に答える