繁体   English   中英

C# DateTime 无法识别时区更改 (BST)

[英]C# DateTime not recognising timezone change (BST)

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

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

这意味着“27 Mar 2016, 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);

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

为什么 DateTime 类型不考虑 DST?

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

为什么 DateTime 类型不考虑 DST?

简答

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

MSDN 文档

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

更长的答案

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

  • Ticks跟踪自纪元值为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 可能是也可能不是。

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

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

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

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

最后,我将添加我非常固执的断言,即DateTime结构的设计以多种方式违反了SRP 特别是, DateTimeKind是一个令人憎恶的东西——恕我直言。

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

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

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

有用的链接在这里

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM