I want to convert a UTC date & time given in numbers for year, month, day, etc. to a time_t. Some systems offer functions like mkgmtime
or timegm
for this purpose but that is not standard and does not exist on my Solaris system.
The only solution I have found so far involves setting the local time zone to UTC with setenv and then call mktime
. However this approach is not thread-safe, slow, not portable and even generates a memory leak on my system.
I have also seen approaches that tried to determine the current UTC offset using gmtime
and then adding that to the result of mktime
. But as far as I have seen all those approaches had gaps. After all, the conversion from the local time to UTC is not unique.
What do you think is the best solution?
I have decided to implement my own version of mkgmtime and it was easier than I thought.
My main concern was that
mkgmtime
must be consistent withgmtime
. Such thatgmtime(mktime(t))
returns the original input values. Therefore I have compared the results for all multiples of 61 between 0 and MAX_INT for time_t and they are indeed equal (at least on my system). Therefore the above routine is correct.This outcome also means that the C library does not take leap seconds into account, which is a bad thing in itself but good for my purpose. The two functions will stay consistent for a long time. To be absolutely sure, my Timestamp class that uses this function always performs a quick check on program start and proves the consistency for a couple of meaningful values.
For completeness, here's a version of mkgmtime() that takes a struct tm* as argument:
Here is a solution I came up with for myself after not finding anything in the standard library to do this for me. This methods only uses basic arithmetic for it calculations making it much faster than looping over every year between 1970 and the date provided. But as with most of the previous answers, this one depends on time_t being implemented using Unix/Epoch time, and doesn't work for timestamps older than 1970, which is't necessary for me.
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 oftime_t
will not work correctly there.After giving it some thought, I came up with the following:
One could probably optimize this further by dropping
plt
(usingpt
in its place and omitting thelocaltime()
andg_free(plt)
calls).This should work across all implementations which expose
mktime()
,gmtime()
andlocaltime()
, 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.