Time.h заявляет struct tm, что имеет (среди прочих участников) следующее:
int tm_mon; /* Month [0, 11] (January = 0) */
int tm_mday; /* Day of the month [1, 31] */
int tm_wday; /* Day of the week [0, 6] (Sunday = 0) */
int tm_yday; /* Day of the year [0, 365] (Jan/01 = 0) */
Подобная структура позволяет вам попадать в невозможные ситуации... например:
Tm_mon=0 ; tm_mday=1 ; tm_yday=360
Или
Tm_year=0 ; tm_yday=1 ; tm_wday=5 ; // January 1, 1900 is on a Monday
Мне повезло memset установить структуру на 0, а затем установить только те поля, которые я хочу установить.
Мой вопрос: существует ли детерминированный способ интерпретации struct tm?
Я экспериментировал с этим некоторое время, так что это не вопрос «как заставить мой код работать». В основном я спрашиваю об опыте других опытных программистов с struct tm и выискиваю любые подводные камни.
Согласно справочной странице для mktime (единственное, что читает тм)
Значения элементов tm_wday и tm_yday из timeptr игнорируются,
При чтении тм - из localtime допустим все поля заданы. Вам решать, какие из них интересны
@MarkNelson - если ничего не помогает, прочитайте руководство :-)
mktime()
— не единственная стандартная библиотечная функция, использующая struct tm
объекты. asctime()
тоже, и asctime_r()
если вы хотите включить дополнения POSIX. В них прямо или косвенно не указываются какие-либо подробности того, как они интерпретируют несовместимое struct tm
. Возможно, они сделают то же самое, что и mktime()
, возможно, нет.
Также strftime(), который, учитывая его специфический характер, кажется особенно хорошим кандидатом для проявления другого поведения.
Для этого см. strftime и ненормализованные даты/время.
Есть ли детерминированный способ интерпретации struct tm?
Да, в основном.
Позвоните mktime(), чтобы решить эти безвыходные ситуации в struct tm.
Также отметим @pm100, вызов mktime() будет игнорировать участников .tm_wday и .tm_yday и продолжит разрешать другие комбинации участников, выходящие за пределы нормального диапазона, как если бы время было местным.
Необычные примеры отметок времени «попался» включают:
Один элемент находится за пределами основного диапазона: уменьшите до основного диапазона и добавьте лишнее к следующему наиболее значимому элементу. Повторяйте по мере необходимости. См. позже исключение.
29 февраля (невисокосный год).
.tm_min = 30, .tm_isdst < 0 и .tm_hour находится в недостающем часе 23-часового дня, когда зона переходит на летнее время (DST).
.tm_min = 30, .tm_isdst < 0 и .tm_hour находится в добавленном часе 25-часового дня, когда зона переходит в летнее время.
Я сомневаюсь в надежности mktime() для лечения таких патологических случаев, как .tm_year = INT_MAX/12 + 100, .tm_mon = INT_MIN. Такие крайности могут показывать разные разрешения (или возвращаемое значение ошибки -1) на разных платформах и отражать разницу в качестве реализации.
Mktime() возвращает -1, чтобы указать на ошибку. К сожалению, он также редко может вернуть -1, чтобы указать действительное время. Спецификация C не предлагает четкого способа различения.
Struct tm может иметь других участников, кроме указанных 9. Я видел .tm_nsecs, .tm_usec, .tm_timezone, .tm_tzoffset или эквиваленты. Вот почему лучше всего инициализировать { 0 } или сначала memset(0) весь объект при заполнении struct tm пользовательским кодом.
Самые сложные — когда .tm_year, .tm_mon, .tm_mday все вне досягаемости: что решать в первую очередь? В зависимости от заказа результат отличается. C указывает:
окончательное значение tm_mday не устанавливается до тех пор, пока не будут определены tm_mon и tm_year.
ISO 8601 позволяет 24:00:00 относиться к моменту в конце календарного дня. То же время 0:00:00 следующего дня. Поэтому иногда сокращение с помощью mktime() не всегда желательно.
Для простоты и здравого смысла обсуждение дополнительных секунд игнорируется.
Отличный! Это имеет большой смысл. Спасибо.