确定夏令时是否适用于特定日期
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_INFORMATION
和GetTimeZoneInformation
但我只得到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.Calendar
UWP 类获取所需的内容。 我还没有测试它是否会使用 IANA 数据中的历史规则,或者它是否使用 Windows 数据。 (如果我有机会检查,我会回来更新这个答案。
请记住,IANA 数据库仅保证从 1970 年开始。 你说你需要从 1960 年开始,虽然有一些区域有这方面的数据(还有一些更古老),但不能保证那个时期的正确性。
- FLTK 2.0构建和演示,适用于VS2019的2011年左右的代码库
- C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd
- "string.h"在构建适用于iOS的qt应用程序中找不到消息
- 适用于 WebView2 旧版本的示例应用程序
- 在 NVIDIA GEFORCE GTX 1050 上下载适用于 Windows 10 的 openCL 1.2
- __attribute__(优化(0))) 是否适用于"recursively"?
- 为什么 std::erase(std::erase_if) 不是适用于<algorithm>任何容器的模板?
- 使用一个参数的模板函数时出错(适用于 2)
- 使用 适用于 Android 和 iOS 的 tf-lite C++ API
- 为什么这适用于 G++ 而不是 CLANG?
- 适用于 macOS 的 Xcode 应用程序。这就是我设置从USB麦克风输入获取音频的方式。一年前工作,现在没有了。为什么
- 适用于 Linux 的 c++ 上的代理脚本
- 为什么我的 SFINAE 表达式不再适用于 GCC 8.2?
- 使输出流式处理运算符适用于 boost::variant<std::vector<int>、int、double 的正确方法是什么>
- 有没有适用于Windows.lib文件的GNU二进制文件描述符(BFD)
- 模板函数仅适用于VS
- 如何在cmake中包含适用于g++或viceversa的库
- 适用于win32、linux、mac的POSIX C包装器
- WinDBG适用于从Visual Studio 2015保存的转储,但不适用于任务管理器。显示异常代码"not found"
- 从uint8_t到NPY_UINT16 PyArray_SimpleNewFromData.适用于Linux,但不适用于