我必须填充tm_gmtoff和tm_zone吗? 以及,我应该使用 mktime 或 gmtime 哪个函数来获取 C



我正在尝试编写一个函数,该函数从年,月,日,小时和分钟值填充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_gmtoffgmtime不是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_gmtofftm_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_gmtofftm_zonetm_msec等。

mktime函数将timeptr指向的结构中的细分时间(表示为本地时间(转换为日历时间值,其编码与time函数返回的值的编码相同。结构的tm_wdaytm_yday组件的原始值将被忽略,其他组件的原始值不限于上述范围。...
...成功完成后,将适当设置结构的tm_wdaytm_yday组件的值,并将其他组件设置为表示指定的日历时间,但其值强制为上述范围;tm_mday的最终值在确定tm_montm_year之前不会设置.
C17dr § 7.27.2.3 2

由于mktime()只指定忽略tm_wdaytm_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.
}

如果你的代码在平台的子集上使用,它可能会在没有这种归零分配的情况下逃脱。

最新更新