Почему `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