Как добавить минуты в структуру tm, которая содержит время UTC? - PullRequest
2 голосов
/ 28 января 2012

Функция set_time() должна занимать определенное время, добавлять к этому времени указанное количество минут и сохранять его в *t.

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

enum {JAN, FEB, MAR, APR, MAY, JUNE, JULY, AUG, SEP, OCT, NOV, DEC};

void set_time(struct tm *t,
              int year, int mon, int day,
              int hour, int min, int sec,
              int mins_to_add)
{
    /* Copy all the values. */
    t->tm_year = (year - 1900);
    t->tm_mon = mon;
    t->tm_mday = day;
    t->tm_hour = hour;
    t->tm_min = min;
    t->tm_sec = sec;

    // No DST since we are storing UTC time.
    t->tm_isdst = 0;

    /* Add minutes to time. */
    t->tm_min += mins_to_add;
    mktime(t);

    /* Debug print */
    printf("%s\n", asctime(t));
}

int main(int argc, char **argv)
{
    struct tm t;
    set_time(&t, 2011, AUG, 1, 10, 00, 00, 0);
    return 0;
}

Итак, я прошу set_time() ничего не добавлять к 01 августа 10:00:00 2011 г. Однако, если в моей системе Windows установлен часовой пояс EST, я получаю вывод: Mon Aug 01 11:00:00 2011

Ошибка возникает из-за того, что я выполняю задачу добавления указанного количества минут (0 в приведенном выше примере) к указанному времени (01 августа, 10:00:00 2011 в приведенном выше примере) с помощью этой части кода:

    /* Add minutes to time. */
    t->tm_min += mins_to_add;
    mktime(t);

mktime() вызывается для настройки всех других переменных-членов структуры в случае, если t->tm_min превышает 59 из-за сложения. Но mktime() обрабатывает время в struct tm объекте как местное время. Таким образом, когда он видит, что дата 01 августа 2011 года, а местный часовой пояс - как EST, он предполагает, что DST необходимо применить, но обнаруживает, что t->tm_isdst установлен на 0 (то есть DST не применяется ко времени в *t). Таким образом, он применяет летнее время, устанавливает дату с 01 августа 10:00:00 2011 года по 01 августа 11:00:00 2011 года и устанавливает t->tm_isdst в 1.

Если бы я инициализировал t->tm_isdst в 1, эта проблема не возникла бы, потому что mktime() обнаружил бы, что время в *t уже применено к DST. Тем не менее, это могло бы испортить результат, если бы я попросил set_time() ничего не добавлять к дате, к которой DST не должен применяться, скажем 01 января 10:00:00 2011 года. Теперь он попытается отключить DST в *t, тем самым устанавливая t->tm_isdst в 0 и перенастраивая дату на 01 января 09:00:00 2011.

В результате это приводит к нежелательному добавлению дополнительного часа или вычитанию дополнительного часа из указанного времени, когда я не хотел ничего добавлять. Это происходит потому, что когда mktime() обнаруживает, что значение DST в t->tm_isdst не соответствует требуемому местному часовому поясу системы, оно пытается установить его правильно, перенастраивая время в *t.

Как правильно решить эту проблему?

Ответы [ 2 ]

2 голосов
/ 28 января 2012

В документации по struct tm указано

Флаг перехода на летнее время (tm_isdst) ... меньше нуля, если информация недоступна.

Я предлагаю вам установить его на -1 и посмотреть, исправит ли это все.

1 голос
/ 28 января 2012

Функция Windows _mkgmtime может использоваться вместо mktime для безопасного выполнения этого преобразования (независимо от настроек местного часового пояса).

...