How to neatly initialize struct tm from ctime

Herbert picture Herbert · Jun 12, 2014 · Viewed 16.1k times · Source

Consider these two ways to get an epoch time from a date formatted as a string:

#include <iostream>

int main() {
    struct tm tm_init = {0};
    strptime("2012-10-26 16:00", "%Y-%m-%dT %H:%M", &tm_init);
    long epoch = mktime(&tm_init);

    struct tm tm_rand;
    strptime("2012-10-26 16:00", "%Y-%m-%dT %H:%M", &tm_rand);
    epoch = mktime(&tm_rand);

    return 0;
}

Source: http://ideone.com/3xMUm8 . Essentially the difference is that tm_init is initialized with 0 and tm_rand is not. The first one triggers g++ (assuming -Wall and -W) to say:

test.cpp:4:24: warning: missing initializer for member ‘tm::tm_hour’ [-Wmissing-field-initializers]

and similar messages for other fields in the tm struct. However, if I leave out the initialization, like in the second case, valgrind tells me:

==9892== Conditional jump or move depends on uninitialised value(s)
==9892==    at 0x51E957C: __offtime (offtime.c:40)
==9892==    by 0x51EBBE7: __tz_convert (tzset.c:653)
==9892==    by 0x51E9EDB: __mktime_internal (mktime.c:310)
==9892==    by 0x400786: main (test.cpp:10)

Of course, the latter is way worse than the first, but I don't like warnings either. Of course, I can initialize all the fields manually, by writing some kind of tm factory; but that would require me to write code depending on the logic of mt, and I would need two write a factory (blegh).

Is this a bug in ctime? Unfortunately I could not find (semi-)official documentation on strptime, that is, there is no cplusplus.com page on it. Am I missing something? WHat is the good way to parse a string to an epoch long?

EDIT:

As suggested by some answers, I should set the tm_isdst manually to -1, 0 or 1. However this:

#include <iostream>

int main() {
    struct tm tm;
    strptime("2012-10-26 16:00", "%Y-%m-%dT %H:%M", &tm);
    tm.tm_isdst = 0;
    long epoch = mktime(&tm);
    return 0;
}

Results in valgrind saying this:

==11329== Conditional jump or move depends on uninitialised value(s)
==11329==    at 0x51E9917: __offtime (offtime.c:83)
==11329==    by 0x51EBBE7: __tz_convert (tzset.c:653)
==11329==    by 0x51EA513: __mktime_internal (mktime.c:310)
==11329==    by 0x40078D: main (test.cpp:7)

So, should I also set _offtime?

Answer

ecatmur picture ecatmur · Jun 12, 2014

From the official documentation of strptime:

struct tm tm;
time_t t;
strptime("6 Dec 2001 12:33:45", "%d %b %Y %H:%M:%S", &tm);
tm.tm_isdst = -1;      /* Not set by strptime(); tells mktime()
                          to determine whether daylight saving time
                          is in effect */
t = mktime(&tm);

So you should be setting tm_rand.tm_isdst = -1 to tell mktime to check DST from the locale. Alternatively you can set it to 0 or 1.

It's unfortunate that the official documentation doesn't explicitly say that tm_isdst is left unset, but at least it's mentioned in the example.