mktime и tm_isdst

Я видел много разных мнений, поэтому решил спросить здесь.

Я прочитал man mktime:

 (A positive or zero value for tm_isdst causes mktime() to presume initially
 that summer time (for example, Daylight Saving Time) is or is not in
 effect for the specified time, respectively.  A negative value for
 tm_isdst causes the mktime() function to attempt to divine whether summer
 time is in effect for the specified time. 

Мой вопрос: не следует ли tm_isdst сохранить как -1, чтобы система могла решить, является ли это dst или нет, и таким образом код становится независимым от dst?

Я что-то упускаю?


person hari    schedule 19.12.2011    source источник


Ответы (3)


Я считаю, что первоначальная причина этого в том, что в некоторых часовых поясах нет летнего времени. Поскольку mktime не является асинхронно-безопасным и не допускает повторного входа, реализация позволяет сохранить текущее значение летнего времени в POSIX extern char tzname[2], проиндексированном дневным светом [0 или 1]. Это означает tzname[0]="[std TZ name]" и tzname="[дневное TZ name, например, EDT]"

См. справочную страницу tzset() для получения дополнительной информации об этом. Стандарты, соответствующие mktime(), должны вести себя так, как если бы они все равно вызывали tzset(). Этот вид устраняет использование tm_isdst, IMO.

Итог: ваша конкретная реализация и часовой пояс (ы) будут определять, будете ли вы использовать -1, 0 или 1 для tm_isdst. Не существует одного правильного способа по умолчанию для всех реализаций.

person jim mcnamara    schedule 19.12.2011

По возможности следует избегать установки tm_isdst в -1. Система не всегда может определить статус перехода на летнее время только по дате и времени. Это неоднозначно за час до и после окончания летнего времени. Например, если вы передаете mktime() 1:30 4 ноября 2012 г., этой информации недостаточно для получения правильного значения time_t из mktime(). Обычно я видел, что mktime() принимает стандартное время в случае его неоднозначности, но я не видел никакой документации, гарантирующей такое поведение на всех платформах. 1:30 4 ноября 2012 г. с tm_isdst == 1 будет на 1 час раньше, потому что время с 1:00:00 до 1:59:59 повторяется.

#include <stdio.h>
#include <time.h>

int main()
{
    time_t daylight, standard;
    struct tm timestr;
    double diff;

    timestr.tm_year = 2012 - 1900;
    timestr.tm_mon = 11 - 1;
    timestr.tm_mday = 4;
    timestr.tm_hour = 1;
    timestr.tm_min = 30;
    timestr.tm_sec = 0;

    /* first with standard time */
    timestr.tm_isdst = 0;
    standard = mktime(&timestr);

    /* now with daylight time */
    timestr.tm_isdst = 1;
    daylight = mktime(&timestr);

    diff = difftime(standard, daylight);

    printf("Difference is %f hour(s)", diff/60.0/60.0);

    return 0;
}

Это производит:

Difference is 1.000000 hour(s)

Оба — 4 ноября 2012 г., 1:30, однако оба являются двумя разными значениями time_t с разницей в 1 час.

mktime() по существу имеет 2 выхода:

  • время_t
  • восстановленная временная структура

Структура времени является одновременно входом и выходом. Он изменен mktime(), чтобы вернуть все элементы структуры в номинальные диапазоны. Например, если вы увеличиваете член tm_hour += 500, это означает увеличение времени на 500 часов. Элемент tm_hour будет изменен на значение от 00 до 23, а tm_day, tm_mday и т. д. будут изменены соответствующим образом. tm_isdst также является и входом, и выходом. Его значения следующие:

  • 1 (действующее летнее время, т. е. дневное время)
  • 0 (летнее время не действует, т.е. стандартное время)
  • -1 (Неизвестный статус перехода на летнее время)

Таким образом, mktime() выводит либо 1, либо 0 для tm_isdst, а не -1.

-1 является возможным вводом, но я бы подумал, что это означает «Неизвестно». Не думайте, что это означает "определить автоматически", потому что в общем случае mktime() не всегда может определить это автоматически.

Явный статус DST (0 или 1) должен исходить от чего-то внешнего по отношению к программному обеспечению, например, хранить его в файле или базе данных или запрашивать у пользователя.

person Rich Jahn    schedule 24.08.2012
comment
Что ж, если вы не знаете летнее время, вам нужно определить его самостоятельно (удачи в этом) или установить его на -1. Другого варианта нет. Если вы установите его на 0, вы вообще не получите DST, что гарантированно неверно, если вы установите его на 1, вы принудительно установите DST, на котором также гарантировано неправильно. - person rustyx; 05.08.2016
comment
@RustyX Жесткое кодирование до -1, 0 или 1 всегда будет неправильным. -1 наименее плохой, но все же плохой. Явный статус (0 или 1) должен быть введен в программное обеспечение от пользователя или сохранен в данных (файле или базе данных). В основном это должно исходить от чего-то внешнего по отношению к приложению. - person Rich Jahn; 20.12.2016
comment
@RichJahn: он хранится в базе данных: базе данных TZ почти каждой системы UNIX/Linux. Самый простой способ получить его оттуда для любого заданного времени - это выполнить mktime с tm_isdst, установленным в -1 ... А вы помните, когда происходят изменения DST? Требование этой информации от пользователя не является чем-то разумным. Вы должны избегать установки tm_isdst в -1 если это возможно. - В каких разумных сценариях общего использования это возможно? - person SF.; 31.03.2017
comment
@СФ. Суть в том, что он нигде не хранится. Компьютер не знает. По аналогии, если бы я сказал: абсолютное значение равно 5, было ли исходное число положительным или отрицательным? Вы не можете написать программу, чтобы понять это, если только это не программа для чтения мыслей. Ответ: я не знаю, вы мне скажите. Я знаю, что 5 ноября 2017 года полночь — это центральное поясное время США, а 5:00 — это среднеевропейское стандартное время США. Но 1:30 ночи? Я не знаю, ты мне скажи. Приложения, которые я написал, делают это фатальной ошибкой или запрашивают у пользователя, если время неоднозначно. Я думаю, что большинство пользовательских интерфейсов для выбора даты, к сожалению, просто полностью игнорируют эту проблему. - person Rich Jahn; 05.04.2017
comment
Таким образом, mktime() выведет либо 1, либо 0 --> я ожидаю, что будет так. Таким образом, mktime() выведет либо положительное значение, либо 0. Тем не менее, возможно, различные разновидности Unix более специфичны. - person chux - Reinstate Monica; 13.08.2018
comment
«Член tm_hour будет изменен на значение от 00 до 59 [...]» — Ну... надеюсь, он установит час от 0 до 23. :-) - person Alexis Wilke; 08.04.2019
comment
@RichJahn Этот код дает мне Difference is 0.000000 hour(s) на gcc-8.1 - person Kostas; 17.05.2019
comment
@Kosta Разница зависит от настроек часового пояса вашей системы, а не только от кода и платформы. Этот пример относится к восточной, центральной, горной или тихоокеанской части США, за исключением Аризоны, после 2007 г., и если ваша система настроена на летнее время. - person Rich Jahn; 23.08.2019

