我正在寻找一些我认为非常简单的东西 - 考虑到特定时区的本地Unix时间(指定为字符串,例如“America / New_York” - 请注意不是我的当地时间),在GMT中获得相应的时间值。即,
的内容time_t get_gmt_time(time_t local_time,
const char* time_zone);
听起来很简单,我能找到的最接近的是来自timegm手册页的以下代码片段:
#include <time.h>
#include <stdlib.h>
time_t
my_timegm(struct tm *tm)
{
time_t ret;
char *tz;
tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
}
必须有一种比这更好的方式,而不是线程安全的憎恶,对吧?右??
答案 0 :(得分:6)
想在这里添加更多细节。
如果您尝试以下操作:
#include <stdio.h>
#include <time.h> /* defines 'extern long timezone' */
int main(int argc, char **argv)
{
time_t t, lt, gt;
struct tm tm;
t = time(NULL);
lt = mktime(localtime(&t));
gt = mktime(gmtime(&t));
printf( "(t = time(NULL)) == %x,\n"
"mktime(localtime(&t)) == %x,\n"
"mktime(gmtime(&t)) == %x\n"
"difftime(...) == %f\n"
"timezone == %d\n", t, lt, gt,
difftime(gt, lt), timezone);
return 0;
}
您会注意到时区转换确保:
mktime(localtime(t)) == t
和mktime(gmtime(t)) == t + timezone
,difftime(mktime(gmtime(t)), mktime(localtime(t))) == timezone
tzset()
初始化的全局变量或任何时区转换函数的调用。)上述示例输出:
$ TZ=GMT ./xx (t = time(NULL)) == 4dd13bac, mktime(localtime(&t)) == 4dd13bac, mktime(gmtime(&t)) == 4dd13bac difftime(...) == 0.000000 timezone == 0 $ TZ=EST ./xx (t = time(NULL)) == 4dd13baf, mktime(localtime(&t)) == 4dd13baf, mktime(gmtime(&t)) == 4dd181ff difftime(...) == 18000.000000 timezone == 18000 $ TZ=CET ./xx (t = time(NULL)) == 4dd13bb2, mktime(localtime(&t)) == 4dd13bb2, mktime(gmtime(&t)) == 4dd12da2 difftime(...) == -3600.000000 timezone == -3600
从这个意义上说,你试图“向后做” - time_t
在UN * X中被视为绝对,即始终相对于“EPOCH”(0: 00 UTC(1970年1月1日)。
UTC与当前时区(上次tzset()
来电)之间的差异始终位于external long timezone
全局。
这并没有摆脱环境操纵的丑陋,但你可以节省自己通过mktime()
的努力。
答案 1 :(得分:4)
来自tzfile(5),它以令人毛骨悚然的细节记录/ usr / share / zoneinfo(在我的系统上)中的文件:
似乎时区使用tzfile 内部,但glibc拒绝 将其暴露给用户空间。这是最多的 可能因为标准化 函数更有用 便携式的,实际记录的 glibc的。
同样,这可能不是您正在寻找的(即API),但信息就在那里,您可以轻松解析它。
答案 2 :(得分:1)
我真的觉得有些东西在滑稽,但似乎有错误的记忆。我知道你可能正在寻找直接的C代码,但这是我得到的最好的代码:
我知道Python在tzinfo
类中有一些时区概念 - 你可以在datetime documentation中阅读它。您可以查看模块的源代码(在tarball中,它位于Modules / datetime.c中) - 它似乎有一些文档,所以也许你可以从中获得一些东西。
答案 3 :(得分:1)
与Python答案类似,我可以向您展示R的作用:
R> now <- Sys.time() # get current time
R> format(now) # format under local TZ
[1] "2009-08-03 18:55:57"
R> format(now,tz="Europe/London") # format under explicit TZ
[1] "2009-08-04 00:55:57"
R> format(now,tz="America/Chicago") # format under explicit TZ
[1] "2009-08-03 18:55:57"
R>
但是R使用扩展通常struct tm
的内部表示---见R-2.9.1 / src / main / datetime.c。
尽管如此,这是一个毛茸茸的主题,如果它是标准库会很好。因为它不是你最好的选择是使用Boost Date_Time (example)
答案 4 :(得分:1)
gmtime,localtime及其变体的问题在于依赖于TZ环境变量。时间函数首先调用tzset(void),它读取TZ以确定偏移量DST等。如果在用户环境中未设置TZ,则(g)libc使用系统时区。因此,如果您在“欧洲/巴黎”中有本地结构,并且您的机器或环境设置为“美国/丹佛”,则转换为GMT时将应用错误的偏移。所有时间函数都调用tzset(void),它读取TZ以设置char * tzname [2],长时区(diff,以秒为单位,从GMT开始)和int daylight(布尔值为DST)。直接设置这些没有任何影响,因为tzset()将在您下次调用localtime等时覆盖它们。
在原始问题中我遇到了与'igor'相同的问题,而setenv工作似乎有问题(re-entran?)。我决定进一步查看是否可以将tzset(void)修改为tzset(char *)以显式设置上述变量。嗯,当然,这只是一个坏主意......但在探讨glibc源代码和IANA TZ数据库源代码时,我得出的结论是setenv方法并不是那么糟糕。
首先,setenv只修改进程全局'char ** environ'(不是调用shell,因此'真正的'TZ不受影响)。第二,glibc实际上锁定了setenv。缺点是setenv / tzset调用不是原子的,所以在原始线程调用tzset之前,另一个线程可以想象地写入TZ。但是,使用线程的良好实现的应用程序应该注意这一点。
如果POSIX定义tzset在广泛的IANA TZ数据库中查找char *(并使用NULL表示'使用用户或系统TZ /),那将会很酷,但如果失败,setenv似乎是好。
答案 5 :(得分:0)
为什么不能使用gmtime_r()
?以下工作对我来说很好:
int main()
{
time_t t_gmt, t_local=time(NULL);
struct tm tm_gmt;
gmtime_r(&t_local, &tm_gmt);
t_gmt = mktime(&tm_gmt);
printf("Time now is: %s", ctime(&t_local));
printf("Time in GMT is: %s", ctime(&t_gmt));
return 0;
}