6

In libc there are two functions to convert from system time to calendar time - gmtime and localtime, but only localtime has inverse function - mktime. Why there is no inverse function for gmtime, and if there shouldn't be any, why gmtime exists?

Igor Liferenko
  • 1,499
  • 1
  • 13
  • 28

2 Answers2

7

I've found this piece of code work satisfactorily:

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

which satifies the test:

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);
Robert
  • 2,330
  • 29
  • 47
2

To explain the existence of gmtime(), some context is required:

gmtime() will convert a timestamp representation (number of seconds since 1970-01-01 00:00:00) to broken-down time representation (aka, struct tm), assuming that the timestamp timezone is 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.

In the other hand, localtime() takes in consideration the [local] system timezone (including daylight saving):

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.

Note that the number of seconds since 1970-01-01 00:00:00 differ from timezone to timezone (when it was 1970-01-01 00:00:00 in New York, it clearly wasn't in, for instance, Tokyo).

The mktime() converts a struct tm to a time_t value (number of seconds since 1970-01-01 00:00:00) based on the [local] system timezone, and should not be interpreted as the inverse of any particular function (such as localtime() or gmtime()), as the inverse term may be [wrongly] interpreted as a safe cross-system conversion:

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;

There is also a non-portable function (for GNU and BSD systems) called timegm(), which assumes a UTC timezone, such as gmtime() does.

References

Blockquoted text is retrieved from parts of release 3.74 of the Linux man-pages project.

pah
  • 4,700
  • 6
  • 28
  • 37
  • 2
    By "inverse" I mean that if we take a certain timestamp X, then X = mktime(localtime(X)), but there is no such function F, that X = F(gmtime(X)), and my question is why F does not exist in libc. – Igor Liferenko Jul 11 '16 at 02:55
  • you can still use `mktime()` for that, but you need to add or subtract the UTC offset from the returned value. – pah Jul 11 '16 at 03:02
  • I can use `localtime` instead of `gmtime` the same way. Then why `gmtime` exists? – Igor Liferenko Jul 11 '16 at 03:04
  • 1
    You can also use the non-portable extension (only on GNU and BSD systems) called `timegm()` – pah Jul 11 '16 at 03:05
  • Since `gmtime()` *ignores* the local timezone, it's perfect to handle UTC time values, which is a common timezone in applications to be depoyed (and sync'd) around the globe. – pah Jul 11 '16 at 03:08
  • Anyway, this may become a *primarly* opinion based discussion, as there are other methods to process such timestamps and keep the values coherent. – pah Jul 11 '16 at 03:09
  • 1
    BTW, is `mktime` fully equivalent to `timelocal`? And does `timegm` ignore the local timezone? – Igor Liferenko Jul 11 '16 at 03:10
  • The man page claims so [for both questions]. It also provides a portable alternative to use `mktime()` without converting UTC time [structs] to local time [structs]. They explicit use the term `inverse` [which I don't really agree with that term as it may be misleading or ambiguous] – pah Jul 11 '16 at 03:14