местное время, mktime: вторая нормализация и поведение DST - PullRequest
0 голосов
/ 03 сентября 2018

Возьмите следующий кусок кода:

static void printTime(const struct tm* t, const time_t stamp){
    printf("%d-%d-%d, %d:%d:%d (DST %s) (stamp: %zu)\n",
            1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, t->tm_isdst ? "Active" : "Inactive", stamp);
}

int main(){
    time_t t = 1540633936;
    struct tm tStruct;
    localtime_r(&t, &tStruct);
    printTime(&tStruct, t);
    for (unsigned i = 0; i < 14; ++i){
        tStruct.tm_sec += 7200;
        //tStruct.tm_hour += 2;
        tStruct.tm_isdst = -1;
        t = mktime(&tStruct);
        localtime_r(&t, &tStruct);
        printTime(&tStruct, t);
    }
    return 0;
}

Показывает два способа увеличения даты. Документация mktime говорит мне:

Функция mktime () изменяет поля структуры tm как следует: tm_wday и tm_yday устанавливаются в значения, определенные из содержимое другого поля; если члены структуры находятся за пределами допустимого интервала, они будут нормализованы (например, 40 октября изменен на 9 ноября); tm_isdst устанавливается (независимо от его начального значения) в положительное значение или в 0, соответственно, чтобы указать, является ли DST или нет эффект при указанном время.

Исходя из этого, я ожидаю, что нормализация будет работать таким образом, что добавление 7200 секунд эквивалентно добавлению двух часов. Но вывод отличается:

tStruct.tm_sec += 7200;

Дает:

2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936)
2018-10-27, 13:52:16 (DST Active) (stamp: 1540641136)
2018-10-27, 15:52:16 (DST Active) (stamp: 1540648336)
2018-10-27, 17:52:16 (DST Active) (stamp: 1540655536)
2018-10-27, 19:52:16 (DST Active) (stamp: 1540662736)
2018-10-27, 21:52:16 (DST Active) (stamp: 1540669936)
2018-10-27, 23:52:16 (DST Active) (stamp: 1540677136)
2018-10-28, 1:52:16 (DST Active) (stamp: 1540684336)
2018-10-28, 2:52:16 (DST Inactive) (stamp: 1540691536)
2018-10-28, 3:52:16 (DST Inactive) (stamp: 1540695136)
2018-10-28, 5:52:16 (DST Inactive) (stamp: 1540702336)
2018-10-28, 7:52:16 (DST Inactive) (stamp: 1540709536)
2018-10-28, 9:52:16 (DST Inactive) (stamp: 1540716736)
2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936)
2018-10-28, 13:52:16 (DST Inactive) (stamp: 1540731136)

(обратите внимание на неправильные скачки времени сразу после изменения летнего времени)

tStruct.tm_hour += 2;

Дает:

2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936)
2018-10-27, 13:52:16 (DST Active) (stamp: 1540641136)
2018-10-27, 15:52:16 (DST Active) (stamp: 1540648336)
2018-10-27, 17:52:16 (DST Active) (stamp: 1540655536)
2018-10-27, 19:52:16 (DST Active) (stamp: 1540662736)
2018-10-27, 21:52:16 (DST Active) (stamp: 1540669936)
2018-10-27, 23:52:16 (DST Active) (stamp: 1540677136)
2018-10-28, 1:52:16 (DST Active) (stamp: 1540684336)
2018-10-28, 3:52:16 (DST Inactive) (stamp: 1540695136)
2018-10-28, 5:52:16 (DST Inactive) (stamp: 1540702336)
2018-10-28, 7:52:16 (DST Inactive) (stamp: 1540709536)
2018-10-28, 9:52:16 (DST Inactive) (stamp: 1540716736)
2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936)
2018-10-28, 13:52:16 (DST Inactive) (stamp: 1540731136)
2018-10-28, 15:52:16 (DST Inactive) (stamp: 1540738336)

Какое поведение ожидается (по крайней мере для меня).

Итак, мой вопрос: есть ли на самом деле ошибка? Или это документированное поведение где-то?

Такое поведение также происходит, когда mmtime необходимо изменить tm_hour. Возьмите следующий пример:

