Windows时区设置写在注册表中可靠吗?

23

我正在创建一个 C++ 项目,应该可以在多个时区工作。应用程序接收一个带有参考时区的事件,并以正确的时间在本地时区向用户图形化显示此事件。例如,柏林的用户收到来自东京的事件。从东京的事件首先转换为 UTC 时间,然后从 UTC 转换为柏林计算机本地时间,最后在用户的图形界面上显示。

为了将时间从其他时区转换为 UTC,我需要从 Windows 注册表中获取时区信息。对于将 UTC 转换为本地计算机时间,我可以使用 Windows API 的几个函数来完成。但是,现在有些时区还要考虑夏令时。我能够从 Windows 信息中轻松创建重复规则。然而,我注意到 DST 应该发生的日期在多个时区上有时会不正确。例如,“E. South America Standard Time”。Windows 提供的循环与互联网上公布的更改时间很少匹配。

如果我理解正确的话,Windows 返回的重复规则是“每年的第二个月,第二周”,但是这个规则很少与互联网上公布的正确日期相匹配,而如果规则是“每年的第二个月,第三周”就会所有日期都正确。此外,正如您在提供的屏幕截图中所看到的,Windows 注册表数据显示 DST 开始时间为 2 周(蓝色高亮),但 DST 结束时间为 3 周(红色包围),这是由我的代码正确计算的。可以在此处找到有关数据内容的说明:http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx

enter image description here

