2016-08-22 6 views
1

еще один вопрос о указываете ей и DSTСистемный вызов указываете ей игнорирует tm_isdst флаг

Linux, Ubuntu, часовой пояс установлен в Европе/Берлин т.е. текущее время является CEST:

>date 
Mon Aug 22 16:08:10 CEST 2016 
>date --utc 
Mon Aug 22 14:08:14 UTC 2016 

все хорошо до сих пор.

Теперь я пытаюсь запустить следующий код:

#include <stdio.h> 
#include <time.h> 
int main() 
{ 

    struct tm tm = {0}; 
    int secs; 

    tm.tm_sec = 0; 
    tm.tm_min = 0; 
    tm.tm_hour = 12; 
    tm.tm_mon = 9 - 1; 
    tm.tm_mday = 30; 
    tm.tm_year = 2016 - 1900; 

    tm.tm_isdst = 0; 
    secs = mktime(&tm); 
    printf("%i\n", secs); 

    tm.tm_isdst = 1; 
    secs = mktime(&tm); 
    printf("%i\n", secs); 

    tm.tm_isdst = -1; 
    secs = mktime(&tm); 
    printf("%i\n", secs); 

    return 0; 
} 

и получить

1475233200 
1475233200 
1475233200 

, который во всех трех случаях неправильно (1 час смещение):

>date -d @1475233200 
Fri Sep 30 13:00:00 CEST 2016 

Так Я немного озадачен, мой временной интервал как-то сломан? Почему флаг tm_isdst полностью игнорируется?

Редактировать: @Nominal Animal получил ответ: mktime изменяет tm_hour! Интересно, где это задокументировано ?!

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

void reset(struct tm* tm){ 
    (*tm) = (const struct tm){0}; 

    tm->tm_sec = 0; 
    tm->tm_min = 0; 
    tm->tm_hour = 12; 
    tm->tm_mon = 9 - 1; 
    tm->tm_mday = 30; 
    tm->tm_year = 2016 - 1900; 
} 

int main() 
{ 

    struct tm tm; 
    int secs; 

    reset(&tm); 
    tm.tm_isdst = 0; 
    secs = mktime(&tm); 
    printf("%i\n", secs); 

    reset(&tm); 
    tm.tm_isdst = 1; 
    secs = mktime(&tm); 
    printf("%i\n", secs); 

    reset(&tm);  
    tm.tm_isdst = -1; 
    secs = mktime(&tm); 
    printf("%i\n", secs); 

    return 0; 
} 

дает

1475233200 
1475229600 
1475229600 
+1

1) Поведение кажется странным. Чтобы сбросить ошибку _better_, сбросьте все ** поля ** (или, по крайней мере, распечатайте их) после вызова 'mktime (&tm);' as 'mktime()' может корректировать поля 'tm'. 2) Использовать соответствующий спецификатор формата, например' printf ("% i \ n", (int) secs); 'или' printf ("% lld \ n", (long long) secs); ', чтобы избежать неопределенного поведения. – chux

+0

Это может показаться странным, но это действительно не странно. Действительно, ['mktime()'] (http://man7.org/linux/man-pages/man3/mktime.3.html) изменяет свой аргумент - в частности, 'tm.tm_hour' и' tm. tm_isdst' в этом случае. –

+0

@Nominal Animal Что означает 'odd', так это то, что' mktime() 'не ожидается, чтобы изменить' tm', поскольку 'tm' находится в основном диапазоне в первых двух случаях. _ahh_, возможно, не в первом случае. Hmmm - a dst of 0 в летнее время может измениться до 1. – chux

ответ

1

Я думаю, теперь я могу видеть, как это может показаться запутанным. Подумайте mktime() как имеющая подпись

time_t mktime_actual(struct tm *dst, const struct tm *src); 

где time_t результат рассчитывается на основе (нормированные) *src и нормированные поля и применяет ли летнее время в то время, сохраняются в *dst.

Это просто, что разработчики языка C исторически предпочли использовать только один указатель, объединив как src, так и dst. Вышеупомянутая логика все еще стоит.

Смотрите страницу `man mktime человека, особенно эта часть:

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

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

Если заданное время, разбитый не может быть представлена ​​в виде календаря времени (секунд с начала эпохи), указываете ей() возвращает (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 также, но не проверен).

1

При успешном завершении, значение tm_wday и tm_yday компонентов структур установлены надлежащим образом, и других компонентов устанавливаются для представления указанного календаря время, ... C11dr §7.27.2.3 2

При вызове mktime(&tm) исходные значения tm не допускаются.

Из-за первого mktime(&tm) вызова, конечно tm.tm_isdst и tm.tm_hour доводили до 1 и 11. Таким образом, OP-х следующий код tm.tm_isdst = 1; и tm.tm_isdst = -1; не влияет на временную метку.

Лучше всего установить все поля для исследования.

struct tm tm0 = {0}; 
struct tm tm; 
int secs; 

tm0.tm_sec = 0; 
tm0.tm_min = 0; 
tm0.tm_hour = 12; 
tm0.tm_mon = 9 - 1; 
tm0.tm_mday = 30; 
tm0.tm_year = 2016 - 1900; 

tm = tm0; 
tm.tm_isdst = 0; 
secs = mktime(&tm); 
printf("%i\n", (int) secs); 

tm = tm0; 
tm.tm_isdst = 1; 
secs = mktime(&tm); 
printf("%i\n", (int) secs); 

tm = tm0; 
tm.tm_isdst = -1; 
secs = mktime(&tm); 
printf("%i\n", (int) secs); 

 Смежные вопросы

  • Нет связанных вопросов^_^