As noted above, while time_t
usually represents seconds elapsed since Jan 1, 1970, this is not specified anywhere. An implementation which uses a different internal representation may show up any time, and any code that makes assumptions about the inner workings of time_t
will not work correctly there.
After giving it some thought, I came up with the following:
time_t mkgmtime(struct tm * pt) {
time_t ret;
/* GMT and local time */
struct tm * pgt, * plt;
ret = mktime(pt);
pgt = g_memdup(gmtime(ret), sizeof(struct tm));
plt = g_memdup(localtime(ret), sizeof(struct tm));
plt->tm_year -= pgt->tm_year - plt->tm_year;
plt->tm_mon -= pgt->tm_mon - plt->tm_mon;
plt->tm_mday -= pgt->tm_mday - plt->tm_mday;
plt->tm_hour -= pgt->tm_hour - plt->tm_hour;
plt->tm_min -= pgt->tm_min - plt->tm_min;
plt->tm_sec -= pgt->tm_sec - plt->tm_sec;
ret = mktime(plt);
g_free(pgt);
g_free(plt);
return ret;
}
One could probably optimize this further by dropping plt
(using pt
in its place and omitting the localtime()
and g_free(plt)
calls).
This should work across all implementations which expose mktime()
, gmtime()
and localtime()
, including across DST switchover dates. (mktime()
will “normalize” out-of-range values, e.g. turning Jan 35 into Feb 4; I would also expect 9:50 DST in the middle of winter to become 8:50 standard time.)
It does suffer from one potential bug: if a time zone’s UTC offset changes for reasons not reflected in the DST flag, timestamps around the cutover time may get interpreted incorrectly: The standard case is when a legislation changes its time zone (e.g. Lithuania changed from Soviet time to CET after independence, and to EET a few years later). Some legislations had double DST in mid-summer, cycling through 3 different UTC offsets per year, which the DST flag cannot represent.