当您调用.Equal
or.Compare
时,会在内部.InternalTicks
比较值,即 aulong
没有前两位。这个字段是不相等的,因为它已经被调整了几个小时来表示世界时的时间:当你调用 时ToUniversalTime()
,它会以当前系统的本地时区设置的偏移量来调整时间。
您应该这样看:DateTime 对象表示未命名时区中的时间,但不是通用时间加时区。时区是本地(系统的时区)或 UTC。您可能会认为这是缺少 DateTime 类,但从历史上看,它已被实现为“自 1970 年以来的滴答数”并且不包含时区信息。
转换为另一个 UTC时,时间是——而且应该是——调整的。这可能是微软选择使用方法而不是属性的原因,以强调在转换为 UTC 时会采取行动。
最初我在这里写的是结构是比较的,标志System.DateTime.Kind
是不同的。这是不正确的:不同的是刻度的数量:
t1.Ticks == t2.Ticks; // false
t1.Ticks.Equals(t2.Ticks); // false
为了安全地比较两个日期,您可以将它们转换为相同的日期。如果您在比较之前将任何日期转换为通用时间,您将获得您所追求的结果:
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
是一种罕见的类型,可以按位不相等,但比较相等,或者可以按位相等(时间部分)并比较不相等。
.NET 6 中的变化
在 .NET 6.0 中,我们现在拥有TimeOnly
和DateOnly
. 您可以使用这些来存储“只是一天中的时间”,或者“只是一年中的日期”。将这些组合在一个结构中,您将拥有一个 Date & Time 结构,而没有原始DateTime
.
备择方案
在 .NET 中很难使用DateTime
、 、 闰秒、日历、时区转换、持续时间等正常工作。TimeZoneInfo
我个人更喜欢NodaTime
Jon Skeet 的作品,它以一种有意义且明确的方式将控制权交还给了程序员。
Jon Skeet 的这篇富有洞察力的文章深入解释了程序员在尝试绕过所有 DateTime 问题时可能面临的麻烦,而只是将所有内容存储在 UTC 中。
来源的背景信息
如果您检查.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.