【问题标题】:DateTime Compare Ignores Kind?日期时间比较忽略种类?
【发布时间】:2015-04-20 14:16:47
【问题描述】:
DateTime d1=new DateTime(2015, 1, 1, 0, 0, 0, DateTimeKind.Utc);
DateTime d2=new DateTime(2015, 1, 1, 0, 0, 0, DateTimeKind.Local);
Console.WriteLine(d1==d2);           // prints true
Console.WriteLine(d1<d2);            // prints false
Console.WriteLine(d1.CompareTo(d2)); // prints 0
Console.WriteLine(d1.ToUniversalTime()==d2.ToUniversalTime()); // prints false

这对我来说似乎是一个错误,如果不是 color me surprised

每次比较都必须调用 ToUniversalTime() 还是有更好的选择?

如何避免因 DateTimeKind.Unspecified 而导致忘记调用 ToUniversalTime() 或得到错误结果等陷阱?

【问题讨论】:

  • 完全,它在文档中 - 您必须确保时间在同一时区。此外,DateTime 不包含任何时区信息。为此,您需要DateTimeOffset
  • Docs 明确说明了这一点在比较 DateTime 对象之前,请确保对象代表同一时区的时间。您可以通过比较它们的 Kind 属性的值来做到这一点。
  • NodaTime 救援 :)
  • here's the actual source 显示比较实际上是多么简单。

标签: c# datetime utc


【解决方案1】:

MSDN 文档很清楚,DateTimeKind 没有考虑使用 Equality 运算符。

Equality 运算符通过比较它们的刻度数来确定两个 DateTime 值是否相等。 在比较 DateTime 对象之前,请确保对象代表同一时区的时间。您可以通过比较它们的 Kind 属性的值来做到这一点

MSDN - DateTime.Equality Operator

您可以编写自己的扩展方法来包含DateTimeKind 比较:

public static bool EqualsWithKind(this DateTime time, DateTime other)
{
      return time.Kind == other.Kind &&
             time == other;
}

考虑到来自 Panagiotis KanavosJames Thorpe 的关于 DateTimeOffset 的 cmets:

在保证偏移量与本地偏移量相同的情况下使用。

public static bool EqualsWithTimezone(this DateTime time, DateTime other)
{
      return new DateTimeOffset(time) == new DateTimeOffset(other);
}

如果不能保证偏移量相同,则使用:

public static bool EqualsInclTimezone(this DateTime time, TimeSpan timeOffset, DateTime other, TimeSpan otherOffset)
{
      return new DateTimeOffset(time, timeOffset) == new DateTimeOffset(other, otherOffset);
}

【讨论】:

  • 你能给我一个避免这个陷阱的替代方案吗?
  • 不要使用 DateTime,使用包含 tz 偏移量的 DateTimeOffset。更好的是,使用 Noda Time,它使用实际的时区名称并调整夏令时的偏移量。航空旅行计算不可或缺
  • 提防这种扩展方法 - 这只是保证它们是相同的时间和相同的种类。即使种类不同,它们可能仍然是同一时间。 DateTimeOffset 真的应该用在这里。
  • @JamesThorpe +1 DateTimeOffset 至少。您需要 NodaTime 才能找到每个时区的正确偏移量
  • @toadflakz 您的编辑假设本地时间具有机器的当前偏移量 - 类似于假设字符串仅使用机器的系统代码页来查找不兼容的字符串。情况并非总是如此(例如,从数据库读取的时间)。这也是ToUniversalTime() 的一个问题,它假设有一定的偏移量。
【解决方案2】:

这不完全是错误,而是DateTime 的缺点。 DateTime 类型不支持除本地/UTC 指示符之外的时区信息。它在文档中这么说-您必须确保日期在同一时区-而不仅仅是具有相同的种类。 DateTimeKind.Local 没有说明实际使用的时区。

如果您关心时区,则应始终使用 DateTimeOffset 类型。它是在 .NET 3.5 中引入的,部分是为了解决时区问题。 DateTimeOffset 等价于 SQL Server 的datetimeoffset 类型,包含时区偏移和时间,允许时区偏移之间的比较和转换。这也允许您在代码和数据库中存储和使用完整的时间信息,避免转换错误。

这类似于使用nvarchar 而不是varchar 以避免代码页转换错误。

由于夏令时,时区可能有不同的偏移量。夏令时规则也会不时改变——俄罗斯规则在过去 10 年中至少改变了 4 次。 Windows 和 .NET 没有解决此问题的方法。

这可能是一个问题,例如在旅游业中。在这种情况下,您可以使用像 Noda Time 这样的库,其中包含具有所有已知时区规则的 IANA 时区数据库。

【讨论】:

    【解决方案3】:

    在我看来是正确的。

    2015 年 1 月 1 日 15:00:00 (UTC)(也是 GMT - 等于 GMT 格林威治标准时间)

    2015 年 1 月 1 日 15:00:00(本地 - 假设本地时间在纽约)

    这两个时间和日期通过比较是相等的。

    但是将第二个转换为 UTC,它会提前 5 小时变为 世界标准时间/格林威治标准时间

    1/1/2015 20:00:00 - 他们不再平等!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-25
      • 2018-07-28
      • 2012-02-18
      • 2016-02-14
      • 2011-12-02
      • 1970-01-01
      • 2012-05-08
      相关资源
      最近更新 更多