我有几个问题:

  • 我是否正确理解了循环规则?(这是MSDN对它的说法:https://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
  • 是否已知有关多个时区的问题,特别是“E. South America Standard Time”这个时区?
  • 一个明显每年定期发生在第10个月第3周的夏令时开始日期为什么被设置为第2周的值?
  • 如果时区写在Windows注册表中,是否可靠?如果不可靠,那么我应该使用Windows API的哪个函数来将带有DST的时区从另一个时区的日期转换为本地机器上设置的时区?

注意:在发布此信息之前,我已经强烈验证了我从注册表中读取的数据是否正确。我很确定这不是这种类型的错误。

注意:我正在使用Windows 7,但问题在Windows 10上仍然存在。


2
@MichaëlRoy - 不,不是“总是”。这是一个短视的常见建议,忽略了许多需要本地时间的情况。例如,调度。请不要这样说。谢谢。 - Matt Johnson-Pint
3
抱歉冒犯了,但实际上我与支持维护Windows时区的Windows服务团队一起工作,尽管它们并不理想,我们都认为IANA时区是正确的选择,但它们已经取得了长足的进步。请查看他们的博客,您会发现Microsoft没有放弃保持数据更新。我们只是没有像IANA数据一样广泛的历史范围。您还可以在此处查看MS时区策略 - Matt Johnson-Pint
6
抱歉,不能这样做。如果每天早上10点在太平洋时间举行一个事件,则不能将其存储为协调世界时(UTC)。否则,在夏令时更改后,您会相差一个小时。有关详细信息,请参见此处和此处。您也不能可靠地将整个日期(例如生日、纪念日、入职日期等)转换为UTC。 - Matt Johnson-Pint
3
假设系统得到了适当的维护(例如使用最新可用的DST信息进行更新,与正常运行的时间服务器同步等),你的方法将尽可能地可靠。即使由IANA、Microsoft等进行了尽可能完善的处理,也无法做到100%防弹。用户可以手动设置时间和时区信息,并且可能设置不正确。夏令时(甚至有些情况下是时区)实际上是出于政治原因而设定的,因此可能随意更改 - 无论各种系统得到多好的维护。 - Peter
1
@MichaëlRoy - 我很乐意讨论,但这些问题确实在我提供的链接中得到了解决,我感觉我们正在篡夺原始问题的讨论。 - Matt Johnson-Pint
显示剩余7条评论
2个回答

74
作为一名微软员工,我在Windows时区数据方面有着重要的参与。请放心,微软非常努力地确保发布更新以尽可能准确地维护Windows时区数据。
其中存在一些挑战,包括政府规定的时区更改时间。例如,我们最近发布了关于斐济、塞浦路斯、苏丹、汤加、纳米比亚和特克斯和凯科斯群岛即将发生的Windows时区更改的通知。在某些情况下,我们可以满足这些不同政府制定的有效日期。但在某些情况下,我们无法做到这一点,因为政府没有提供足够的提前通知时间。
考虑最近苏丹的情况,他们在IANA tz邮件列表于2017年10月17日通知了将于2017年11月1日生效的变更。 IANA已经处理了此更改,Microsoft也是如此。但由于短时间内通知,以及在Windows操作系统中处理此类更改需要的时间,将需要一段时间才能创建新的"苏丹标准时间"时区在Windows时区数据中。因此,我们发布了临时指导方针,暂时使用其他时区,等到有正确反映受影响地区时间完整历史记录的数据后再切换回来。
关于您提到的Windows时区"E.南美洲标准时间",时区数据是正确的。如果您展开注册表条目,您会发现有一个名为"Dynamic DST"的子键,其中包含逐年更改数据。对于某些时区,这根本不必要,而对于其他时区,您会发现很少的数据,因为同一规则每年都会重复。但是在巴西的情况下,您会注意到动态DST条目为2009年至2040年。

Windows TZ Registry Data

(点击图像查看完整分辨率,并注意我手动标记为红色的随年份变化的区域)

动态 DST条目支持Win32 DYNAMIC_TIME_ZONE_INFORMATION结构和相应的"dynamic"命名的API,例如GetDynamicTimeZoneInformation。(它们还支持.NET Framework中的System.TimeZoneInfo类。)这些API中的大多数自Windows Vista / Server 2008以来已经存在,其他一些则在Windows 7 / Server 2012中出现。

请注意,来自当前年份动态规则的TZI条目被内部Windows进程复制到父键中。这支持使用Win32 TIME_ZONE_INFORMATION结构的API,这些API自Windows 2000以来一直存在。

回答您提出的具体问题:

我是否正确理解了重复规则?

您引用的特定规则仅适用于2017年,它表示夏令时在第二个月(2月)的第三个星期六以当地时间23:59:59.999结束,然后在第十个月(10月)的第二个星期六以当地时间23:59:59.999开始。请记住,巴西位于南半球,因此夏令时在年末开始,在下一年早期结束。

此外,你可能会想知道为什么周六的时间是23:59:59.999而不是周日的00:00:00.000。这是历史的产物。过去曾有某些程序和进程错误地使用<=而不是<来评估转换,因此会意外地将时钟向前移动一毫秒进入下一天,然后又回到当前天,再次进入下一天。由错误更改日期引发的事件可能会导致进一步的问题。微软已尽力修复这些错误,但仍然选择偏差1毫秒,而不是冒着有一天在新的地方出现问题的风险。(这仅适用于恰好在午夜发生转换的情况。)

是否存在关于几个时区,特别是“E.南美洲标准时间”的已知问题?

对于该时区没有已知问题,但是已知Windows时区(即使具有动态DST数据)最多只能支持单年内两个时区转换。因此,在像摩洛哥每年转换四次这样的地方,存在一些内部解决方法,使得“当前”时区数据与本地时间同步,但不能在任何给定时间正确表示整年的所有时间点。

我们目前也没有适用于南极洲特洛尔站的时区映射。原因是从2005年到2015年,这个研究站(人口少于50)每年都要切换三个不同的偏移量(UTC+0、UTC+1和UTC+2)。

如果您的应用程序对上述情况有关键依赖,则建议使用具有IANA数据源的API,而不是Win32 API。

为什么夏令时开始日期明显在每年的第10月的第3周,却被设置在第2周?

是的,如上所述,这是由于1毫秒的有意错误。在巴西部分实行夏令时的地区,2017年夏令时从10月15日星期日00:00:00.000开始。那是本月的第三个星期日。 1毫秒之前是10月14日星期六23:59:59.999,这是本月的第二个星期六。这可能每年都不同,这就是为什么需要动态夏令时数据的原因。

Windows注册表中写入的时区是否可靠?如果不可靠,哪个Windows API函数应该用于将具有DST的时区从写入在与本地机器上设置的时区不同的日期中进行转换?

如果您正在使用Win32 API,则它们会自己使用注册表数据,因此无需直接处理注册表数据。您应该优先选择“动态”版本的API,因为它们可以正确地考虑动态DST数据的年度变化。有时这些被标记为“Ex”。例如,您所询问的函数最好由TzSpecificLocalTimeToSystemTimeEx函数处理。

...

如果你的应用程序能够避免使用Windows时区数据,我建议你这样做。最好使用IANA数据源或者派生自它们的数据源。有很多方法来处理IANA时区数据。在WinRT/UWP中,像Windows.Globalization.CalendarWindows.Globalization.DatetimeFormatting.DateTimeFormatter这样的新Windows API确实使用IANA时区,这是未来的发展方向。在标准C++领域,我强烈推荐使用Howard Hinnant的date/tz库ICU项目提供的库。还有其他几个可行的选择。

1
非常感谢您提供的出色答案,它解释了我无法理解的一些问题,例如为什么这些特定的时间更改始于前一天,在时区标准中提到的实际日期和时间之前1毫秒。现在我知道该怎么做来解决我的问题,再次感谢您。 - Jean-Milost Reymond
1
ICU C API + CLDR 数据现在已经包含在 Windows 中,因此如果您可以接受依赖于最新的操作系统版本,那么您可以使用这些 API 而无需自己重新分发(相当大的)软件包。 - Peter Torr - MSFT
请问,您能为我澄清一下吗?如果新的Windows更新包已经下载并安装在电脑上,那么注册表中的时区才会被更新(如果需要)。我的理解是正确的吗? - RomanG

1

很高兴看到Matt的文章中有如此详细的内容。

关于“是否已知存在多个时区问题,特别是'E. South America Standard Time'?”

除了Matt对Windows时区数据的极其详细和全面的贡献外,还有其他机构、非政府组织和政府机构的广泛审核,这些都在其他地方有所概述。例如,在像摩洛哥这样的地方,不仅每年要转换四次,而且信息还受到政府设置该地区夏令时的官方观察的影响,有时会出现很少的提前时间,从而导致额外的发布和部署挑战。(更不用说当政府多次修改决策时。)

因此,对于一些具有关键依赖关系的应用程序,可能更喜欢使用 IANA 源的 API 或调用 Windows.Globalization 中的时区数据。所有这些都是及时的,如果您原谅双关语的话,在北半球我们即将进入冬令时。

1
这是在北美洲的时间更改之前,就像你说的那样,但在欧洲的时间更改之后。我想这个星期和春季的几个星期都处于同样的情况下,可能是一年中某些人最不喜欢的时候。 - Daniel H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接