Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to neatly initialize struct tm from ctime

Tags:

c++

c++11

ctime

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?

like image 860
Herbert Avatar asked Jun 12 '14 13:06

Herbert


2 Answers

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.

like image 84
ecatmur Avatar answered Nov 13 '22 05:11

ecatmur


I don't think strptime does anything with tm_isdst, and doesn't handle timezones (well or at all depending on the variation). mktime on the other hand requires tm_isdst be set for its calculations so presumably the uninitialized dependency propagates into other code. In short, you need to decide what to do with timezone handling...

like image 28
mark Avatar answered Nov 13 '22 07:11

mark