我们的系统从供应商处接收 ASCII 格式的数据“20210715083015”。这个时间是美国东部时间,已经调整为夏令时。
我们的后端需要自纪元以来的纳秒时间。我正在尝试使用 mktime() 进行转换,但 mktime() 坚持为 DST 添加一个小时,即使我强制 tm_isdst 为 0。
这是代码片段:
std::tm tmstr{};
<breakdown ASCII time into tm structure>
tmstr.tm_isdst = 0;
cout << "isdst before: " tmstr.tm_isdst;
time_t seconds = std::mktime(&tmstr);
cout << ", isdst after: " tmstr.tm_isdst << endl;
这是输出:
isdst before: 0, isdst after: 1
它忽略设定值0,并应用它自己的转换。
如何使用 mktime() 或类似的东西,而不尝试将时间调整为我的时区?我不想在内部设置时区,我只是希望它能够从 tm 结构直接转换为秒。
这是 g++ 版本 7.3.1,在 Redhat 版本 6.10 下。
忽略设定值0
不,这并不是忽略该成员。
mktime()
考虑到.tm_isdst
,然后将所有struct tm
成员调整为该日期的通常值,因此在OP的情况下预计会更改为.tm_isdst
和.tm_hour
,因为 tmstr
7 月 15 日为“20210715”的白天时间。
如何使用
或类似的东西,而不尝试将时间调整为我的时区?mktime()
mktime()
是当地时间换算。通常的方法是让 mktime()
推导出夏令旗。
tmstr.tm_isdst = -1;
这个时间是美国东部时间,已经调整为夏令时。
不,您断言它是美国标准东部时间(EST)。美国东部时间 (ET) 有夏令时调整。
将您的时区从包含夏令时和标准时段的时区更改为仅包含标准时段的时区。研究
setenv()
。也许很简单:
// Implementation dependent code.
setenv("TZ", "EST5", 1);
tzset();
time_t seconds = mktime(&tmstr);
缺少关键问题:
time_t
结果正确吗?
暂时忽略
tmstr
。报告的 time_t
值是多少?预期的 time_t
值是多少?
我怀疑OP确实得到了正确的
time_t
值,只是不是struct tm
中的预期结果。
希望您可以使用
timegm()
函数(不要与 gmtime()
函数混淆......有关更多信息,请参阅下文)。
mktime()
函数有一个状态。如果上次使用时 isdst
设置为 0,则使用 isdst = -1
将使用 0。如果上次使用 isdst
设置为 1,则使用 isdst = -1
将使用 1。这是仅当在时间更改发生时(在该 1 小时窗口内)调用该函数时,才为 true。其他函数也不太可能改变该变量(即 localtime()
也可能影响该状态,我还没有测试过)。
// a date when the time change happened (Sun Oct 26 01:08:48 PST 1980)
time_t et = 341399328;
struct tm t = {};
localtime_r(&et, &t);
struct tm ot = {};
ot.tm_year = t.tm_year;
ot.tm_mon = t.tm_mon;
ot.tm_mday = t.tm_mday;
ot.tm_hour = t.tm_hour;
ot.tm_min = t.tm_min;
ot.tm_sec = t.tm_sec;
ot.tm_isdst = 1;
std::cerr << " +--> " << mktime(&ot) << " (1)\n";
ot.tm_isdst = 0;
std::cerr << " +--> " << mktime(&ot) << " (0)\n";
ot.tm_isdst = -1;
std::cerr << " +--> " << mktime(&ot) << " (-1)\n"; // same as 0
ot.tm_isdst = 0;
std::cerr << " +--> " << mktime(&ot) << " (0)\n";
ot.tm_isdst = 1;
std::cerr << " +--> " << mktime(&ot) << " (1)\n";
ot.tm_isdst = -1;
std::cerr << " +--> " << mktime(&ot) << " (-1)\n"; // same as 1
输出应始终与输入相同(即从
time_t
转换为 struct tm
,反之亦然)。相反,我得到:
341399328 -> 1980/10/26 1:8:48
+--> 341395728 (1)
+--> 341399328 (0)
+--> 341399328 (-1)
+--> 341399328 (0)
+--> 341395728 (1)
+--> 341395728 (-1)
因此,或多或少,您不能相信
mktime()
转换。
现在有两个新功能:
timegm()
和timelocal()
。 timelocal()
功能与mktime()
相同。 timegm()
完全忽略区域设置时区,因此您可以正确地来回转换 UTC 时间。相反的是 gmtime()
.
换句话说:
time_t
至 struct tm
与 gmtime()
struct tm
至 time_t
与 timegm()
且 (1) 中的输入
time_t
始终等于 (2) 中的输出 time_t
。
在这里发帖更容易...
从
mktime()
手册页来看,您没有阅读所写内容...
函数将以本地时间表示的细分时间结构转换为日历时间表示形式。该函数忽略调用者在mktime()
和tm_wday
字段中提供的值。tm_yday
字段中指定的值通知tm_isdst
夏令时 (DST) 是否在mktime()
结构中提供的时间内生效:正值表示 DST 生效;零表示 DST 未生效;负值表示tm
应(使用时区信息和系统数据库)尝试确定 DST 在指定时间是否生效。mktime()
函数修改mktime()
结构的字段如下:tm
和tm_wday
设置为根据其他字段的内容确定的值;如果结构成员超出其有效区间,则会对其进行标准化(例如,将 10 月 40 日更改为 11 月 9 日);tm_yday
分别设置(无论其初始值如何)为正值或 0,以指示 DST 在指定时间是否生效。调用tm_isdst
还会设置外部变量mktime()
以及有关当前时区的信息。tzname
似乎没有办法阻止 mktime() 应用夏令时。但它确实告诉我们是否确实增加了额外的时间。所以一个简单的解决方法如下......
time_t t = mktime( &tmst );
if(tmst.tm_isdst == 1)
t -= 60*60;