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?

3 Answers3

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.

telcoM
  • 96,466
  • Legacy POSIX definitions for the old etc zone (thanks to @PeterM comment above): https://github.com/eggert/tz/blob/main/etcetera - you can find even more info in the file comments. – Mark Glossop Jun 15 '23 at 19:14
  • That's interesting but does it also explain the 12 hour difference? That half day still puzzles me. – Ned64 Jun 29 '23 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 02 '23 at 12:33
  • @telcoM OK, thanks. Now I got it. – Ned64 Jul 02 '23 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.

  • Very Interesting. I will have to look at what use cases would be affected. Or just write some code to fix it. Thank You – Walter L. Preuninger II Jun 11 '23 at 04:09
  • 1
    Dividing by 15 gives exactly that, provided the result is rounded properly (not truncated) – ilkkachu Jun 11 '23 at 07:55
  • @ilkkachu: In what way? What is divided? What kind of rounding? Can you provide an example? – Peter Mortensen Jun 11 '23 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 '23 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); }

Z0OM
  • 3,149