TimeZoneInfo.GetUtcoffset有多可靠?

How reliable is TimeZoneInfo.GetUtcoffset?

我在 TimeZoneInfo 对象中使用函数 GetUtcoffset() 来获取特定日期时间的 Utc 偏移值。通常,夏令时会遵循特定规则,例如“ 从十月的第一个星期日开始,到四月的第一个星期日结束”(澳大利亚东部标准时间),所以我相信函数产生的结果。

但我想知道如果夏令时因政府决定而延迟或改变,结果会怎样?过去,(澳大利亚)政府曾多次违反某些事件的夏令时规定。在这些情况下,调用 GetUtcoffset() 会产生不正确的结果还是库足够聪明 "occasionally" 从互联网上的某些来源获取夏令时更新?

用法: 我正在开发一个停车管理系统。一个数据库在时区不同的多个客户端之间共享。停车事件时间可以在法庭上用作证据,因此我们做对是非常重要的。当系统收到车辆已停放的通知时,系统将生成到达时间。鉴于通知可能来自不同时区的远程站点,我想知道我是否可以可靠地使用时区来计算客户端站点的当前日期时间。

示例:

string clientTimeZoneId = "AUS Eastern Standard Time";
var utcTime = DateTime.UtcNow;
var clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(clientTimeZoneId);
var arrivalTime = utcTime.Add(clientTimeZone.GetUtcOffset(utcTime));

这就是 GetUtcOffset 功能变得重要的地方。鉴于 2019-2020 "AUS Eastern Standard Time" 夏令时于 2020 年 4 月 5 日结束,在 6 日调用 GetUtcOffset 将得到 +10:00:00。但是,如果出于任何原因(例如奥运会),夏令时延长至第 10 天,则 arrivalTime 可能有一个小时不正确,具体取决于 GetUtcOffset 的值。

目前,我们的系统管理员每年都必须手动更新数据库中的夏令时日期,但最好不要这样做。我不确切知道政府在这些事件中做了什么,但他们一定是在某个地方发送了更新,否则受影响时区的所有计算机都会有不正确的时间。因此,回到我最初的问题,TimeZoneInfo 是否会从任何 "DST source"?

获取数据?

您的问题分为两部分。我将从您首先给出的代码示例开始:

var arrivalTime = utcTime.Add(clientTimeZone.GetUtcOffset(utcTime));

这是错误地应用了偏移量。由于 utcTime 将其 Kind 属性 设置为 DateTimeKind.Utc,因此调用 Add 后的结果值也是如此。因此,您不是在调整偏移量,而是实际上选择了不同的时间点(未来 10 小时或 11 小时)。

有几种不同的方法可以代替:

  • 您可以在 TimeZoneInfo 上使用 ConvertTimeConvertTimeFromUtc 方法。结果日期和时间将与以前相同,但 Kind 将正确设置为 DateTimeKind.Unspecified.

      DateTime utcNow = DateTime.UtcNow;
      DateTime arrivalTime = TimeZoneInfo.ConvertTime(utcNow, clientTimeZone);
    
  • 您可以使用检索到的偏移量构造一个 DateTimeOffset

      DateTimeOffset utcTime = DateTimeOffset.UtcNow;
      DateTimeOffset arrivalTime = utcTime.ToOffset(clientTimeZone.GetUtcOffset(utcTime));
    
  • 您可以使用 TimeZoneInfo.ConvertTime 更轻松地创建 DateTimeOffset。这与前面的代码做同样的事情,但更清晰。

      DateTimeOffset utcTime = DateTimeOffset.UtcNow;
      DateTimeOffset arrivalTime = TimeZoneInfo.ConvertTime(utcTime, clientTimeZone);
    

对于您描述的场景,我建议使用 DateTimeOffset。您将同时拥有本地时间和与 UTC 的时差。存储整个东西。 (如果您的数据库使用 Microsoft SQL Server 之类的东西,您可以简单地使用一个 datetimeoffset 字段。)

同时拥有本地时间和偏移量的好处是,即使某些内容发生变化,您也有足够的数据来恢复世界时的实际时间点,并且您仍然拥有一种格式的数据可以通过当地时间合理化。 (或者,您可以将 UTC 时间和本地时间存储在两个单独的字段中。有时这有助于建立索引。)

关于您关于数据可靠性的第二个问题,我建议您阅读 以及我的回答。一般来说 - 是的。数据是可靠的。但是让我们解开你描述的关于政府偏差的场景。

  • 首先,您是否知道时区数据中没有记录的任何此类已经发生的偏差?看看the history on timeanddate.com, or in the sources of the IANA time zone database. If there are deviations that aren't recorded, they probably should be. Please send such information to the tz discussion list。其他人都从那里获取更改。

  • 对于 Windows,您可以在 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\AUS Eastern Standard Time\Dynamic DST 检查注册表数据。请参阅 Josh Free 的 Exploring Windows Time Zones... Microsoft 博客 post 以了解它们的工作原理。对于悉尼,记录的最后一次规则更改是在 2008 年,这与 IANA 和时间和日期信息一致。

  • 请记住,Windows 假设 2007 年悉尼的规则对于之前的所有时间点都是相同的。它没有 IANA 数据库那样的历史深度。例如,Windows 不知道 IANA 数据库中评论中提到的 2000 年奥运会的偏差。如果此类历史更改对您的应用程序很重要,那么您需要使用 NodaTime 中的 TZDB 提供程序。 (虽然我无法想象为什么您需要这些历史记录才能用于当今的停车应用程序。)

那么,如果澳大利亚政府决定在未来做出偏差会怎样呢?

  • 如果他们给出了足够的通知,那么 IANA 和 Windows 都会选择更改并通过 Windows 更新进行分发。您的 Windows 计算机会自动接收更改,并且您的应用程序会正确地接受偏差。

  • 如果他们没有提供足够的通知,那么 Microsoft 将遵循 aka.ms/time 中描述的策略,提供解决方法的指导并致力于尽快把零钱拿出来。 IANA 也可能会匆忙做出更改,但分发时间因实施而异(Linux、Java、Python、Android、Noda Time 等每次分发时间以他们自己的方式分区数据。)

过去确实发生过临时通知时区更改,并且存在问题。幸运的是,近年来它们已经减少了。我在 2016 年就此发表了博客。请参阅:On the Timing of Time Zone Changes

TL;DR:

Hence, back to my original question, would the TimeZoneInfo be fetching data from any "DST source"?

  • 对于 Windows,来源是 Microsoft 通过 Windows 更新。如果您使系统保持最新状态,那么您的应用程序将具有正确的时区数据。此类更新于 this blog.

    公布
  • 对于其他平台(Linux、OSX)上的 .NET 运行,最终来源是包含这些更新的 IANA time zone database, whose data is distributed by the platform you're running. For example, Ubuntu Linux ships a tzdata package