我必须填充 tm_gmtoff 和 tm_zone 吗?以及,我应该使用哪个函数 mktime 或 gmtime 在 C 中获得无时区的日期时间?

Do I have to populate tm_gmtoff and tm_zone? and, Which function shall I use mktime or gmtime to get a timezone-less datetime in 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()。 "right" 设置此结构的方法是什么?

另外我得到了一个随机值tm_wday,这个值可以自动计算吗?

mktime 仅适用于本地时间,并且其行为受规范约束,因此它 无法 检查 struct tm 的任何非标准字段,例如 tm_gmtoffgmtime 不是 mktime 的模拟,而是 localtime 的模拟——它向相反的方向转换。如果你想要 struct tm 格式的 normalization/conversion 分解 UTC 时间到 time_t,你需要使用非标准但广泛使用的 timegm 函数,或者将其写出来你自己。还好POSIXexactly specifies公式:

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反转它并获得标准化结果。

Do I have to populate tm_gmtoff and tm_zone?
Which would be the "right" way of setting up this struct?

对于可移植代码,是的。

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

The mktime function converts the broken-down time, expressed as local time, in the structure pointed to by timeptr into a calendar time value with the same encoding as that of the values returned by the time function. The original values of the tm_wday and tm_yday components of the structure are ignored, and the original values of the other components are not restricted to the ranges indicated above. ...
... On successful completion, the values of the tm_wday and tm_yday components of the structure are set appropriately, and the other components are set to represent the specified calendar time, but with their values forced to the ranges indicated above; the final value of tm_mday is not set until tm_mon and tm_year are determined.
C17dr § 7.27.2.3 2

由于仅指定 mktime() 忽略 tm_wdaytm_yday所有其他成员都可能对结果有贡献 。没有 C 规范将结果限制为上述必需成员中的 7 个。

良好的编程习惯会在调用 mktime().

之前将所有未明确 initialized/assigned 的成员归零
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.
}

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