我正在尝试编写一个函数,该函数从年,月,日,小时和分钟值填充struct tm
。
应用程序不处理时区信息,即我们假设输入数据时区与应用程序和用户时区匹配。
我试过这个:
void timeCreate(struct tm* pTm1, int year, int month, int day, int hour, int minute) {
pTm1->tm_year = year - 1900;
pTm1->tm_mon = month - 1;
pTm1->tm_mday = day;
pTm1->tm_hour = hour;
pTm1->tm_min = minute;
pTm1->tm_sec = 0;
pTm1->tm_isdst = -1;
mktime(pTm1);
}
如果我不做任何其他事情,我会得到 mktime 设置的 CET 时间(CET 是我的本地时区(,似乎 mktime 更改了时间和日期,所以它不再是 2014/01/01 00:00,而是前一天。
如果我这样做:
pTm1->tm_gmtoff = 0;
pTm1->tm_zone = "UTC";
然后我没有得到任何更正。
我也看到了gmtime()
而不是mktime()
的功能。哪种是设置此结构的"正确"方法?
我也得到了一个随机值tm_wday
,该值可以自动计算吗?
mktime
仅适用于本地时间,并且其行为受规范约束,因此它无法检查任何非标准字段struct tm
,如tm_gmtoff
。gmtime
不是mktime
的类似物,而是localtime
的模拟——它以相反的方向转换。如果你想将struct tm
格式的分解UTC时间规范化/转换为time_t
,你需要使用非标准但广泛使用的timegm
函数,或者自己写出来。值得庆幸的是,POSIX准确地指定了公式:
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
请注意,如果按照写入 C 代码的方式复制,则会充满整数溢出,因此需要引入一些类型提升或其他修复以使其有效。
如果您需要规范化方面timegm
也执行,但需要可移植地完成而不依赖于不可移植的timegm
,则可以在使用上述公式反转它并获得规范化结果后调用gmtime_r
。
我是否必须填充
tm_gmtoff
并tm_zone
?
这是设置此结构的"正确"方法?
对于可移植代码,是的。
C 规范有
"TM结构应至少包含以下成员,按任何顺序"(C规范(-->struct tm
至少具有以下9个成员:
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
并且可能包括其他如tm_gmtoff
、tm_zone
、tm_msec
等。
mktime
函数将timeptr
指向的结构中的细分时间(表示为本地时间(转换为日历时间值,其编码与time
函数返回的值的编码相同。结构的tm_wday
和tm_yday
组件的原始值将被忽略,其他组件的原始值不限于上述范围。...
...成功完成后,将适当设置结构的tm_wday
和tm_yday
组件的值,并将其他组件设置为表示指定的日历时间,但其值强制为上述范围;tm_mday
的最终值在确定tm_mon
和tm_year
之前不会设置.
C17dr § 7.27.2.3 2
由于mktime()
只指定忽略tm_wday
和tm_yday
,所有其他成员都可以对结果做出贡献。 没有 C 规范将结果限制为上述所需成员中的 7 个。
良好的编程实践会在调用mktime()
之前将所有未显式初始化/分配的成员清零。
void timeCreate(struct tm* pTm1, int year, int month, int day, int hour, int minute) {
memset(pTm1, 0, sizeof *pTm1); // Zero all members.
pTm1->tm_year = year - 1900;
pTm1->tm_mon = month - 1;
pTm1->tm_mday = day;
pTm1->tm_hour = hour;
pTm1->tm_min = minute;
pTm1->tm_sec = 0;
pTm1->tm_isdst = -1;
mktime(pTm1);
// recommend to return the result of mktime() so caller can ID invalid timestamps.
}
如果你的代码在平台的子集上使用,它可能会在没有这种归零分配的情况下逃脱。