2

When using mktime(), the .tm_isdst member, when 0 or 1, results in a struct tm --> time_t difference of one 1 hour or the day light savings time shift for many zones such as America/ Los_Angeles.

This is as expected and consistent with most time-zones. For 2 struct tm that differ only in .tm_isdst = 0 or 1, the result of mktime() differ by the daylight shift.

With a few zones, including ones which employ daylight savings times, (e. g. America/Dawson_Creek) the mktime() result seems to ignore .tm_isdst for most struct tm times unless it is that one hour of that 25-hour day when going off daylight savings time.

1) Why do some timezones ignore .tm_isdst? (Even though they use DST) Perhaps I have missed something.

My opinion is that mktime() is broke for some minor zones that derive data from the major zones.

2) If indeed a gcc mktime() bug, what are the next steps?

Note: No issues seen when .tm_idst < 0.


System call mktime ignores tm_isdst flag does not apply here as the struct tm is refreshed before each mktime() call.

Output (Scroll down and notice the * * * lines)

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, 

Test Code

#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;
}

Some compiler output

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

I used zdump to dump zone data yet found no difference for zones that work as expected and those that did not.
Some time zone data dump.

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
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Sometimes I wonder if people ask questions here to gauge public reaction to a bug that someone's refusing to acknowledge... please tell me this is not one of those occasions? To be clear, I've read some manuals and so forth, and it seems like a bug to me too... the next step, you asked about, well, the answer is to report it as a bug. They may have a rational explanation, but I think time-related programming is just a difficult task to get right, as some jurisdictions just randomly invent leap seconds which you'd need to add for example... 'tis flawed from the get-go. – autistic Aug 23 '18 at 23:47
  • @autistic [this is not one of those occasions](https://stackoverflow.com/questions/51995342/why-mktime-unexpectedly-ignores-tm-isdst-for-some-timezones?noredirect=1#comment90940789_51995342), it is a issue uncovered in my coding this month. "report it as a bug." --> OK, yet I am unfamiliar with how and where and who "they" are. Your ideas would be appreciated. Aside: I do not see leap-seconds as part of the issue. – chux - Reinstate Monica Aug 23 '18 at 23:53
  • I'm also unfamiliar with how and where and who "they" are, but I presume since you're using `gcc` that'll be on some kind of `glibc`-related mailing list or git repo or something... you might want to see about improving your testcase, though. It might be a good idea to use `assert()` to codify what is expected, rather than `printf` to print out data and rely upon the human brain to deduce what is expected. Not sure if it's the answer you're looking for but I notice you call `tzset` and the POSIX manual says "Local timezone information shall be set as though mktime() called tzset()." – autistic Aug 24 '18 at 00:05
  • Yes, the more I look at it, the more I think you've stumbled across a bug... but you've also created one yourself... I don't think you should need to set the `tm_isdst` member there, and I don't think `-2` for that field means what you think it means... [This testcase](http://codepad.org/eau5XKVD) makes a bit more sense; it demonstrates how `localtime` should be setting `tm_isdst` (to either `1` to indicate that the OS is certain DST occurred, or `-1` to indicate that the information is unavailable/uncertain). Same bug, I think; just as `localtime` should set, `mktime` shouldn't ignore. – autistic Aug 24 '18 at 03:00
  • If you're looking to report bugs in glibc, you can go to the [GNU glibc home page](https://www.gnu.org/software/libc/) and follow the [Get Involved](https://www.gnu.org/software/libc/involved.html) link which has further link on how to triage and report bugs. Note that Glibc is somewhat independent of the compiler. You also appear to be using Cygwin; it is not necessarily a bug in Glibc but could be a problem in Cygwin. I've no proof either way — I merely raise possibilities. – Jonathan Leffler Aug 24 '18 at 18:35

0 Answers0