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

3

我正在尝试将文件的创建日期修改为发布日期。我首先将类似于“2005年4月2日”的字符串转换为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 并使用它来应用更改。这将文件时间设置为2005年4月2日上午12:00:00,是正确的。但是,4月2日之后的视频被设置为上午1:00:00,确实,夏令时在2005年4月3日发生过。
我该如何确定某个日期是否在夏令时之前或之后,以便可以相应调整 st.wHour?目标是将所有时间设置为上午12:00:00。最好能够处理自60年代以来到当前的日期。
我尝试使用 TIME_ZONE_INFORMATIONGetTimeZoneInformation 但只返回了TIME_ZONE_ID_STANDARD

你正在使用哪个函数将 SYSTEMTIME 转换为 FILETIME?我正在阅读的文档说,SYSTEMTIME 可以表示协调世界时或本地时间,具体取决于所调用的函数。 - Howard Hinnant
我正在使用“SystemTimeToFileTime”。 - anon
文档中说SystemTimeToFileTime假定SYSTEMTIME已经是UTC时间。因此我不明白为什么st.wHour = 6会映射到UTC午夜。 - Howard Hinnant
2
那么问题可能在下游?我正在使用 SetFileTime 并传递 FILETIME{3921264640, 29701910},这在我的机器上会提前6个小时。 - anon
1
我认为操作系统会在2007年之前和之后都显示本地时间。而且我还怀疑它将使用2007年(及以后)的时区规则来处理2007年之前的日期,但是我可能对这个方面有所错误。如果我是正确的,那么3月第二个星期日到4月第一个星期日之间的2007年之前的日期可能会偏差1小时。 - Howard Hinnant
显示剩余9条评论
1个回答

3
一些事情:
  • 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时间。(在将文件从相机移动到计算机等情况下,这种情况经常发生)

最后,您说:

...最好能够处理追溯到60年代以及当前的日期。

很不幸,Windows时区无法追踪那么久远的历史日期。 Microsoft的时区政策 是追踪2010年以后所有有人居住的地球上的时区和夏令时规则。尽管如此,一些时区从2010年之前开始跟踪某些历史变化作为这个政策正式制定之前的文物。(它们在给定的时区内是准确的,但在所有时区中起始年份不统一)。

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

  • ICU项目支持时区和C语言实现。虽然它有点重,但如果您在应用程序的本地化方面使用它,那么还是很不错的。

  • Howard Hinnant(在上面的问题中发表评论的人)拥有一个出色的日期库,支持IANA时区。

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

请记住,IANA数据库仅保证从1970年开始的数据。您说您需要1960年的数据,虽然有一些区域的数据可以追溯到那个时期(甚至更早),但在那段时间内不能保证正确性。


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