【发布时间】:2017-07-06 07:29:09
【问题描述】:
我正在尝试使用 std::chrono::time_point<std::chrono::system_clock> 将当前年份存储在 1970 年之前的日期中,但是我遇到了一个关于从其内容读取到 std::tm 结构的问题。
我首先将time_point 转换为time_t,然后读取其值以获得tm_year 值。但是,当尝试这样做时,代码在使用 localtime_s 时会失败,但在我使用 gmtime_s 时会成功。这仅适用于 1970 年 1 月 1 日之前的日期,之后的日期使用这两个函数都可以正常工作。
下面的代码重现了错误。如果terstGmTimeVsLocalTime 用utc=true 调用它可以工作,如果它用utc=false 调用它不会产生正确的输出。
#include <iomanip>
#include <time.h>
#include <iostream>
void testGmTimeVsLocaltime(const bool& utc) {
// Create time
std::tm timeInfoWrite = std::tm();
timeInfoWrite.tm_year = 1969 - 1900; // Year to parse, here it is 1969
timeInfoWrite.tm_mon = 0;
timeInfoWrite.tm_mday = 1;
timeInfoWrite.tm_hour = 1;
timeInfoWrite.tm_min = 0;
timeInfoWrite.tm_sec = 0;
timeInfoWrite.tm_isdst = -1;
std::chrono::time_point<std::chrono::system_clock> timePoint = std::chrono::system_clock::from_time_t(utc ? _mkgmtime(&timeInfoWrite) : std::mktime(&timeInfoWrite));
// Convert to time_t
std::time_t timeT = std::chrono::system_clock::to_time_t(timePoint);
// Read values
std::tm timeInfoRead;
if (utc) {
gmtime_s(&timeInfoRead, &timeT);
} else {
localtime_s(&timeInfoRead, &timeT);
}
// Output result
std::cout << (timeInfoRead.tm_year + 1900) << '\n';
// Wait for input
std::getchar();
}
int main() {
testGmTimeVsLocaltime(true); // Set to false to show bug
return 0;
}
utc=true 输出 1969,正如预期的那样。但是,utc=false 输出 1899(可能是因为发生错误并且 tm_year 被设置为 -1)。
我有什么遗漏吗? The documentation 没有具体说明 localtime_s 在 1970 年 1 月 1 日之前的日期应该失败。
如果有什么不同,我使用的是 Windows 10 x64。
【问题讨论】:
-
MSDN 文档建议它会失败:“1970 年 1 月 1 日午夜之前。”见msdn.microsoft.com/en-us/library/bf12f0hc.aspx
-
@RichardCritten 啊,我知道我一定一直在使用特定于操作系统的实现。没有意识到
localtime_s不是标准的一部分。在 1970 年 1 月 1 日午夜之前,是否会有另一个函数不会因值而失败?或者也许是另一种策略?我想我可以检索 UTC 和本地时间之间的差异,只需使用gmtime_s函数,然后在需要本地时间时将差异添加到结果中,但似乎可以做得更简单。 -
你链接的文档说 "Converts given time since epoch";其中 epoch 是实现决定时间开始的时间。在 Windows 上,有一些操作系统调用可以随时工作,但我不知道任何标准 API。
-
您的代码因
localtime_s和gmtime_s而失败。只是失败的方式不同。尝试在这两种情况下添加std::cout << "timeT = " << timeT << '\n';; you'll see that it's-1`。 (另外,你需要#include <chrono>。)将1969 - 1900更改为1959 - 1900,它仍然会输出1969。