Почему `mktime()` неожиданно игнорирует.tm_isdst для некоторых часовых поясов?

Когда используешь mktime(), .tm_isdst член, когда 0 или 1, приводит к struct tm -> time_t разница в 1 час или дневной световой сдвиг для многих зон, таких как Америка / Лос-Анджелес.

Это соответствует ожиданиям и согласуется с большинством часовых поясов. Для 2 struct tm которые отличаются только .tm_isdst = 0 or 1, результат mktime() отличаются дневной сменой.

С несколькими зонами, включая те, которые используют летнее время (например, Америка / Доусон-Крик) mktime() результат, кажется, игнорирует .tm_isdst для большинства struct tm раз, если это не один час из этого 25-часового дня при переходе на летнее время.

1) Почему некоторые часовые пояса игнорируют .tm_isdst ? (Хотя они используют DST) Возможно, я что-то пропустил.

Мое мнение таково, что mktime() сломан для некоторых второстепенных зон, которые получают данные из основных зон.

2) Если действительно GCC mktime() ошибка, каковы следующие шаги?

Примечание: проблем не было, когда .tm_idst < 0,


Системный вызов mktime игнорирует, флаг tm_isdst здесь не применяется, так как struct tm обновляется перед каждым mktime() вызов.

Вывод (прокрутите вниз и обратите внимание на строки * * *)

time_t (and delta from previous) via various .isdst settings.
    Unchanged after localtime(),   -1         ,    0         ,   1  

America/Los_Angeles
  No trouble here, each  delta goes up by 3600 or the one-time 0 for .isdst=0, .isdst=1
  gmt:Sun Oct 25 05:30:00 1970 +0000 GMT   0
local:Sat Oct 24 22:30:00 1970 -0700 PDT   1
time_t:25680600,25680600      , 25680600      , 25684200      , 25680600      , 
  gmt:Sun Oct 25 06:30:00 1970 +0000 GMT   0
local:Sat Oct 24 23:30:00 1970 -0700 PDT   1
time_t:25684200,25684200  3600, 25684200  3600, 25687800  3600, 25684200  3600, 
  gmt:Sun Oct 25 07:30:00 1970 +0000 GMT   0
local:Sun Oct 25 00:30:00 1970 -0700 PDT   1
time_t:25687800,25687800  3600, 25687800  3600, 25691400  3600, 25687800  3600, 
  gmt:Sun Oct 25 08:30:00 1970 +0000 GMT   0
local:Sun Oct 25 01:30:00 1970 -0700 PDT   1
time_t:25691400,25691400  3600, 25695000  7200, 25695000  3600, 25691400  3600, 
  gmt:Sun Oct 25 09:30:00 1970 +0000 GMT   0
local:Sun Oct 25 01:30:00 1970 -0800 PST   0
time_t:25695000,25695000  3600, 25695000     0, 25695000     0, 25691400     0, 
  gmt:Sun Oct 25 10:30:00 1970 +0000 GMT   0
local:Sun Oct 25 02:30:00 1970 -0800 PST   0
time_t:25698600,25698600  3600, 25698600  3600, 25698600  3600, 25695000  3600, 
  gmt:Sun Oct 25 11:30:00 1970 +0000 GMT   0
local:Sun Oct 25 03:30:00 1970 -0800 PST   0
time_t:25702200,25702200  3600, 25702200  3600, 25702200  3600, 25698600  3600, 

Africa/Juba
  Trouble here, delta goes up by 7200, .isdst seems only relevant for the one hour.  else time_t same without regard to `.isdst.

  gmt:Wed Oct 14 17:30:00 1970 +0000 GMT   0
local:Wed Oct 14 20:30:00 1970 +0300 CAST  1
time_t:24773400,24773400      , 24773400      , 24773400      , 24773400      , 
  gmt:Wed Oct 14 18:30:00 1970 +0000 GMT   0
local:Wed Oct 14 21:30:00 1970 +0300 CAST  1
time_t:24777000,24777000  3600, 24777000  3600, 24777000  3600, 24777000  3600, 
  gmt:Wed Oct 14 19:30:00 1970 +0000 GMT   0
local:Wed Oct 14 22:30:00 1970 +0300 CAST  1
time_t:24780600,24780600  3600, 24780600  3600, 24780600  3600, 24780600  3600, 
  gmt:Wed Oct 14 20:30:00 1970 +0000 GMT   0
local:Wed Oct 14 23:30:00 1970 +0300 CAST  1
time_t:24784200,24784200  3600, 24784200  3600, 24787800  7200, 24784200  3600, ***
  gmt:Wed Oct 14 21:30:00 1970 +0000 GMT   0
local:Wed Oct 14 23:30:00 1970 +0200 CAT   0
time_t:24787800,24787800  3600, 24784200     0, 24787800     0, 24784200     0, 
  gmt:Wed Oct 14 22:30:00 1970 +0000 GMT   0
local:Thu Oct 15 00:30:00 1970 +0200 CAT   0
time_t:24791400,24791400  3600, 24791400  7200, 24791400  3600, 24791400  7200, ***
  gmt:Wed Oct 14 23:30:00 1970 +0000 GMT   0
local:Thu Oct 15 01:30:00 1970 +0200 CAT   0
time_t:24795000,24795000  3600, 24795000  3600, 24795000  3600, 24795000  3600, 

America/Dawson_Creek
  Trouble here, delta goes up by 7200, .isdst seems only relevant for the one hour.  else time_t same without regard to `.isdst.
  gmt:Sun Oct 25 05:30:00 1970 +0000 GMT   0
