получение метки времени utc с помощью strftime ()

Я пытаюсь кодировать текущее время utc в строку с помощью функции strftime:

time_t now;
struct tm nowLocal;
struct tm nowUtc;

now = time(NULL);
localtime_r(&now, &nowLocal);
gmtime_r(&now, &nowUtc);

Пока все хорошо: nowLocal содержит текущее время в моем часовом поясе (CET), nowUtc содержит время utc, разница точно соответствует значению tm_gmtoff:

nowLocal: {tm_sec = 28, tm_min = 27, tm_hour = 13, tm_mday = 23, tm_mon = 4, tm_year = 112, tm_wday = 3, tm_yday = 143, tm_isdst = 1, tm_gmtoff = 7200, tm_zone = 0x8127a38 "CEST"}

nowUtc: {tm_sec = 28, tm_min = 27, tm_hour = 11, tm_mday = 23, tm_mon = 4, tm_year = 112, tm_wday = 3, tm_yday = 143, tm_isdst = 0, tm_gmtoff = 0, tm_zone = 0x3e9907 "GMT"}

Затем я вызываю strftime() в формате "%s", чтобы получить секунды с эпохи:

char tsFromLocal[32];
char tsFromUtc[32];

strftime(tsFromLocal, sizeof(tsFromLocal), "%s", &nowLocal);
strftime(tsFromUtc, sizeof(tsFromUtc), "%s", &nowUtc);

Результат мне кажется странным. Я ожидал получить точно такую ​​же строку из обоих вызовов strftime(), поскольку формат %s описывается как:

Количество секунд с начала эпохи, т. Е. С 1970-01-01 00:00:00 UTC. Високосные секунды не учитываются, если не доступна поддержка дополнительных секунд.

Но у меня два разных значения:

tsFromLocal:"1337772448"

tsFromUtc: "1337768848"

и, кроме того, разница не 7200 (tm_gmtoff), а 3600. Кто-нибудь может объяснить такое поведение? Или это ошибка?

Причина, по которой я это делаю, заключается в том, что мне нужно передать значение времени по сети и сравнить его с текущим временем на целевой машине, которая может находиться в другом часовом поясе. На целевой машине я хотел:

struct tm restoredUtc;
time_t restored;

strptime(tsFromUtc, "%s", &restoredUtc);
restored = timegm(&restoredUtc);

Но я получил:

restoredUtc:{tm_sec = 28, tm_min = 27, tm_hour = 12, tm_mday = 23, tm_mon = 4, tm_year = 112, tm_wday = 3, tm_yday = 143, tm_isdst = 1, tm_gmtoff = 7200, tm_zone = 0x8127a38 "CEST"}

Так что strptime() в любом случае устанавливает tm_zone в соответствии с текущим часовым поясом. Но даже если бы я использовал timelocal() вместо timegm(), я не получу правильного значения, поскольку оно должно быть 11:27:28 CEST, а не 12: 27:28 CEST. Связана ли эта ошибка с разными результатами strftime()?

Есть какие-нибудь комментарии к этой более поздней части?


person Petr    schedule 23.05.2012    source источник
comment
Насколько мне известно, strftime() интерпретирует прошедшую временную структуру как МЕСТНОЕ ВРЕМЯ. Вы получаете разницу в 1 час, потому что исходная структура nowLocal имеет tm_isdst = 1 (установлено предыдущим вызовом localtime_r ()). Если вы установите для этого поля значение 0 перед вызовом strftime, вы, вероятно, получите ожидаемую разницу в 2 часа.   -  person Claudix    schedule 03.08.2012
comment
Учитывая, что у вас есть время в секундах от эпохи в результате time(), вы можете сравнить его с тем, что возвращает strftime(), чтобы определить, какой из ответов правильный (если любой из них). Я предполагаю, что местное время правильное, а UTC - неправильное. Можно ли затем заставить strftime() работать так, как вы хотите, за исключением установки переменной TZ на UTC0 и последующего вызова tzset(), открыто для обсуждения.   -  person Jonathan Leffler    schedule 17.03.2013


Ответы (3)


Вам, вероятно, лучше всего просто использовать GMT, учитывая, что gmtime_r всегда даст вам один и тот же ответ. Если отдельная машина хочет отображать местное время, это можно сделать позже, но придерживаться одного часового пояса для хранения и передачи данных по сети - хорошая идея, а значения GMT легко получить.

person Tom Parker-Shemilt    schedule 15.10.2012

Я подозреваю, что комментарий правильный: strftime() интерпретирует время как местное. Обратите внимание, что tm_gmtoff не является стандартизированным полем; Интересно, strftime() даже смотрит на это. Но я не могу найти ничего конкретного, чтобы это подтвердить.

Однако, отвечая на вторую часть вашего вопроса, почему бы просто не передать результаты time(NULL) напрямую по сети? Или, если у вас уже есть время в форме struct tm, используйте mktime() для преобразования в time_t, а затем перенесите это? printf("%lu", (unsigned long) time) намного проще, чем пытаться использовать strftime("%s"), который не стандартизирован C99 или POSIX.

person rra    schedule 17.03.2013

Q1: Кто-нибудь может объяснить такое поведение? Или это ошибка?

Да, это ошибка в том tsFromLocal:"1337772448"! = tsFromUtc: "1337768848". Но ошибка в tsFromUtc. Они должны быть одинаковыми, и оба должны быть 1337772448.

1337772448% (24 * 60 * 60) -> 41248 - это секунды дня в формате UTC или 11:27:28, что соответствует вашей структуре nowUtc и является временем, которое вы думаете (в UTC)

Q2: ... Связана ли эта ошибка с разными результатами strftime ()?

Я согласен с общей идеей использования time_t в качестве целого числа для сетевой связи.

Да, похоже.

Если вы хотите продолжить этот пост: подумайте о том, чтобы опубликовать числовое значение now сразу после time(&now) и четко указать, в какое время вы ожидали, что это будет тогда.

person chux - Reinstate Monica    schedule 25.08.2013