44

DateTimeの比較でばかげた欠陥のように見えることに気づきました。

DateTime d = DateTime.Now;
DateTime dUtc = d.ToUniversalTime();

d == dUtc; // false
d.Equals(dUtc); //false
DateTime.Compare(d, dUtc) == 0; // false

1つがDateTimeKind.Localで、もう1つがDateTimeKind.UTCの場合、DateTimesでのすべての比較操作はどのタイプのスマート変換も実行できないようです。比較に関係する両方を常にUTC時間に変換する以外に、DateTimeを確実に比較するためのより良い方法はありますか?

4

2 に答える 2

50

.Equalまたはを呼び出すと.Compare、内部的に値.InternalTicksが比較されます。これは、ulong最初の 2 ビットを除いた です。このフィールドは、世界時の時間を表すために数時間調整されているため、等しくToUniversalTime()ありません。 を呼び出すと、現在のシステムのローカル タイムゾーン設定のオフセットで時刻が調整されます。

このように表示する必要があります。DateTime オブジェクトは、名前のないタイム ゾーンの時刻を表しますが、協定世界時とタイム ゾーンを合わせたものではありません。タイムゾーンはローカル (システムのタイムゾーン) または UTC です。これは DateTime クラスの欠如と考えるかもしれませんが、歴史的には「1970 年以降のティック数」として実装されており、タイムゾーン情報は含まれていません。

別の UTC に変換する場合、時刻は調整されます (調整する必要があります)。これがおそらく、Microsoft がプロパティではなくメソッドを使用して、UTC への変換時にアクションが実行されることを強調することを選択した理由です。

もともと構造体を比較してフラグSystem.DateTime.Kindが違うとここに書きました。これは正しくありません: 異なるのはティックの量です:

t1.Ticks == t2.Ticks;       // false
t1.Ticks.Equals(t2.Ticks);  // false

2 つの日付を安全に比較するには、それらを同じ種類に変換できます。比較する前に任意の日付を世界時に変換すると、次の結果が得られます。

DateTime t1 = DateTime.Now;
DateTime t2 = someOtherTime;
DateTime.Compare(t1.ToUniversalTime(), t2.ToUniversalTime());  // 0
DateTime.Equals(t1.ToUniversalTime(), t2.ToUniversalTime());  // true

現地時間を変更せずに UTC 時間に変換する

UTC に変換する代わりに(時間は同じままですが、ティック数は異なります)、 を上書きDateTimeKindて UTC に設定することもできます (現在は UTC になっているため、時間を変更しますが、比較します)。ティック数が等しいため、等しい)。

var t1 = DateTime.Now
var t2 = DateTime.SpecifyKind(t1, DateTimeKind.Utc)
var areEqual = t1 == t2   // true
var stillEqual = t1.Equals(t2) // true

DateTimeこれは、ビット単位で等しくない可能性があるが、等しいと比較されるか、ビット単位で等しく (時間部分) かつ等しくない場合があるまれなタイプの 1 つだと思います。

.NET 6 の変更点

.NET 6.0 では、TimeOnlyDateOnly. これらを使用して、「時刻のみ」「年の日付のみ」を保存できます。これらを構造体に組み合わせると、元の歴史的な煩わしさのない日付と時刻の構造体ができますDateTime

代替案

、、閏秒、カレンダー、タイムゾーンのシフト、期間などを適切に操作することは、.NET では困難DateTimeです。TimeZoneInfo個人的にNodaTimeは、Jon Skeet の方が好きです。これは、意味のある明確な方法でプログラマーに制御を戻します。

Jon Skeet によるこの洞察に満ちた投稿では、プログラマーがすべてを UTC で保存するだけで DateTime の問題を回避しようとするときに直面する可能性がある問題について詳しく説明しています。

ソースからの背景情報

.NET ソースの構造体を確認するDateTimeと、元は (.NET 1.0 で) が単なるティック数であったことを説明するメモが見つかりますDateTimeが、後でユニバーサルかローカルかを保存する機能が追加されました。時間。ただし、シリアル化すると、この情報は失われます。

これはソースのメモです:

    // This value type represents a date and time.  Every DateTime
    // object has a private field (Ticks) of type Int64 that stores the
    // date and time as the number of 100 nanosecond intervals since
    // 12:00 AM January 1, year 1 A.D. in the proleptic Gregorian Calendar.
    //
    // Starting from V2.0, DateTime also stored some context about its time
    // zone in the form of a 3-state value representing Unspecified, Utc or
    // Local. This is stored in the two top bits of the 64-bit numeric value
    // with the remainder of the bits storing the tick count. This information
    // is only used during time zone conversions and is not part of the
    // identity of the DateTime. Thus, operations like Compare and Equals
    // ignore this state. This is to stay compatible with earlier behavior
    // and performance characteristics and to avoid forcing  people into dealing
    // with the effects of daylight savings. Note, that this has little effect
    // on how the DateTime works except in a context where its specific time
    // zone is needed, such as during conversions and some parsing and formatting
    // cases.
于 2011-08-03T17:24:23.367 に答える
6

これに対処するために、DateTime と TimeZone を含む独自の DateTime オブジェクト (SmartDateTime と呼びましょう) を作成しました。元の DateTime 演算子を使用して比較を行う前に、== や Compare などのすべての演算子をオーバーライドして UTC に変換します。

于 2011-08-03T17:24:56.817 に答える