local:Sat Oct 24 22:30:00 1970 -0700 PDT   1
time_t:25680600,25680600      , 25680600      , 25680600      , 25680600      , 
  gmt:Sun Oct 25 06:30:00 1970 +0000 GMT   0
local:Sat Oct 24 23:30:00 1970 -0700 PDT   1
time_t:25684200,25684200  3600, 25684200  3600, 25684200  3600, 25684200  3600, 
  gmt:Sun Oct 25 07:30:00 1970 +0000 GMT   0
local:Sun Oct 25 00:30:00 1970 -0700 PDT   1
time_t:25687800,25687800  3600, 25687800  3600, 25687800  3600, 25687800  3600, 
  gmt:Sun Oct 25 08:30:00 1970 +0000 GMT   0
local:Sun Oct 25 01:30:00 1970 -0700 PDT   1
time_t:25691400,25691400  3600, 25695000  7200, 25695000  7200, 25691400  3600, ***
  gmt:Sun Oct 25 09:30:00 1970 +0000 GMT   0
local:Sun Oct 25 01:30:00 1970 -0800 PST   0
time_t:25695000,25695000  3600, 25695000     0, 25695000     0, 25691400     0, 
  gmt:Sun Oct 25 10:30:00 1970 +0000 GMT   0
local:Sun Oct 25 02:30:00 1970 -0800 PST   0
time_t:25698600,25698600  3600, 25698600  3600, 25698600  3600, 25698600  7200, ***
  gmt:Sun Oct 25 11:30:00 1970 +0000 GMT   0
local:Sun Oct 25 03:30:00 1970 -0800 PST   0
time_t:25702200,25702200  3600, 25702200  3600, 25702200  3600, 25702200  3600, 

America/Fort_Nelson
  Trouble here, delta goes up by 7200, .isdst seems only relevant for the one hour.  else time_t same without regard to `.isdst.
  gmt:Sun Oct 25 05:30:00 1970 +0000 GMT   0
local:Sat Oct 24 22:30:00 1970 -0700 PDT   1
time_t:25680600,25680600      , 25680600      , 25680600      , 25680600      , 
  gmt:Sun Oct 25 06:30:00 1970 +0000 GMT   0
local:Sat Oct 24 23:30:00 1970 -0700 PDT   1
time_t:25684200,25684200  3600, 25684200  3600, 25684200  3600, 25684200  3600, 
  gmt:Sun Oct 25 07:30:00 1970 +0000 GMT   0
local:Sun Oct 25 00:30:00 1970 -0700 PDT   1
time_t:25687800,25687800  3600, 25687800  3600, 25687800  3600, 25687800  3600, 
  gmt:Sun Oct 25 08:30:00 1970 +0000 GMT   0
local:Sun Oct 25 01:30:00 1970 -0700 PDT   1
time_t:25691400,25691400  3600, 25695000  7200, 25695000  7200, 25691400  3600, ***
  gmt:Sun Oct 25 09:30:00 1970 +0000 GMT   0
local:Sun Oct 25 01:30:00 1970 -0800 PST   0
time_t:25695000,25695000  3600, 25695000     0, 25695000     0, 25691400     0, 
  gmt:Sun Oct 25 10:30:00 1970 +0000 GMT   0
local:Sun Oct 25 02:30:00 1970 -0800 PST   0
time_t:25698600,25698600  3600, 25698600  3600, 25698600  3600, 25698600  7200, ***
  gmt:Sun Oct 25 11:30:00 1970 +0000 GMT   0
local:Sun Oct 25 03:30:00 1970 -0800 PST   0
time_t:25702200,25702200  3600, 25702200  3600, 25702200  3600, 25702200  3600, 

America/Punta_Arenas
  Trouble here, delta goes up by 7200, .isdst seems only relevant for the one hour.  else time_t same without regard to `.isdst.

  gmt:Sat Mar 28 23:30:00 1970 +0000 GMT   0
