我正在使用标准mktime
函数将 astruct tm
转换为纪元时间值。这些tm
字段在本地填充,我需要将纪元时间设为 GMT。tm
有一个gmtoff
字段允许您为此目的以秒为单位设置本地 GMT 偏移量。
但我不知道如何获取这些信息。当然一定有一个标准函数可以返回偏移量吗?怎么localtime
做?
只需执行以下操作:
#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, <);
printf("Offset to GMT is %lds.\n", lt.tm_gmtoff);
printf("The time zone is '%s'.\n", lt.tm_zone);
return 0;
}
注意:自 epoch 返回的秒数按time()
格林威治标准测量。
localtime 是怎么做的?
根据localtime
手册页
localtime() 函数的行为就像它调用 tzset(3) 并设置外部变量 tzname 与有关当前时区的信息, 时区 与协调世界时 (UTC) 和本地标准时间之间的差异(以秒为单位)
所以你可以打电话localtime()
,你会有所不同timezone
或打电话tzset()
:
extern long timezone;
....
tzset();
printf("%ld\n", timezone);
注意:如果您选择localtime_r()
注意不需要设置这些变量,则需要先调用tzset()
set timezone
:
根据 POSIX.1-2004,localtime() 的行为就像调用了 tzset() 一样,而 localtime_r() 没有这个要求。对于可移植代码 tzset() 应该在 localtime_r() 之前调用
我想我应该在问之前做更多的搜索。事实证明,有一个鲜为人知的timegm
功能与gmtime
. 它在 GNU 和 BSD 上受支持,这对我的目的来说已经足够了。更便携的解决方案是将TZ
环境变量的值临时设置为“UTC”,然后使用mktime
,然后再设置TZ
回去。
但timegm
对我有用。
获取本地时间偏移功能的通用版本在这里。我从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);
}
这是适用于所有标准 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;
std::
使用 C++ 时添加命名空间。结果以小时为单位,范围为 [-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
对象的存储作为指针返回localtime
并且gmtime
可能是共享的(并且它位于 windows/msvc 上)。这就是我引入临时计算的原因。
我相信至少在 linux 中是这样的:时区信息来自 /usr/share/zoneinfo/。localtime 读取 /etc/localtime,它应该是 zoneinfo 中相应文件的副本。您可以通过zdump -v
对时区文件执行操作来查看里面的内容(zdump 可能在 sbin 中,但您不需要提升权限来读取时区文件)。这是其中的一个片段:
/usr/share/zoneinfo/EST5EDT 2033 年 11 月 6 日星期日 05:59:59 UTC = 2033 年 11 月 6 日星期日 01:59:59 EDT isdst=1 gmtoff=-14400 /usr/share/zoneinfo/EST5EDT 2033 年 11 月 6 日星期日 06:00:00 UTC = 2033 年 11 月 6 日星期日 01:00:00 EST isdst=0 gmtoff=-18000 /usr/share/zoneinfo/EST5EDT 2034 年 3 月 12 日星期日 06:59:59 UTC = 2034 年 3 月 12 日星期日 01:59:59 EST isdst=0 gmtoff=-18000 /usr/share/zoneinfo/EST5EDT 2034 年 3 月 12 日星期日 07:00:00 UTC = 2034 年 3 月 12 日星期日 03:00:00 EDT isdst=1 gmtoff=-14400 /usr/share/zoneinfo/EST5EDT 2034 年 11 月 5 日星期日 05:59:59 UTC = 2034 年 11 月 5 日星期日 01:59:59 EDT
如果你愿意,我想你可以自己解析这个。我不确定是否有一个只返回 gmtoff 的 stdlib 函数(很可能有,但我不知道......)
编辑: man tzfile 描述了 zoneinfo 文件的格式。您应该能够简单地映射到适当类型的结构中。这似乎是 zdump 基于它所做的事情。
这是受@Hill 和@friedo 答案启发的两条线:
#include <time.h>
...
time_t rawtime = time(0);
timeofs = timegm(localtime(&rawtime)) - rawtime;
以秒为单位返回与 UTC 的偏移量。
不需要_GNU_SOURCE
定义,但请注意,这timegm
不是 POSIX 标准,可能在 GNU 和 BSD 之外不可用。
就这样结束了。当然 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;
}
这是我的方式:
time_t z = 0;
struct tm * pdt = gmtime(&z);
time_t tzlag = mktime(pdt);
替代自动本地存储struct tm
:
struct tm dt;
memset(&dt, 0, sizeof(struct tm));
dt.tm_mday=1; dt.tm_year=70;
time_t tzlag = mktime(&dt);
tzlag
,以秒为单位,将是 UTC 偏移量的负数;与 UTC 相比,您的时区标准时间滞后:
LocalST + tzlag = UTC
如果您还想考虑“夏令时” ,请在申请后(或使用 获得后)减去特定本地时间tm_isdst
的tzlag
位置。tm_isdst
struct tm
mktime
localtime
工作原理:
该集合struct tm
用于“纪元”时刻,即 1970 年 1 月 1 日,对应于0。在该日期time_t
调用会将其转换为 UTC(因此得到 0),然后从中减去 UTC 偏移量为了产生输出。因此它产生 UTC_offset 的负数。mktime()
time_t
time_t