【问题标题】:C# DateTime not recognising timezone change (BST)C# DateTime 不识别时区更改(BST)
【发布时间】:2016-07-29 12:52:52
【问题描述】:

几周前,也就是 2016 年 3 月 27 日,英国的时钟提前了一小时。在 01:00,时钟“跳”到 02:00

http://www.timeanddate.com/time/dst/events.html

这意味着“2016 年 3 月 27 日,01:30”是无效的日期时间,即它不代表实际“发生”的时间。

最近,当 Java 日期时间解析器无法理解我们传递给它的时间时,我们发现了这一点。但是,C# DateTime 似乎根本没有任何问题。

DateTime dt = DateTime.Parse("2016-03-27 01:30:00"); 
bool invalid = TimeZoneInfo.Local.IsInvalidTime(dt);

虽然invalid 已正确设置为true,但DateTime 存储它似乎没有任何问题。

另外,声明如下:

diff = new DateTime(2016, 3, 27, 2, 30, 0) - new DateTime(2016, 3, 27, 0, 30, 0);

导致 TimeSpan 代表 2 小时,这是不正确的。

为什么 DateTime 类型不考虑夏令时?

【问题讨论】:

  • 作为一个英国人,我实际上并没有考虑过这一点,但由于世界各地的时区在不同日期进行夏令时更改,如果它知道的话,我几乎会感到惊讶.. 我希望它只看是有效的日期/时间,例如 00:00 和 23:59:59 之间的有效日期..
  • NodaTime 可能会为您解决此问题。但是,它有不同的 api。
  • NodaTime 等其他 API 存在是有原因的,原因是内置类型严重不足。已经固定了一些解决此问题的支持,但还不够好。如果您需要处理时区,请使用其他内容。

标签: c# datetime dst


【解决方案1】:

所以,这里唯一的问题是:

为什么 DateTime 类型不考虑夏令时?

简答

因为这就是 DateTime 结构的设计方式。

来自the MSDN docs

时区之间的转换操作(例如 UTC 和本地时间之间,或一个时区和另一个时区之间)会考虑夏令时,但算术和比较操作不会。

更长的答案

.NET 的 DateTime 只跟踪两个逻辑值,TicksKind。在内部,出于性能和兼容性原因,它们被合并为一个 64 位整数,但您可以将它们视为两个单独的值。

  • Ticks 跟踪自 epoch 值为 0001-01-01 00:00:00 以来的 100 纳秒间隔数。

  • Kind 跟踪有关 Ticks 是否旨在以 UTC、计算机的本地时区或其他未指定的时区表示时间的元数据。

每个使用DateTime 的函数都应该考虑Kind。框架中的许多人都这样做。有些没有。许多其他图书馆,甚至一些关注时间的图书馆,都完全忽略了它。

当您调用DateTime.Parse("2016-03-27 01:30:00") 时,Kind 设置为Unspecified,因为没有上下文。您提供的值不一定在任何特定时区,因此0001-01-01 00:00:00 纪元日期也不是。换句话说,您可以将其视为与 UTC 或任何 DST 规则没有任何偏移的日期和时间。

请注意,这与 Java 和 JavaScript 有很大不同,其中 Date 对象跟踪自 1970-01-01 00:00:00 UTC 纪元以来的毫秒数。 总是 UTC,.NET 可能是也可能不是。

有多种方法可以处理此问题。迄今为止最好的方法是使用由Noda Time 开源库提供的更合理的API。但是,如果您只想使用 .NET 内置的功能,请考虑使用 DateTimeOffset 结构。与 DateTime 不同,它跟踪其与 UTC 的偏移量,并在计算时将其考虑在内。

您仍然需要使用TimeZoneInfo.IsInvalidTime 检查输入是否无效。 解析时,.NET 中没有内置的 API 会在无效时间抛出异常。但是你会发现TimeZoneInfo上的转换函数,比如ConvertTimeToUtc,如果传入的源时区时间无效,确实会抛出异常。

您还应该检查TimeZoneInfo.IsAmbiguousTime,因为在回退转换期间给定一个发生两次的值,例如英国的2016-10-30 01:30,.NET 将始终选择标准时间偏移.虽然这乍一看似乎是正确的,但请考虑 daylight 实例实际上首先发生 - 这通常是大多数现实世界场景中所需的行为(无论如何根据我的经验)。

除此之外,关于您的 Java 代码,如果您还没有使用 Java 7 及更低版本,则应考虑使用 Joda Time,或者 Java 8 中内置的 java.time。您会发现它们与 @ 非常相似987654325@,前面提到过。

最后,我将添加我非常固执的断言,即DateTime 结构的设计在多个方面违反了SRP。特别是,DateTimeKind 是可憎的 - 恕我直言。

【讨论】:

  • 这里有很多好的答案 - 谢谢大家。马特必须在最详细的问题上打分并找出真正的问题,这真的很简单,如果有点宽泛的话。结果发现 DateTime 不是我想的那样相当
【解决方案2】:

我知道这是旧的,但我遇到了它,并认为其他人可能会从这个线程中得到帮助。虽然 Matt Johnson-Pint 给出的答案是全面的并且确实非常有帮助,但有一种简单的方法可以实现对 DateTime 的所需“调整”以显示当前本地时间——我认为这可以回答实际问题:

    string dtNow = DateTime.Now.ToLocalTime().ToString();

在英国 BST 中,这给出了正确的结果。

有用的链接here

【讨论】:

    【解决方案3】:

    DateTime 有一个 Kind 属性,它指示它是本地时间还是 UTC 或未指定。您的解析格式不允许它设置这种类型,因此它将是未指定的。我认为您需要设置它以使其正确处理 DST。

    一般你处理UTC格式的日期时间,然后当你需要显示给用户时转换为本地日期时间来解决这个问题。

    【讨论】:

    • 即使将 Kind 属性设置为本地,仍然可以有一个 DateTime 表示该日期的 01:30。我的问题的日期差异部分仍然返回 2 小时。
    【解决方案4】:

    我猜微软决定为您提供使用 IsInvalidTime 方法自行管理无效日期的灵活性。

    如果您的申请要求日期有效,那么您应该检查时间是否有效。

    【讨论】:

      【解决方案5】:

      Azure Web Apps 有一个很好的默认设置,可用于在 Azure 界面或 Visual Studio 中更改此设置。 这里有一个很棒的博客向您展示如何做到这一点: https://www.jasongaylord.com/blog/tip-changing-an-azure-app-service-time-zone

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-14
        • 1970-01-01
        • 1970-01-01
        • 2023-03-28
        • 2013-11-10
        • 2011-09-06
        • 1970-01-01
        • 2018-08-14
        相关资源
        最近更新 更多