4

Here is my code:

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

time_t my_timegm (struct tm *tm, char *tz_offset);

int
main (void)
{
  struct tm time_tm;
  time_t start_timet, end_timet;

  time_tm.tm_sec = 0;
  time_tm.tm_min = 0;
  time_tm.tm_hour = 0;
  time_tm.tm_mday = 1;
  time_tm.tm_mon = 0;
  time_tm.tm_year = 2023 - 1900;
  time_tm.tm_isdst = -1;
  start_timet = my_timegm (&time_tm, "Etc/GMT-6");
  printf ("\n%li ", start_timet);
  start_timet = my_timegm (&time_tm, "America/Chicago");
  printf ("\n%li ", start_timet);
}

And my_timegm is

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

time_t
my_timegm (struct tm *tm, char *tz_offset)
{
  time_t ret;
  char *tz;

  tz = getenv ("TZ");
  setenv ("TZ", tz_offset, 1);
  tzset ();
  ret = mktime (tm);
  if (tz)
    setenv ("TZ", tz, 1);
  else
    unsetenv ("TZ");
  tzset ();
  return ret;
}

The output is:

1672509600

1672552800

Why is there a 43200 seconds (12 hours) difference? Or, why is GMT-6 not the same as America/Chicago, which is UTC-6?

2

3 Answers 3

5

Blame POSIX.

When the POSIX specification for the TZ environment variable was originally created, the positive time zone digits were assigned for timezones of the Western Hemisphere, because that was probably the only consensus that could be achieved out of existing practices of the various Unix system developers... and most or all of them had their headquarters in the continental USA at the time, so they did not want to start using that pesky minus sign on their home turf.

Essentially, TZ=xxx-6 would specify a timezone labeled xxx where you can get UTC by subtracting 6 hours from the local time.

The zoneinfo database (once known as Olson timezone database, now maintained by IANA) uses the same sign convention for its Etc/GMTxx timezones, for legacy-compatibility reasons.

$ TZ=GMT-6 date              # legacy POSIX
Sun 11 Jun 10:23:18 GMT 2023 # note: GMT is a lie here!
$ TZ=Etc/GMT-6 date          # modern zoneinfo form
Sun 11 Jun 10:23:25 +06 2023

$ TZ=GMT6 date               # legacy POSIX
Sat 10 Jun 22:24:31 GMT 2023 # note: GMT is a lie here!
TZ=Etc/GMT+6 date            # modern zoneinfo form
Sat 10 Jun 22:24:35 -06 2023

Note that when you use the zoneinfo form, the UTC offset displayed along with the timestamp (+06 or -06) has the opposite sign from what you might expect from the zoneinfo timezone name.

4
  • Legacy POSIX definitions for the old etc zone (thanks to @PeterM comment above): github.com/eggert/tz/blob/main/etcetera - you can find even more info in the file comments. Jun 15 at 19:14
  • That's interesting but does it also explain the 12 hour difference? That half day still puzzles me.
    – Ned64
    Jun 29 at 17:07
  • @Ned64 POSIX TZ environment variable definition has the sign of the timezone value the opposite of what you would expect, and the newer Olson/IANA Etc/GMTnn timezones follow the POSIX convention, rather than the "obvious" one. So America/Chicago (POSIX: CST6CDT) is 6 hours west of Greenwich (I.e. GMT - 6 hours), and Etc/GMT-6 is actually 6 hours east of Greenwich, i.e. you an me would call it "GMT + 6 hours". The total difference between these two timezones (which are located at exactly the opposite sides of the earth) is 12 hours.
    – telcoM
    Jul 2 at 12:33
  • @telcoM OK, thanks. Now I got it.
    – Ned64
    Jul 2 at 12:41
2

"I want to take a longitude, divide by 15 and use this as an offset"

There is an error in that. By that reasoning, the GMT zone must extend from 0 to 15 west, with other zones running 15w to 30w, 30w to 45w and so on.

But the GMT zone extends from 7.5 east to 7.5 west. Thus the next zone to the west is 7.5w to 22.5w then 22.5w to 37.5w and so on.

That doesn't explain the 12 hour difference you noted, but does point to an error in the desired calculation.

4
  • Very Interesting. I will have to look at what use cases would be affected. Or just write some code to fix it. Thank You Jun 11 at 4:09
  • 1
    Dividing by 15 gives exactly that, provided the result is rounded properly (not truncated)
    – ilkkachu
    Jun 11 at 7:55
  • @ilkkachu: In what way? What is divided? What kind of rounding? Can you provide an example? Jun 11 at 14:23
  • 1
    @PeterMortensen, really? The quote here says "take a longitude, divide by 15". And as for rounding, wouldn't the usual do? Longitudes x between 7.5 and 22.5 would give x/15 between 0.5 and 1.5, rounding to 1. The only way to get the 0..15, 15..30 etc. zones as described here would be to truncate the result, i.e. to drop any fractional part.
    – ilkkachu
    Jun 11 at 15:41
1

I just found an answer in the post date tz converts with GMT and City Names with same GMT get differ result, that states GMT-x is actually x hours AHEAD of GMT. This is validated in my code.

int
main (void)
{
  struct tm time_tm;
  time_t start_timet, end_timet;

  time_tm.tm_sec = 0;
  time_tm.tm_min = 0;
  time_tm.tm_hour = 0;
  time_tm.tm_mday = 1;
  time_tm.tm_mon = 0;
  time_tm.tm_year = 2023 - 1900;
  time_tm.tm_isdst = 0;
  start_timet = my_timegm (&time_tm, "America/Chicago");
  printf ("\n%li ", start_timet);
  time_tm.tm_isdst = 0;
  start_timet = my_timegm (&time_tm, "Etc/GMT+6");
  printf ("\n%li ", start_timet);
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .