_mktime64() и отмена зимнего времени
От: igyrus  
Дата: 03.12.11 20:59
Оценка: 31 (1) +1
Столкнулся с такой проблемой. После отмены перехода на зимнее время функция _mktime64() стала не правильно расчитывать __time64_t для дат зимнего времени прошлых лет. Она считает так, как будто бы раньше тоже не было перехода зимнее/летнее. Для примера возьмём дату "26 Jan 2011 03:00:00" (строка "Зима начала 2011 года" в таблице на странице http://habrahabr.ru/blogs/sysadm/130363/). Результат должен быть 1296000000. Такой результат даёт функция MySQL UNIX_TIMESTAMP('2011-01-26 03:00:00'). А _mktime64() возвращает 1295996400 (на 1 час меньше).
Если заглянуть внутрь _mktime64()
/*
 * Adjust for timezone. No need to check for overflow since
 * localtime() will check its arg value
 */

 __tzset();

 _ERRCHECK(_get_dstbias(&dstbias));
 _ERRCHECK(_get_timezone(&timezone));

 tmptm1 += timezone;

 /*
  * Convert this second count back into a time block structure.
  * If localtime returns NULL, return an error.
  */
 if ( _localtime64_s(&tbtemp, &tmptm1) != 0 )
    goto err_mktime;

 /*
  * Now must compensate for DST. The ANSI rules are to use the
  * passed-in tm_isdst flag if it is non-negative. Otherwise,
  * compute if DST applies. Recall that tbtemp has the time without
  * DST compensation, but has set tm_isdst correctly.
  */
 if ( (tb->tm_isdst > 0) || ((tb->tm_isdst < 0) &&
    (tbtemp.tm_isdst > 0)) ) {
      tmptm1 += dstbias;
      if ( _localtime64_s(&tbtemp, &tmptm1) != 0 )
         goto err_mktime;
 }

то видим, что функциями _get_dstbias(&dstbias) и _get_timezone(&timezone) запрашиваются сдвижки DST и часового пояса действующие на сегодняшний момент. Т.е. dstbias=0 т.к. нет переходов зимнего/летнего времени и timezone=14400 (UTC+4). А ведь на момент "26 Jan 2011 03:00:00" часовой пояс был UTC+3.
Мне просто необходимо что бы _mktime64() в клиентской программе и UNIX_TIMESTAMP(), FROM_UNIXTIME() на MySQL сервере давали одинаковый результат.
Похоже придётся хранить все даты перевода часов с 1970 по 2011 и самому корректировать результат. Хотя оставляю надежду на какой-нибудь дельный совет.
Re: _mktime64() и отмена зимнего времени
От: MasterZiv СССР  
Дата: 04.12.11 08:04
Оценка:
> Столкнулся с такой проблемой. После отмены перехода на зимнее время функция
> _mktime64() стала не правильно расчитывать __time64_t для дат зимнего времени
> прошлых лет. Она считает так, как будто бы раньше тоже не было перехода
> зимнее/летнее. Для примера возьмём дату "26 Jan 2011 03:00:00" (строка "Зима
> начала 2011 года" в таблице на странице

Чё сюда-то пишешь? Ты это в Госдуму пиши, тем идиотам которые это придумали.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: _mktime64() и отмена зимнего времени
От: Denis Mingulov Финляндия http://denis.mingulov.com
Дата: 04.12.11 09:02
Оценка:
Здравствуйте, MasterZiv, Вы писали:

>> начала 2011 года" в таблице на странице

MZ>Чё сюда-то пишешь? Ты это в Госдуму пиши, тем идиотам которые это придумали.
А какая разница, раз речь про 1970 год и далее — значит эта функция всегда неправильно работала.
в СССР в 1981 году менялось ("декретное время", в 1991, Калиниград поменял часовой пояс, регионы разные в любое другое время).
Будет интересно посмотреть на правильную функцию/утилиту, учитывающую всё.
Re: _mktime64() и отмена зимнего времени
От: Roman Odaisky Украина  
Дата: 04.12.11 10:55
Оценка:
Здравствуйте, igyrus, Вы писали:

I>После отмены перехода на зимнее время функция _mktime64() стала неправильно расчитывать __time64_t для дат зимнего времени прошлых лет. Она считает так, как будто бы раньше тоже не было перехода зимнее/летнее.


Windows никогда не славилась корректной работой с часовыми поясами.

I>Похоже придётся хранить все даты перевода часов с 1970 по 2011 и самому корректировать результат. Хотя оставляю надежду на какой-нибудь дельный совет.


Все даты перевода хранятся в tz database и поддерживаются в актуальном состоянии, с недавнего времени под эгидой IANA: http://www.iana.org/time-zones. Нормальные libc задействуют эту БД, а в Windows, наверное, можно таскать ее с собой и брать данные из нее, минуя системные вызовы.

~/src :) cat mktime.c
#include <time.h>
#include <stdio.h>

int main()
{
    // "26 Jan 2011 03:00:00"
    struct tm t2011 = {
        .tm_sec   = 0,
        .tm_min   = 0,
        .tm_hour  = 3,
        .tm_mday  = 26,
        .tm_mon   = 0,
        .tm_year  = 111,
        .tm_wday  = 0,
        .tm_yday  = 0,
        .tm_isdst = -1
    };
    printf("%lu\n", (unsigned long)mktime(&t2011));

    struct tm t2012 = t2011;
    t2012.tm_year += 1;
    printf("%lu\n", (unsigned long)mktime(&t2012));

    return 0;
}
~/src :) gcc -std=c99 -o mktime mktime.c
~/src :) TZ=Europe/Moscow ./mktime
1296000000
1327532400
~/src :) TZ=Europe/UTC ./mktime
1296010800
1327546800
До последнего не верил в пирамиду Лебедева.
Re[3]: _mktime64() и отмена зимнего времени
От: igyrus  
Дата: 04.12.11 15:49
Оценка:
Здравствуйте, Denis Mingulov, Вы писали:
DM>А какая разница, раз речь про 1970 год и далее — значит эта функция всегда неправильно работала.
DM>в СССР в 1981 году менялось ("декретное время", в 1991, Калиниград поменял часовой пояс, регионы разные в любое другое время).
DM>Будет интересно посмотреть на правильную функцию/утилиту, учитывающую всё.

Да нет. Раньше она правильно работала. Для московского часового пояса что с 1970 по 1981, что с 1981 по осень 2011 был часовой пояс UTC+3.
Т.е. tmptm1 += timezone; , где timezone=10800 и летом начиная с 1981г стало прибавляться dstbias=3600. А с осени 2011 пояс стал UTC+4, т.е timezone=14400. А функции _mktime64() пофиг какую дату её просят посчитать. Она теперь всегда 14400 прибавляет.
Re[2]: _mktime64() и отмена зимнего времени
От: igyrus  
Дата: 04.12.11 16:31
Оценка:
Да, спасибо. Подтвердили моё мнение, что для Windows делать надо самому. А даты перехода я, пожалуй, возьму из таблицы `mysql`.`time_zone_transition`.
Re[4]: _mktime64() и отмена зимнего времени
От: Denis Mingulov Финляндия http://denis.mingulov.com
Дата: 05.12.11 06:11
Оценка:
Здравствуйте, igyrus, Вы писали:

I>Да нет. Раньше она правильно работала.

Ничего не понял.
Раз у вас эта функция могла работать правильно с учетом того, что летнее время то вводили, то отменяли, то вообще меняли часовые пояса — вам лучше разобраться с тем, что именно и почему у вас сейчас не работает.

Либо всё-таки вариант, что раньше она тоже неправильно работала.
Re[3]: _mktime64() и отмена зимнего времени
От: sts  
Дата: 05.12.11 07:43
Оценка:
Здравствуйте, igyrus, Вы писали:

I>Да, спасибо. Подтвердили моё мнение, что для Windows делать надо самому. А даты перехода я, пожалуй, возьму из таблицы `mysql`.`time_zone_transition`.

еще libical умеет это делать
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.