获取C中的时区GMT偏移量

时间:2012-12-10 15:37:18

标签: c datetime timezone libc

我正在使用标准mktime函数将struct tm转换为纪元时间值。 tm字段在本地填充,我需要将纪元时间作为GMT。 tm有一个gmtoff字段,允许您为此目的设置本地GMT偏移量(以秒为单位)。

但我无法弄明白如何获取这些信息。当然必须有某个标准函数来返回偏移量? localtime如何做到这一点?

8 个答案:

答案 0 :(得分:20)

请执行以下操作:

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

注意:time()返回的纪元以来的秒数就像在格林威治一样。

答案 1 :(得分:6)

  

本地时间如何做到这一点?

根据localtime手册页

  

localtime()函数就好像它调用了tzset(3)并设置了   外部变量tzname包含有关当前时区的信息,    timezone 与Coordinated Universal之间的差异   时间(UTC)和本地标准时间(秒)

因此,您可以致电localtime(),但timezonetzset()会有所不同:

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

注意:如果您选择localtime_r(),请注意不需要设置这些变量,您需要先调用tzset()来设置timezone

  

根据POSIX.1-2004,localtime()需要表现得像   调用tzset(),,而localtime_r()没有这个   需求。对于可移植代码,应该先调用tzset()   则localtime_r()

答案 2 :(得分:6)

我想在问之前我应该​​多做一些搜索。事实证明,有一个鲜为人知的timegm函数与gmtime相反。它支持GNU和BSD,这对我的目的来说已经足够了。更便携的解决方案是暂时将TZ环境变量的值设置为“UTC”,然后使用mktime,然后再设置TZ

timegm对我有用。

答案 3 :(得分:4)

获取本地时间偏移功能的通用版本在这里 我从statckoverflow中的the answer借了一些代码

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

答案 4 :(得分:0)

我认为至少在Linux中存在以下情况:时区信息来自/ usr / share / zoneinfo /。 localtime读取/ etc / localtime,它应该是zoneinfo中相应文件的副本。您可以通过在时区文件上执行zdump -v来查看内部情况(zdump可能位于sbin中,但您不需要提升权限来读取时区文件)。这是一个剪辑:

/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 

我想你可以自己解析这个问题。我不确定是否有一个stdlib函数只返回gmtoff(可能会有,但我不知道......)

编辑: man tzfile描述了zoneinfo文件的格式。您应该能够简单地映射到适当类型的结构。它似乎是zdump基于其所做的事情。

答案 5 :(得分:0)

这里有两条线,灵感来自@Hill和@friedo的答案:

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

以秒为单位返回UTC的偏移量。

不需要定义_GNU_SOURCE,但是请注意,timegm不是POSIX标准,在GNU和BSD之外可能不可用。

答案 6 :(得分:0)

这是可移植的解决方案,可以在所有标准C(和C ++)平台上使用:

const std::time_t epoch_plus_11h = 60 * 60 * 11;
const int local_time = localtime(&epoch_plus_11h)->tm_hour;
const int gm_time = gmtime(&epoch_plus_11h)->tm_hour;
const int tz_diff = local_time - gm_time;

使用C ++时添加std::命名空间。结果以小时为单位,范围为[-11,12];

说明: 我们只是将日期时间“ 1970-01-01 11:00:00”转换为tm结构两次-使用本地时区和GMT。结果是小时部分之间的差异。

之所以选择“ 11:00 :: 00”,是因为这是我们在全球范围具有相同日期的唯一时间点(考虑格林尼治标准时间)。因此,我们不必在计算中考虑日期更改带来的额外魔术。

警告

我的答案的先前版本仅在linux上有效:

// DO NOT DO THAT!!
int timezonez_diff = localtime(&epoch_plus_11h)->tm_hour -
                     gmtime(&epoch_plus_11h)->tm_hour;

这可能不起作用,因为结果tm对象作为指针从localtimegmtime返回的存储可能是共享的(并且在Windows / msvc上)。这就是我介绍临时计算的原因。

答案 7 :(得分:0)

到此结束。确保tm_secs是多余的,只是为了保持一致性。

int timezone_offset() {
    time_t zero = 0;
    const tm* lt = localtime( &zero );
    int unaligned = lt->tm_sec + ( lt->tm_min + ( lt->tm_hour * 60 ) ) * 60;
    return lt->tm_mon ? unaligned - 24*60*60 : unaligned;
}