Get the time zone GMT offset in C

纵饮孤独 提交于 2019-12-17 16:06:38

问题


I'm using the standard mktime function to turn a struct tm into an epoch time value. The tm fields are populated locally, and I need to get the epoch time as GMT. tm has a gmtoff field to allow you to set the local GMT offset in seconds for just this purpose.

But I can't figure out how to get that information. Surely there must be a standard function somewhere that will return the offset? How does localtime do it?


回答1:


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.




回答2:


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()




回答3:


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.




回答4:


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

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



回答5:


I believe the following is true in linux at least: timezone info comes from /usr/share/zoneinfo/. localtime reads /etc/localtime which should be a copy of the appropriate file from zoneinfo. You can see whats inside by doing zdump -v on the timezone file (zdump may be in sbin but you don't need elevated permissions to read timezone files with it). Here is a snipped of one:

/usr/share/zoneinfo/EST5EDT  Sun Nov  6 05:59:59 2033 UTC = Sun Nov  6 01:59:59 2033 EDT isdst=1 gmtoff=-14400
/usr/share/zoneinfo/EST5EDT  Sun Nov  6 06:00:00 2033 UTC = Sun Nov  6 01:00:00 2033 EST isdst=0 gmtoff=-18000
/usr/share/zoneinfo/EST5EDT  Sun Mar 12 06:59:59 2034 UTC = Sun Mar 12 01:59:59 2034 EST isdst=0 gmtoff=-18000
/usr/share/zoneinfo/EST5EDT  Sun Mar 12 07:00:00 2034 UTC = Sun Mar 12 03:00:00 2034 EDT isdst=1 gmtoff=-14400
/usr/share/zoneinfo/EST5EDT  Sun Nov  5 05:59:59 2034 UTC = Sun Nov  5 01:59:59 2034 EDT 

I guess you could parse this yourself if you want. I'm not sure if there is a stdlib function that just returns the gmtoff (there may well be but I don't know...)

edit: man tzfile describes the format of the zoneinfo file. You should be able to simply mmap into a structure of the appropriate type. It appears to be what zdump is doing based on an strace of it.




回答6:


Here's a two-liner inspired by @Hill's and @friedo's answers:

#include <time.h>
...
time_t rawtime = time(0);
timeofs = timegm(localtime(&rawtime)) - rawtime;

Returns offset from UTC in seconds.

Doesn't need _GNU_SOURCE defined, but note that timegm is not a POSIX standard and may not be available outside of GNU and BSD.



来源:https://stackoverflow.com/questions/13804095/get-the-time-zone-gmt-offset-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!