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

Determine if daylight savings applies to a certain date

本文关键字:日期 适用于 是否 夏令时      更新时间:2023-10-16

我正在尝试将文件的创建日期修改为发布日期。我首先将诸如"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的指针,并返回指向同样以 UTC 表示的FILETIME的指针。 不涉及本地时区。

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

  • 除了TIME_ZONE_STANDARD之外,您没有看到GetTimeZoneInformation的任何响应,因为这告诉您当前有效的内容 - 这与您可能正在使用的日期断开连接。

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

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

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

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

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

  • 将该值传递给SetFileTime,后者需要以 UTC 为单位的输入。

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

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

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

最后,你说:

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

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

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

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

  • Howard Hinnant(对上述问题发表了评论)拥有出色的日期库,并支持 IANA 时区。

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

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