tStruct.tm_hour += 25;

Дает:

2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936)
2018-10-28, 12:52:16 (DST Inactive) (stamp: 1540727536)
2018-10-29, 13:52:16 (DST Inactive) (stamp: 1540817536)
2018-10-30, 14:52:16 (DST Inactive) (stamp: 1540907536)
2018-10-31, 15:52:16 (DST Inactive) (stamp: 1540997536)
2018-11-1, 16:52:16 (DST Inactive) (stamp: 1541087536)
2018-11-2, 17:52:16 (DST Inactive) (stamp: 1541177536)
2018-11-3, 18:52:16 (DST Inactive) (stamp: 1541267536)
2018-11-4, 19:52:16 (DST Inactive) (stamp: 1541357536)
2018-11-5, 20:52:16 (DST Inactive) (stamp: 1541447536)
2018-11-6, 21:52:16 (DST Inactive) (stamp: 1541537536)
2018-11-7, 22:52:16 (DST Inactive) (stamp: 1541627536)
2018-11-8, 23:52:16 (DST Inactive) (stamp: 1541717536)
2018-11-10, 0:52:16 (DST Inactive) (stamp: 1541807536)
2018-11-11, 1:52:16 (DST Inactive) (stamp: 1541897536)
tStruct.tm_sec += 90000

Дает:

2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936)
2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936)
2018-10-29, 12:52:16 (DST Inactive) (stamp: 1540813936)
2018-10-30, 13:52:16 (DST Inactive) (stamp: 1540903936)
2018-10-31, 14:52:16 (DST Inactive) (stamp: 1540993936)
2018-11-1, 15:52:16 (DST Inactive) (stamp: 1541083936)
2018-11-2, 16:52:16 (DST Inactive) (stamp: 1541173936)
2018-11-3, 17:52:16 (DST Inactive) (stamp: 1541263936)
2018-11-4, 18:52:16 (DST Inactive) (stamp: 1541353936)
2018-11-5, 19:52:16 (DST Inactive) (stamp: 1541443936)
2018-11-6, 20:52:16 (DST Inactive) (stamp: 1541533936)
2018-11-7, 21:52:16 (DST Inactive) (stamp: 1541623936)
2018-11-8, 22:52:16 (DST Inactive) (stamp: 1541713936)
2018-11-9, 23:52:16 (DST Inactive) (stamp: 1541803936)
2018-11-11, 0:52:16 (DST Inactive) (stamp: 1541893936)

1 Ответ

0 голосов
/ 03 сентября 2018

В зависимости от вашего точного часового пояса (и юрисдикции), в какое-то время рано утром 2018-10-28 часы переводятся на 1 час назад, потому что DST заканчивается. Из ваших примеров видно, что это происходит в 3:00 в вашем часовом поясе / юрисдикции.

В первом случае (добавление 7200 секунд к 2018-10-28, 1:52:16) значение tm_sec выходит за пределы нормального диапазона (0 - 59), поэтому mktime может определить, что вы добавили 2 часа, и, поскольку он знает, что он пересекает границу летнего времени, он соответствующим образом корректирует время. Это приводит к 2018-10-28, 2:52:16, то есть через 2 часа после 2018-10-28, 1: 52: 16.

Для следующего приращения в первом случае (добавление 7200 секунд к 2018-10-28, 2:52:16), то же самое происходит снова (так как вы снова пересекаете границу DST - вы сбросили tm_isdst до -1 в конце концов). Это приводит к 2018-10-28, 3:52:16, то есть через 2 часа после 2018-10-28, 2: 52: 16.

Во втором случае (добавление 2 часов к 2018-10-28, 1:52:16) значение tm_hour все еще находится в пределах нормального диапазона (0 - 23), поэтому mktime не может определить, что Вы добавили 2 часа, и это просто относится к местному времени. Это приводит к 2018-10-28, 3:52:16, то есть через 3 часа после 2018-10-28, 1: 52: 16.

Чтобы избежать подобных проблем:

  • не переустанавливать tm_isdst на -1 без необходимости (и вы понимаете, что произойдет)
  • максимально работает с временными метками UTC и при отображении конвертируется только в местное время.
...