local:Sat Mar 28 20:30:00 1970 -0300 -03   1
time_t: 7515000, 7515000      ,  7515000      ,  7515000      ,  7515000      , 
  gmt:Sun Mar 29 00:30:00 1970 +0000 GMT   0
local:Sat Mar 28 21:30:00 1970 -0300 -03   1
time_t: 7518600, 7518600  3600,  7518600  3600,  7518600  3600,  7518600  3600, 
  gmt:Sun Mar 29 01:30:00 1970 +0000 GMT   0
local:Sat Mar 28 22:30:00 1970 -0300 -03   1
time_t: 7522200, 7522200  3600,  7522200  3600,  7522200  3600,  7522200  3600, 
  gmt:Sun Mar 29 02:30:00 1970 +0000 GMT   0
local:Sat Mar 28 23:30:00 1970 -0300 -03   1
time_t: 7525800, 7525800  3600,  7529400  7200,  7529400  7200,  7525800  3600, ***
  gmt:Sun Mar 29 03:30:00 1970 +0000 GMT   0
local:Sat Mar 28 23:30:00 1970 -0400 -04   0
time_t: 7529400, 7529400  3600,  7529400     0,  7529400     0,  7525800     0, 
  gmt:Sun Mar 29 04:30:00 1970 +0000 GMT   0
local:Sun Mar 29 00:30:00 1970 -0400 -04   0
time_t: 7533000, 7533000  3600,  7533000  3600,  7533000  3600,  7533000  7200, ***
  gmt:Sun Mar 29 05:30:00 1970 +0000 GMT   0
local:Sun Mar 29 01:30:00 1970 -0400 -04   0
time_t: 7536600, 7536600  3600,  7536600  3600,  7536600  3600,  7536600  3600, 

Тестовый код

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

// Some code to set the time zone
extern void tzset(void);
int set_tz(const char *tz) {
  //tz = getenv("TZ");
  //if (tz) {
  printf("%s\n", tz);
  if (setenv("TZ", tz, 1))
    return 1;
  tzset();
  return 0;
}

// Print time info around the potential mistake    
void time_oops(const char *tz, time_t t0, time_t t1) {
  time_t before[4] = {0};
  if (set_tz(tz)) {
    return;
  }
  set_tz(tz);
  for (time_t t = t0; t <= t1; t += 3600) {

    char buf[200];
    struct tm *tm = gmtime(&t);
    strftime(buf, sizeof buf, "%c %z %Z", tm);
    printf("  gmt:%-36s %d\n", buf, tm->tm_isdst);

    tm = localtime(&t);
    strftime(buf, sizeof buf, "%c %z %Z", tm);
    printf("local:%-36s %d\n", buf, tm->tm_isdst);

    printf("time_t:%8jd,", (intmax_t) t);
    for (int dst = -2; dst <= 1; dst++) {
      tm = localtime(&t);
      if (dst > -2)
        tm->tm_isdst = dst;
      time_t t_make = mktime(tm);
      printf("%8jd", (intmax_t) t_make);
      if (t == t0) {
        printf(" %5s, ", "");
      } else {
        printf(" %5jd, ", (intmax_t) (t_make - before[dst + 2]));
      }
      before[dst + 2] = t_make;
    }
    puts("");
  }
}

// Run some tests    
int main(void) {
  int h = 60 * 60;
  time_oops("America/Los_Angeles", 25662600 + 5 * h, 25662600 + 11 * h);
  time_oops("Africa/Juba", 24784200 - 3 * h, 24784200 + 3 * h);
  time_oops("America/Dawson_Creek", 25662600 + 5 * h, 25662600 + 11 * h);
  time_oops("America/Fort_Nelson", 25687800 - 2 * h, 25687800 + 4 * h);
  time_oops("America/Punta_Arenas", 7522200 - 2 * h, 7522200 + 4 * h);
  return 0;
}

Некоторый вывод компилятора

