【问题标题】:Determine if daylight savings applies to a certain date确定夏令时是否适用于某个日期
【发布时间】:2020-04-29 03:29:00
【问题描述】:

我正在尝试将文件的创建日期修改为发布日期。我首先将诸如“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 转换为FILETIME?我正在阅读的文档说 SYSTEMTIME 可以表示 UTC 或本地时间,具体取决于被调用的函数。
  • 我正在使用SystemTimeToFileTime
  • 文档说SystemTimeToFileTime 假定SYSTEMTIME 已经在UTC。因此我不明白 st.wHour = 6 是如何映射到午夜 UTC 的。
  • 那么问题可能出在下游?我正在使用SetFileTime 并传递FILETIME{3921264640, 29701910},这会在我的机器上提前 6 小时产生。
  • 我认为操作系统会在 2007 年之前和之后向您显示本地时间。而且我也怀疑它将使用 2007 年(及以后)时区规则进行预2007 年的日期,但我在这方面可能是错误的。如果我是对的,那么 2007 年之前 3 月的第 2 个星期日和 4 月的第一个星期日之间的日期可能会延迟 1 点。

标签: c++ windows datetime dst


【解决方案1】:

一些事情:

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

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

  • SystemTimeToFileTime 函数接受指向 SYSTEMTIME 的指针,该指针被解释为 UTC,并返回指向 FILETIME 的指针,该指针也是 UTC 格式。不涉及本地时区。

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

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

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

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

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

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

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

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

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

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

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

最后,你说:

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

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

如果历史日期对您的应用程序很重要,您将需要一种非常不同的方法 - 一种不使用 Windows 时区数据,而是使用 IANA time zone database 的方法。 (更多信息请参见the timezone tag wiki。)以下是您可以探索的一些想法:

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

  • Howard Hinnant(对上述问题发表评论)拥有出色的 Date library 并支持 IANA 时区。

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

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

【讨论】:

    猜你喜欢
    • 2013-05-25
    • 2013-11-20
    • 1970-01-01
    • 2010-09-09
    • 2010-11-06
    • 2011-04-30
    • 2019-08-22
    • 2013-11-16
    • 2015-09-17
    相关资源
    最近更新 更多