Как аккуратно инициализировать struct tm из ctime

Рассмотрим эти два способа получить время эпохи из даты, отформатированной в виде строки:

#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;
}

Источник: http://ideone.com/3xMUm8. По сути, разница в том, что tm_init инициализируется 0, а tm_rand — нет. Первый запускает g++ (предполагая -Wall и -W), чтобы сказать:

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

и аналогичные сообщения для других полей в структуре tm. Однако, если я пропущу инициализацию, как во втором случае, valgrind скажет мне:

==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)

Конечно, последнее намного хуже первого, но я тоже не люблю предупреждения. Конечно, я могу инициализировать все поля вручную, написав какую-нибудь tm factory; но это потребовало бы от меня написания кода в зависимости от логики mt, и мне понадобилось бы два написания фабрики (блеф).

Это ошибка в ctime? К сожалению, мне не удалось найти (полу)официальную документацию по strptime, то есть страницы cplusplus.com по нему нет. Я что-то пропустил? Каков хороший способ проанализировать строку до эпохи?

ИЗМЕНИТЬ:

Как было предложено в некоторых ответах, я должен вручную установить для tm_isdst значение -1, 0 или 1. Однако это:

#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;
}

Результаты в valgrind говорят следующее:

==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)

Итак, должен ли я также установить _offtime?


person Herbert    schedule 12.06.2014    source источник
comment
Мне это кажется ошибкой: ни clang, ни gcc не предупреждают о неинициализированных tm tm_rand, хотя я думаю, что должны. Я проверю это с последними версиями gcc и clang и, если проблема все еще существует, отправлю отчет об ошибке.   -  person Ali    schedule 13.06.2014
comment
@Ali, извини, что не опубликовал это как отчет об ошибке :) Я всегда очень неохотно подаю отчет об ошибке, потому что я никогда не уверен, что прав. Пожалуйста, держите меня в курсе того, в чем проблема, а также если это pebkac.   -  person Herbert    schedule 14.06.2014


Ответы (2)


Из официальной документации 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);

Таким образом, вы должны установить tm_rand.tm_isdst = -1, чтобы указать mktime проверять летнее время из локали. В качестве альтернативы вы можете установить его на 0 или 1.

К сожалению, в официальной документации прямо не говорится, что tm_isdst не задано, но, по крайней мере, это упоминается в примере.

person ecatmur    schedule 12.06.2014
comment
Как вы предложили, я установил для tm_isdst значение 0, что также приводит к неустановленным членам tm: в частности, _offtime. Более того, в моей реальной программе я вижу эпохи в 2038 году, поэтому, вероятно, _offtime — это смещение времени для часовых поясов, которое не инициализируется, а затем переводится в эпоху как случайное добавление или вычитание из эпохи без зоны. - person Herbert; 12.06.2014
comment
@ Герберт, две проблемы: вы не читаете %s, поэтому вам нужно установить tm_sec; также есть ложный T после %d, который может мешать синтаксическому анализу. - person ecatmur; 12.06.2014
comment
только что обнаружил, что если указана только дата (т.е. формат "%Y%m%d"), то код запускается в ошибках valgrind spit на mktime. memset(&tm, 0, sizeof(tm)) помог исправить - похоже части tm не инициализированы strptime - person lowtech; 01.02.2021

Я не думаю, что strptime что-то делает с tm_isdst и не обрабатывает часовые пояса (ну или вообще, в зависимости от варианта). mktime, с другой стороны, требует установки tm_isdst для своих вычислений, поэтому, по-видимому, неинициализированная зависимость распространяется на другой код. Короче говоря, вам нужно решить, что делать с обработкой часового пояса...

person mark    schedule 12.06.2014