为什么libc中没有gmtime的反函数?

Why there is no inverse function for gmtime in libc?

libc 中有两个函数可以将系统时间转换为日历时间 - gmtimelocaltime,但只有 localtime 具有反函数 - mktime。为什么 gmtime 没有反函数,如果不应该有,为什么 gmtime 存在?

要解释 gmtime() 的存在,需要一些上下文:

gmtime() 会将 timestamp 表示(自 1970-01-01 00:00:00 以来的秒数)转换为 分解时间表示 (又名,struct tm),假设 timestamp 时区是 UTC:

The gmtime() function converts the calendar time timep to broken-down time representation, expressed in Coordinated Universal Time (UTC). It may return NULL when the year does not fit into an integer. The return value points to a statically allocated struct which might be overwritten by subsequent calls to any of the date and time functions.

另一方面,localtime()考虑了[本地]系统时区(包括夏令时):

The localtime() function converts the calendar time timep to broken- down time representation, expressed relative to the user's specified timezone. The function acts as if it called tzset(3) and sets the external variables tzname with information about the current timezone, timezone with the difference between Coordinated Universal Time (UTC) and local standard time in seconds, and daylight to a nonzero value if daylight savings time rules apply during some part of the year.

请注意,自 1970-01-01 00:00:00 以来的秒数因时区而异(在纽约 1970-01-01 00:00:00 时,显然不在,例如东京)。

mktime() 根据[本地] 系统时区将 struct tm 转换为 time_t 值(自 1970-01-01 00:00:00 以来的秒数),并且 不应被解释为任何特定函数(例如localtime()gmtime())的,因为inverse 术语可能被 [错误地] 解释为安全的跨系统转换:

The mktime() function converts a broken-down time structure, expressed as local time, to calendar time representation. The function ignores the values supplied by the caller in the tm_wday and tm_yday fields. The value specified in the tm_isdst field informs mktime() whether or not daylight saving time (DST) is in effect for the time supplied in the tm structure: a positive value means DST is in effect;

还有一个不可移植的函数(用于 GNU 和 BSD 系统)称为 timegm(),它采用 UTC 时区,例如 gmtime()

参考资料

块引用的文本是从 Linux 手册页项目的 3.74 版的部分内容中检索到的。

我发现这段代码工作令人满意:

namespace std {
    time_t timegm(tm* _Tm) 
    {
        auto t = mktime(_Tm);
        return t + (mktime(localtime(&t)) - mktime(gmtime(&t)));
    }
}

满足测试:

auto t1 = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
auto t2 = std::timegm(std::gmtime(&t1));
EXPECT_EQ(t1, t2);