Я думаю, что вам действительно следует использовать -1 для поля tm_isdst, если у вас нет информации о типе времени, с которым вы имеете дело.

Например, в Калифорнии у нас все еще есть PST и PDT. Если вы анализируете дату и информация о часовом поясе присутствует, вы должны установить tm_isdst соответственно. Как упоминал Джим Макнамара, эти имена доступны в массиве tzname[] после вызова tzset().

Например, в следующем коде C++ записывается PST/PDT:

int main(int argc, char * argv [])
{
    tzset();
    std::cerr << tzname[0] << "/" << tzname[1] << "\n";

    return 0;
}

Смещение в массиве tzname[] соответствует значению tm_isdst. (PST – стандартное тихоокеанское время, tm_isdst = 0, и PDT, тихоокеанское летнее время, tm_isdst = 1.)

Если у вас нет информации о часовом поясе во входных данных, то лучше всего использовать -1. Вы сталкиваетесь с проблемой только, когда дата соответствует дню и времени, когда произошло изменение. Как объясняет Рич Ян, 4 ноября 2012 года у него было изменение времени между стандартным и дневным временем, и примерно в это же время gmtime() должен был сделать выбор, и это мало чем отличается от того, что вы ожидаете. При этом, это происходит всего 2 часа в году и посреди ночи. Поэтому, если вы не работаете над критическим типом программного обеспечения, где дата очень важна, это, вероятно, не будет иметь большого значения.

Итак, в качестве резюме:

  • если у вас есть часовой пояс, привязанный к дате, которую вы хотите преобразовать, используйте эту информацию для определения значения tm_isdst (при этом я не слишком уверен, как вы справитесь с этим, если вам нужно поддерживать все часовые пояса... tzname[] дает только текущий часовой пояс пользователя.)
  • во всех остальных случаях используйте -1
person Alexis Wilke    schedule 08.04.2019
comment
То, что вы говорите, верно, я думаю, но звучит так пассивно, и такая низкая планка, как будто у меня нет информации, думаю, я просто буду жить этим. Если вы введете только местное время, то да, ваше время будет правильным в 99,98% случаев, но с немного большим количеством кода оно может быть правильным в 100% случаев. Очевидно, что люди не приняли бы машины для голосования, которые в 0,02% случаев регистрировали 2 голоса вместо 1. Мой совет — используйте однозначный формат, такой как ISO 8601. - person Rich Jahn; 23.08.2019
comment
@RichJahn, вы пытались ходить, скажем, в Walmart, и спрашивать людей, какой часовой пояс в этом месте? Сколько из них дали вам правильный ответ? Научить более 7 миллиардов человек тому, что такое часовой пояс, — огромное бремя. Тем не менее, большинство будет знать, что такое дата и время. Я бы посоветовал, чтобы все правительства в мире отказались от зимнего и летнего времени, чтобы начать. - person Alexis Wilke; 23.08.2019