Get the time zone GMT offset in C

I guess I should have done a bit more searching before asking. It turns out there's a little known timegm function which does the opposite of gmtime. It's supported on GNU and BSD which is good enough for my purposes. A more portable solution is to temporarily set the value of the TZ environment variable to "UTC" and then use mktime, then set TZ back.

But timegm works for me.


The universal version of obtaining local time offset function is here.
I borrowed pieces of code from this answer in stackoverflow.

int time_offset()
{
    time_t gmt, rawtime = time(NULL);
    struct tm *ptm;

#if !defined(WIN32)
    struct tm gbuf;
    ptm = gmtime_r(&rawtime, &gbuf);
#else
    ptm = gmtime(&rawtime);
#endif
    // Request that mktime() looksup dst in timezone database
    ptm->tm_isdst = -1;
    gmt = mktime(ptm);

    return (int)difftime(rawtime, gmt);
}

Just do the following:

#define _GNU_SOURCE /* for tm_gmtoff and tm_zone */

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

/* Checking errors returned by system calls was omitted for the sake of readability. */
int main(void)
{
  time_t t = time(NULL);
  struct tm lt = {0};

  localtime_r(&t, &lt);

  printf("Offset to GMT is %lds.\n", lt.tm_gmtoff);
  printf("The time zone is '%s'.\n", lt.tm_zone);

  return 0;
}

Note: The seconds since epoch returned by time() are measured as if in Greenwich.


How does localtime do it?

According to localtime man page

The localtime() function acts as if it called tzset(3) and sets the external variables tzname with information about the current timezone, timezone with the difference between Coordinated Universal Time (UTC) and local standard time in seconds

So you could either call localtime() and you will have the difference in timezone or call tzset():

extern long timezone;
....
tzset();
printf("%ld\n", timezone);

Note: if you choose to go with localtime_r() note that it is not required to set those variables you will need to call tzset() first to set timezone:

According to POSIX.1-2004, localtime() is required to behave as though tzset() was called, while localtime_r() does not have this requirement. For portable code tzset() should be called before localtime_r()