make all 
Building file: ../test.c
Invoking: Cygwin C Compiler
gcc -O0 -g3 -pedantic -Wall -Wextra -Wconversion -c -fmessage-length=0 -v -MMD -MP -MF"test.d" -MT"test.o" -o "test.o" "../test.c"
Using built-in specs.
COLLECT_GCC=gcc
Target: x86_64-pc-cygwin
Configured with: /cygdrive/i/szsz/tmpp/gcc/gcc-7.3.0-3.x86_64/src/gcc-7.3.0/configure --srcdir=/cygdrive/i/szsz/tmpp/gcc/gcc-7.3.0-3.x86_64/src/gcc-7.3.0 --prefix=/usr --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc --docdir=/usr/share/doc/gcc --htmldir=/usr/share/doc/gcc/html -C --build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=x86_64-pc-cygwin --without-libiconv-prefix --without-libintl-prefix --libexecdir=/usr/lib --enable-shared --enable-shared-libgcc --enable-static --enable-version-specific-runtime-libs --enable-bootstrap --enable-__cxa_atexit --with-dwarf2 --with-tune=generic --enable-languages=ada,c,c++,fortran,lto,objc,obj-c++ --enable-graphite --enable-threads=posix --enable-libatomic --enable-libcilkrts --enable-libgomp --enable-libitm --enable-libquadmath --enable-libquadmath-support --disable-libssp --enable-libada --disable-symvers --with-gnu-ld --with-gnu-as --with-cloog-include=/usr/include/cloog-isl --without-libiconv-prefix --without-libintl-prefix --with-system-zlib --enable-linker-build-id --with-default-libstdcxx-abi=gcc4-compatible --enable-libstdcxx-filesystem-ts
Thread model: posix
gcc version 7.3.0 (GCC) 
COLLECT_GCC_OPTIONS='-O0' '-g3' '-Wpedantic' '-Wall' '-Wextra' '-Wconversion' '-c' '-fmessage-length=0' '-v' '-MMD' '-MP' '-MF' 'test.d' '-MT' 'test.o' '-o' 'test.o' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-pc-cygwin/7.3.0/cc1.exe -quiet -v -MMD test.d -MF test.d -MP -MT test.o -dD -idirafter /usr/lib/gcc/x86_64-pc-cygwin/7.3.0/../../../../lib/../include/w32api -idirafter /usr/lib/gcc/x86_64-pc-cygwin/7.3.0/../../../../x86_64-pc-cygwin/lib/../lib/../../include/w32api ../test.c -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase-strip test.o -g3 -O0 -Wpedantic -Wall -Wextra -Wconversion -version -fmessage-length=0 -o /cygdrive/c/Users/TPC/AppData/Local/Temp/cc3JcC90.s
GNU C11 (GCC) version 7.3.0 (x86_64-pc-cygwin)
    compiled by GNU C version 7.3.0, GMP version 6.1.2, MPFR version 4.0.1-p6, MPC version 1.1.0, isl version isl-0.16.1-GMP

я использовал zdump Для сброса данных зон пока не найдено различий для зон, которые работают как ожидалось, и тех, которые не работали.
Некоторый дамп данных часового пояса.

zdump -v /usr/share/zoneinfo/America/Los_Angeles
zdump -v /usr/share/zoneinfo/America/Dawson_Creek

/usr/share/zoneinfo/America/Los_Angeles Sun Apr 26 09:59:59 1970 UT = Sun Apr 26 01:59:59 1970 PST isdst=0 gmtoff=-28800
/usr/share/zoneinfo/America/Los_Angeles Sun Apr 26 10:00:00 1970 UT = Sun Apr 26 03:00:00 1970 PDT isdst=1 gmtoff=-25200
/usr/share/zoneinfo/America/Los_Angeles Sun Oct 25 08:59:59 1970 UT = Sun Oct 25 01:59:59 1970 PDT isdst=1 gmtoff=-25200
/usr/share/zoneinfo/America/Los_Angeles Sun Oct 25 09:00:00 1970 UT = Sun Oct 25 01:00:00 1970 PST isdst=0 gmtoff=-28800
/usr/share/zoneinfo/America/Dawson_Creek Sun Apr 26 09:59:59 1970 UT = Sun Apr 26 01:59:59 1970 PST isdst=0 gmtoff=-28800
/usr/share/zoneinfo/America/Dawson_Creek Sun Apr 26 10:00:00 1970 UT = Sun Apr 26 03:00:00 1970 PDT isdst=1 gmtoff=-25200
/usr/share/zoneinfo/America/Dawson_Creek Sun Oct 25 08:59:59 1970 UT = Sun Oct 25 01:59:59 1970 PDT isdst=1 gmtoff=-25200
/usr/share/zoneinfo/America/Dawson_Creek Sun Oct 25 09:00:00 1970 UT = Sun Oct 25 01:00:00 1970 PST isdst=0 gmtoff=-28800

0 ответов

Другие вопросы по тегам