5

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?

Herbert
  • 5,279
  • 5
  • 44
  • 69
  • 1
    Official documentation: http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html . cplusplus.com is most certainly *not* official, and is considered a poor reference by many on this site. – ecatmur Jun 12 '14 at 13:26
  • Related: [Why do I get “warning: missing initializer for member”? -Wmissing-field-initializers](http://stackoverflow.com/q/21658485/341970) – Ali Jun 12 '14 at 20:32
  • It looks like a bug to me: Neither clang, nor gcc warns about the uninitialized `tm tm_rand` although I think they should. I will check this with the latest gcc and clang and if the problem still exists, submit a bug report. – Ali Jun 12 '14 at 20:35
  • @Ali, sorry for not posting this as a bug report then :) I'm always very reluctant in filing a bug report because I'm never really sure I'm right. Please keep me updated about what seems to be the problem, also if it is pebkac. – Herbert Jun 14 '14 at 11:23

2 Answers2

12

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • As you suggested, I've set tm_isdst to 0, which also leads to unset members of tm: in particular _offtime. Moreover, in my real-life program I see epochs in 2038, so probably the _offtime is a time offset for time zones, which is not initialized and then translated into the epoch as a random addition or subtraction from the zoneless epoch. – Herbert Jun 12 '14 at 13:51
  • 1
    @Herbert two issues: you're not reading `%s` so you need to set `tm_sec`; also there's a spurious `T` after `%d` which may be interfering with parsing. – ecatmur Jun 12 '14 at 14:05
  • just found that if only date is supplied (i.e. format is `"%Y%m%d"`) then the code run in valgrind spit errors on `mktime`. `memset(&tm, 0, sizeof(tm))` helped to fix it - looks like parts of `tm` are not initialized by `strptime` – lowtech Feb 01 '21 at 06:19
1

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...

mark
  • 5,269
  • 2
  • 21
  • 34