Creating time_t value from a UTC date and time in C

Programming with time is difficult and error prone, for this reason I usually try to keep things in UTC so that I don’t have to worry about time zones and daylight saving offsets etc.

When in C++ I mostly use the boost::ptime library but I was surprised recently about tricky it seemed to be to initialise a time_t value for an arbitrary UTC time just using the standard C/C++ libs. We can’t use mktime() as it will figure out which time zone you’re in and assume that the time values you have passed refer to this zone, it will also apply daylight saving offsets.

Anyway to cut a long story short there are no standard functions in the C lib for this, instead your either have to use the posix timegm() function on Unix-like systems and _mkgmtime() on windows systems.

Here is an example, lets say we want to initialise a time_t value for this UTC date/time: 01/10/2020 10:16:03 (10th. Jan 2020)


#include <assert.h>
#include <time.h>
#ifndef timegm
// use _mkgmtime() on windows
#define timegm _mkgmtime
void test_utc() {
struct tm utc;
time_t tim;
struct tm* utc_out;
// Want to create a time_t value for UTC:
// 01/10/2020 10:16:03
// tm_year is time since 1900
utc.tm_year = 2020 - 1900;
// Month is zero based, i.e. Jan is month 0
utc.tm_mon = 1 - 1;
utc.tm_mday = 10;
utc.tm_hour = 10;
utc.tm_min = 16;
utc.tm_sec = 03;
utc.tm_isdst = 0;
// Get time_t val for the UTC date/time
tim = timegm(&utc); // or _mkgmtime() on windows
// Now let's use gmtime() to check all is ok
// by retrieving a UTC tm struct and comparing
// with original
utc_out = gmtime(&tim);
assert(utc.tm_year == utc_out->tm_year);
assert(utc.tm_mon == utc_out->tm_mon);
assert(utc.tm_mday == utc_out->tm_mday);
assert(utc.tm_hour == utc_out->tm_hour);
assert(utc.tm_min == utc_out->tm_min);
assert(utc.tm_sec == utc_out->tm_sec);

That seems to do the trick, although I am astounded that this isn’t covered by the standard lib. To achieve the same using just the standard lib it seems you have to either hack around with the timezone env. variable before calling mktime() or assume you know how the time_t val is constructed and build your own timegm() function…