确定夏令时是否适用于特定日期

Determine if daylight savings applies to a certain date

我正在尝试将文件的创建日期修改为发布日期。我首先将诸如“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 转换为 FILETIME 并使用它来应用更改。

这会将文件时间设置为 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.

几件事:

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

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

  • SystemTimeToFileTime 函数接受指向被解释为 UTC 的 SYSTEMTIME 的指针,以及 returns 指向被解释为 UTC 的 FILETIME 的指针也是在UTC方面。不涉及本地时区。

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

  • 除了 TIME_ZONE_STANDARD,您没有看到 GetTimeZoneInformation 的任何回复,因为它告诉您当前有效的内容 - 它与您可能的日期断开连接与

  • 一起工作
  • 您不应该试图自己弄清楚如何针对 DST 进行调整。 DST 不是普遍适用的,它并不总是一个小时的偏移量。相反,使用将您关心的时区转换为 UTC 的函数。

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

  • 构造一个 SYSTEMTIME,将您关心的日期和时间组件设置为零(默认值)。

  • 使用GetDynamicTimeZoneInformation函数获取本地时区。您希望 "dynamic" 版本考虑到 Windows 知道的标准时间和 DST 规则的任何历史差异,而不仅仅是当前的规则集。

  • 将这两个值传递给 TzSpecificLocalTimeToSystemTimeEx 函数。它将把输入时间解释为输入时区(这是系统的本地时区)。结果是一个 FILETIME 是根据 UTC。

  • 将该值传递给 SetFileTime,它期望以 UTC 形式输入。

此外,请记住 not all file systems track file times in the same way:

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

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

最后,你说:

... Preferably this would work on dates dating back to the 60s as well as current.

不幸的是,Windows 时区不会跟踪那么远的历史日期。 Microsoft's time zone policy 用于跟踪 2010 年和地球上所有人口稠密地区的时区和 DST 规则。虽然,几个时区跟踪 2010 年之前的 一些 历史变化,作为此政策正式化之前的人工产物。 (它们在给定区域内是准确的,只是在所有区域的开始年份不统一)。

如果历史日期对您的应用程序很重要,您将需要一种非常不同的方法 - 一种不使用 Windows 时区数据,而是使用 IANA time zone database. (More on these in the timezone tag wiki 的方法。)以下是您可以探讨的一些想法:

  • ICU project has time zone support and a C implementation。对于这个目的来说它有点重,但是如果您将它用于应用程序的其他本地化方面则很好。

  • Howard Hinnant(对上述问题发表了评论)在 IANA 时区支持方面表现出色 Date library

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

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