Linux上的时区转换C API,有人吗?

Linux上的时区转换C API,有人吗?,c,linux,timezone,C,Linux,Timezone,我正在寻找一些我认为非常简单的东西——给定特定时区中的本地Unix时间(指定为字符串,例如“America/New_York”-注意,这不是我的本地时间),以GMT获取相应的时间值,即 time_t get_gmt_time(time_t local_time, const char* time_zone); 听起来似乎很简单,但我能找到的最接近的代码片段是来自timegm手册页的以下代码片段: #include <time.h>

我正在寻找一些我认为非常简单的东西——给定特定时区中的本地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;
       } 
#包括
#包括
时间
my_timegm(结构tm*tm)
{
时差;
char*tz;
tz=getenv(“tz”);
setenv(“TZ”,“1”);
tzset();
ret=mktime(tm);
如果(tz)
setenv(“TZ”,TZ,1);
其他的
未清算的环境(“TZ”);
tzset();
返回ret;
} 

一定有比这更好战的方式,不是线程安全的可憎的,对吧?对吧?

我真的以为我的口齿不清,但似乎记错了。我知道您可能正在寻找直接的C代码,但以下是我最好的:

我知道Python通过
tzinfo
类有一些时区的概念,您可以在。您可以查看该模块的源代码(在中,它在Modules/datetime.c中)-它似乎有一些文档,因此您可以从中获得一些信息。

来自tzfile(5),它以可怕的细节记录了/usr/share/zoneinfo(在我的系统上)中的文件:

时区似乎使用了tzfile 内部,但glibc拒绝 向用户空间公开它。这是最重要的 可能是因为标准化 函数更有用,也更简单 可移植,并由 格里伯


同样,这可能不是您想要的(即API),但是信息就在那里,您可以轻松地解析它。

与Python的答案类似,我可以向您展示它的功能:

R>now格式(now)#本地TZ下的格式
[1] "2009-08-03 18:55:57"
R> 格式(现在,tz=“欧洲/伦敦”)#明确tz下的格式
[1] "2009-08-04 00:55:57"
R> 格式(现在,tz=“America/Chicago”)#明确tz下的格式
[1] "2009-08-03 18:55:57"
R>
但是R使用了一个内部表示,它扩展了通常的
struct tm
——参见R-2.9.1/src/main/datetime.c


尽管如此,这是一个令人毛骨悚然的话题,如果它是标准库,那就太好了。既然不是,那么你最好的选择就是使用为什么不能使用
gmtime\u 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;
}

想在这里添加更多细节

如果您尝试以下操作:

#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;
}
从这个意义上说,您正试图“向后做”时间在UN*X中被视为绝对时间,即始终相对于“历元”(1970年1月1日0:00 UTC)

UTC和当前时区(上次
tzset()
call)之间的差异始终在
外部长时区中


这并不能消除环境操纵的丑陋性,但您可以省去经历
mktime()

gmtime、localtime及其变体的问题是依赖于TZ环境变量。时间函数首先调用tzset(void),它读取TZ以确定偏移量DST等。如果TZ未在用户环境中设置,(g)libc使用系统时区。因此,如果您在“欧洲/巴黎”中有一个本地struct tm,并且您的机器或环境设置为“美国/丹佛”,那么当您转换为GMT时,将应用错误的偏移量。所有时间函数都调用tzset(void),它读取TZ以设置char*tzname[2]、长时区(以秒为单位,从GMT开始)和int-daylight(对于DST为布尔值)。直接设置它们没有影响,因为tzset()将在下次调用localtime时覆盖它们,等等

我在最初的问题中遇到了与“igor”相同的问题,而setenv的工作似乎有问题(重入?)。我决定进一步查看是否可以将tzset(void)修改为tzset(char*)以显式设置上述变量。当然,这只是个坏主意。。。但是在探究glibc源和IANA TZ数据库源时,我得出结论,setenv方法并没有那么糟糕

首先,setenv只修改进程全局“char**environ”(不是调用shell,因此“real”TZ不受影响)。其次,glibc实际上在setenv中设置了一个锁。缺点是setenv/tzset调用不是原子的,因此可以想象,另一个线程可以在原始线程调用tzset之前写入TZ。但是一个使用线程的实现良好的应用程序无论如何都应该注意这一点


如果POSIX将tzset定义为在广泛的IANA TZ数据库中查找字符*(并将NULL定义为“使用用户或系统TZ/”),这将是很酷的,但是如果不能做到这一点,setenv似乎是可以的

是的,我的意思是直接的C API当然-主题现在更正了,谢谢!至于Python实现,我想到了这个想法,但我真的希望避免走这条路。查看了源代码和API文档后,我发现Python的datetime非常不了解时区——它提供的只是一个抽象的tzinfo类,如果要在时区之间进行时间转换,应该实现这个类。有一些库实现了这一点,但是,真的-事情真的那么糟糕吗?好问题。事情真的差不多就那么糟了。全世界似乎都认为你永远不会对假装在不同的时区感兴趣,或者需要转换到UTC以外的任何时区。这是一个问题。关于
time\u t
struct tm
的含义,这里有一点混淆。在所有UN*X-like系统上,
time\t
/
time()
是“自‘epoch’(UTC 0:00 01/01/1970)起的秒数”,可通过
localtime()
将其转换为
struct tm
(给出ti
#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;
}
$ 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