首页 > 解决方案 > 确定夏令时是否适用于某个日期

问题描述

我正在尝试将文件的创建日期修改为发布日期。我首先将诸如“2 April 2005”之类的字符串转换为std::tm. 然后我创建一个SYSTEMTIME如下:

std::tm dt = from_string("2 April 2005");
SYSTEMTIME st { 0 };
st.wYear = dt.tm_year + 1900; // dt is years from 1900
st.wMonth = dt.tm_mon + 1; // dt is month index 0
st.wDay = dt.tm_mday;
st.wHour = 6; // FILETIME is based on UTC, which is 6 hours ahead

之后,我将其转换SYSTEMTIME为 aFILETIME并使用它来应用更改。

这将设置正确的文件时间2 April 2005 12:00:00 AM。然而,4 月 2 日之后的视频被设置为1:00:00 AM,果然,夏令时发生在 2005 年 4 月 3 日。

如何确定某个日期是在夏令时之前还是之后,以便进行st.wHour相应调整?目标是将所有时间设置为12:00:00 AM。最好这适用于可追溯到 60 年代和当前的日期。

我尝试使用TIME_ZONE_INFORMATIONGetTimeZoneInformation但我只回来了TIME_ZONE_ID_STANDARD

标签: c++windowsdatetimedst

解决方案


一些东西:

  • SYSTEMTIME只是一个简单的结构。它具有年、月、周几、日(月)、小时、分钟、秒和毫秒的单独字段。它既不是 UTC 也不是本地时间,也不是其他任何东西。在将其传递给函数之前,不会考虑到这一点。

  • FILETIME是另一种简单的结构。它表示自 1601-01-01 午夜以来的 100 纳秒间隔数。许多文档会让您认为它始终使用 UTC,但有类似的功能FileTimeToLocalFileTime反驳了这一点。因此,就像 一样SYSTEMTIME,由每个单独的函数来决定如何解释它。

  • SystemTimeToFileTime函数接受指向SYSTEMTIME被解释为 UTC 的 a 的指针,并返回指向FILETIME同样以 UTC 表示的 a 的指针。不涉及本地时区。

  • 不要尝试自己调整当地时间(st.wHour = 6在您的代码中)。小时需要根据时区和 DST 有所不同。

  • GetTimeZoneInformation除了 之外,您没有看到任何回复TIME_ZONE_STANDARD,因为这会告诉您当前有效的内容 - 这与您可能正在使用的日期无关。

  • 您不应该试图弄清楚如何自己调整 DST。DST 并非普遍适用,而且并不总是一小时偏移。相反,请使用将您关心的时区转换为 UTC 的函数。

最终听起来你在问如何将文件时间设置为特定日期本地时区的午夜。因此,我建议您执行以下步骤:

  • SYSTEMTIME使用您关心的日期和时间组件设置为零(默认值)构造 a 。

  • GetDynamicTimeZoneInformation使用该函数获取本地时区。您需要“动态”版本,以便考虑 Windows 知道的标准时间和 DST 规则的任何历史差异,而不仅仅是当前的规则集。

  • 将这两个值传递给TzSpecificLocalTimeToSystemTimeEx函数。它将输入时间解释为输入时区(这是系统的本地时区)。结果是以FILETIMEUTC 表示的。

  • 将该值传递给SetFileTime,它需要 UTC 格式的输入。

另外,请记住,并非所有文件系统都以相同的方式跟踪文件时间

  • NTFS 存储实际的 UTC 时间,因此您可以在计算机之间移动文件,即使计算机具有不同的时区设置,时间戳也表示世界时的同一点。

  • FAT 及其变体存储本地时间。因此,当您调用 时SetFileTime,Windows 会从 UTC 转换为本地时区并写入结果。如果您随后在具有不同时区的系统上打开文件,则日期将在时区中解释,从而导致不同的 UTC 时间。(在将文件从相机移动到计算机时,这种情况经常出现在 U 盘、存储卡等上。)

最后,你说:

...最好这适用于可追溯到 60 年代和当前的日期。

不幸的是,Windows 时区并没有跟踪那么远的历史日期。 Microsoft 的时区政策是跟踪 2010 年和地球上所有人口稠密地区的时区和 DST 规则。虽然,有几个时区会跟踪2010 年之前的一些历史变化,因为这是该政策正式制定之前的产物。(它们在给定区域内是准确的,只是在所有区域的起始年份不统一)。

如果历史日期对您的应用程序很重要,您将需要一种非常不同的方法 - 一种不使用 Windows 时区数据,而是使用IANA 时区数据库的方法。(有关时区标签 wiki 的更多信息。)以下是您可以探索的一些想法:

  • ICU 项目具有时区支持和C 实现。对于这个目的来说它有点重,但如果您将它用于应用程序的其他本地化方面,那就很好了。

  • Howard Hinnant(他对上述问题发表了评论)有一个​​出色的Date 库,支持 IANA 时区。

  • 或许可以从Windows.Globalization.CalendarUWP 类中获得您需要的东西。我还没有测试它是否会使用来自 IANA 数据的历史规则,或者它是否使用 Windows 数据。(如果我有机会检查,我会回来更新这个答案。)

请记住,IANA 数据库只保证从 1970 年开始。你说你需要从 1960 年开始,虽然有一些区域有数据(还有一些更早),但不能保证那个时期的正确性。


推荐阅读