Думаю, теперь я понимаю, как это может сбить с толку. Думайте о mktime()
как о подписи
time_t mktime_actual(struct tm *dst, const struct tm *src);
где результат time_t
рассчитывается на основе (нормализованного) *src
, а нормализованные поля и то, применяется ли в это время летнее время, сохраняются в *dst
.
Просто разработчики языка C исторически предпочитали использовать только один указатель, комбинируя как src
, так и dst
. Однако приведенная выше логика остается в силе.
См. справочную страницу `man mktime, особенно эту часть:
Функция mktime() преобразует структуру времени в разбивке, выраженную как местное время, в представление календарного времени. Функция игнорирует значения, предоставленные вызывающей стороной в полях tm_wday и tm_yday. Значение, указанное в поле tm_isdst, сообщает mktime(), действует ли летнее время (DST) для времени, указанного в структуре tm: положительное значение означает, что действует летнее время; ноль означает, что летнее время не действует; а отрицательное значение означает, что mktime() должна (используя информацию о часовом поясе и системных базах данных) попытаться определить, действует ли летнее время в указанное время.
Функция mktime() изменяет поля структуры tm следующим образом: tm_wday и tm_yday устанавливаются на значения, определяемые из содержимого других полей; если элементы структуры находятся за пределами допустимого интервала, они будут нормализованы (так, например, 40 октября заменяется на 9 ноября); tm_isdst устанавливается (независимо от его начального значения) в положительное значение или в 0, соответственно, чтобы указать, действует ли летнее время или нет в указанное время. Вызов mktime() также устанавливает внешнюю переменную tzname с информацией о текущем часовом поясе.
Если указанное время в разбивке не может быть представлено как календарное время (секунды с начала Эпохи), mktime() возвращает (time_t) -1 и не изменяет элементы структуры времени в разбивке.
Другими словами, если вы немного измените свою тестовую программу, скажем, на
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
static const char *dst(const int flag)
{
if (flag > 0)
return "(>0: is DST)";
else
if (flag < 0)
return "(<0: Unknown if DST)";
else
return "(=0: not DST)";
}
static struct tm newtm(const int year, const int month, const int day,
const int hour, const int min, const int sec,
const int isdst)
{
struct tm t = { .tm_year = year - 1900,
.tm_mon = month - 1,
.tm_mday = day,
.tm_hour = hour,
.tm_min = min,
.tm_sec = sec,
.tm_isdst = isdst };
return t;
}
int main(void)
{
struct tm tm = {0};
time_t secs;
tm = newtm(2016,9,30, 12,0,0, -1);
secs = mktime(&tm);
printf("-1: %04d-%02d-%02d %02d:%02d:%02d %s %lld\n",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, dst(tm.tm_isdst), (long long)secs);
tm = newtm(2016,9,30, 12,0,0, 0);
secs = mktime(&tm);
printf(" 0: %04d-%02d-%02d %02d:%02d:%02d %s %lld\n",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, dst(tm.tm_isdst), (long long)secs);
tm = newtm(2016,9,30, 12,0,0, 1);
secs = mktime(&tm);
printf("+1: %04d-%02d-%02d %02d:%02d:%02d %s %lld\n",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, dst(tm.tm_isdst), (long long)secs);
return EXIT_SUCCESS;
}
затем его запуск производит вывод
-1: 2016-09-30 12:00:00 (>0: is DST) 1475226000
0: 2016-09-30 13:00:00 (>0: is DST) 1475229600
+1: 2016-09-30 12:00:00 (>0: is DST) 1475226000
Другими словами, он ведет себя именно так, как описано (в цитате выше). Такое поведение задокументировано в C89, C99 и POSIX.1 (я думаю, что и в C11, но не проверял).
person
Nominal Animal
schedule
22.08.2016
mktime(&tm);
, так какmktime()
может настроитьtm
поля. 2) Используйте соответствующий спецификатор формата, напримерprintf("%i\n", (int) secs);
илиprintf("%lld\n", (long long) secs);
, чтобы избежать неопределенного поведения. - person chux - Reinstate Monica   schedule 22.08.2016mktime()
изменяет свой аргумент, в частности,tm.tm_hour
иtm.tm_isdst
в данном случае. - person Nominal Animal   schedule 22.08.2016odd
, так это то, чтоmktime()
не изменитtm
, посколькуtm
находится в основном диапазоне в первых двух случаях. ааа может быть, не в первом случае. Хммм - летнее время 0 может измениться на 1. - person chux - Reinstate Monica   schedule 22.08.2016mktime()
всегда меняетtm_isdst
на0
или1
. Поведение описано (как я объяснил в своем ответе) в man 3 mktime, а также в гораздо более сокращенной форме в POSIX .1а>. - person Nominal Animal   schedule 22.08.2016