【问题标题】:How do I get time_t in GMT on Windows in C如何在 C 中的 Windows 上获取 GMT 中的 time_t
【发布时间】:2012-07-31 21:14:55
【问题描述】:

我正在编写一些可以在多个相互通信的系统上运行的代码。我使用 time() 来获取 time_t,但这会导致系统之间的时区差异出现问题,所以我想在 GMT 中获取 time_t。我一直在查看 time.h 函数,但我不清楚如何确定我会正确获得时间。到目前为止,这是我想出的:

time_t t = time();
struct tm *gtm = gmtime(&t);
time_t gt = mktime(gtm);

现在,这似乎在我的机器上得到了正确的答案,但我想知道它是否会在我推出之前通用,即使其他机器的时钟设置为当地时间或 GMT 或不同时间区域或其他任何东西。我担心的原因是mktime。在描述中,它说它将 tm 结构解释为“以本地时间表示的日历时间”。在我看来,它不会返回 GMT 时间,尽管它似乎在我的机器上。另外,当我打印出 gt 时,它比 t 提前 4 小时,这似乎是正确的。但如果我运行以下命令:

time_t t = time();
struct tm *gtm = gmtime(&t);
struct tm *ltm = localtime(&t);
printf("%d, %d\n", gtm->tm_hour, ltm->tm_hour);

时间都是一样的,都是当地时间,这不是我所期望的。

为了记录,在另一个答案中,我看到了对 timegm() 的引用,这听起来很完美,但它在我的系统上不存在。

简而言之,我如何在 C 语言的任何 Windows 机器上获取 GMT 的 time_t?

编辑:删除了添加的 msvcrt 标记,因为我没有使用 msvcrt。

【问题讨论】:

    标签: c time gmt time-t


    【解决方案1】:

    根据定义,time_t 始终采用 UTC。所以 time() 做你想做的事。只有在 time_t 和细分时间表示之间进行转换时,时区才会发挥作用。

    如果您有 UTC 时间的分解时间表示,您需要暂时将时区切换为 UTC,使用 mktime() 转换为 time_t,然后将时区切换回来,如下所示:

    time_t convert_utc_tm_to_time_t (struct tm *tm)
    {
        char *tz;
        time_t result;
    
        /* temporarily set timezone to UTC for conversion */
        tz = getenv("TZ");
        if (tz) {
          tz = strdup (tz);
          if (!tz) {
            // out of memory
            return -1;
          }
        }
        setenv("TZ", "", 1);
        tzset();
    
        tm->tm_isidst = 0;
        result = mktime (tm);
    
        /* restore timezone */
        if (tz) {
          setenv("TZ", tz, 1);
          free (tz);
        }
        else {
          unsetenv("TZ");
        }
        tzset();
    
        return result;
    }
    

    【讨论】:

    • 你需要复制getenv()返回的字符串,因为它返回一个指向当前环境的直接指针,当你修改环境时,它就会失效。
    • FWIW、GNU 的 libc 和一些 BSD 也有一个非标准函数 timegm(3)gmtime 的反函数,但除非您针对特定系统,否则我不会使用它为了便携性。
    • 谢谢,这正是我想要的。所以,如果 time() 在不同的时区给我不同的结果,问题很可能是计算机,而不是代码。也许他们的时钟设置错了或什么的。
    • 迂腐提示:在致电mktime() 之前,可能需要为tm->tm_isidst == 0 投保。
    • "根据定义,time_t 始终采用 UTC。"在 *nix 中可能是正确的,当然在 windows 和其他地方很常见,但它并没有按照 C 规范定义。
    【解决方案2】:

    查看代码的第一部分:

    time_t t = time();
    struct tm *gtm = gmtime(&t);
    time_t gt = mktime(gtm);
    

    您将 gt 设置为虚假值。调用mktime 时,需要传入当地时间的struct tm*time_t 定义为自 1970 年 1 月 1 日 00:00 UTC 以来的秒数。除非您当前处于 +0000 时区,否则您对 mktime 的调用的返回值将代表过去或未来几个小时的某个时间。

    至于第二部分:

    time_t t = time();
    struct tm *gtm = gmtime(&t);
    struct tm *ltm = localtime(&t);
    printf("%d, %d\n", gtm->tm_hour, ltm->tm_hour);
    

    我认为 gmtimelocaltime 在内存中返回相同的缓冲区,这可能就是为什么您在输出中得到相同的两个小时。第二个调用覆盖第一个调用的输出。试试这个:

    struct tm gtm, ltm;
    struct tm *tm_buf;
    time_t t = time();
    tm_buf = gmtime(&t);
    memcpy(&gtm, tm_buf, sizeof(struct tm));
    tm_buf = localtime(&t);
    memcpy(&ltm, tm_buf, sizeof(struct tm));
    printf("%d, %d\n", gtm.tm_hour, ltm.tm_hour);
    

    【讨论】:

    • 这很有帮助,谢谢。既然你提到了它,我看到了一些关于 gmtime 和 localtime 共享一个结构,但我忘记了。这肯定会导致令人困惑的结果。
    • 这是唯一正确的答案。这个错误完全是技术性的。
    【解决方案3】:

    使用gmtime() 作为您想要的格林威治标准时间数字。 localtime 会报本地时区的时间,当本地时区也是 GMT 时才会报 GMT。

    Windows 有自己的代码和数据结构来处理时间和时区。要使用 Windows 特定的 c 代码获取 GMT,您可以使用GetSystemTime()。您可以在此 URL 找到有关它的更多信息:http://msdn.microsoft.com/en-us/library/windows/desktop/ms724390(v=vs.85).aspx

    请注意,Windows 使用自己的日期/时间结构,但GetSystemTime() 及其相关页面也对此进行了详细说明。简而言之,GetSystemTime() 检索当前系统日期和时间,以协调世界时 (UTC)(也称为 GMT)表示。它使用Windows结构SYSTEMTIME,这是一个包含许多时间元素的结构。

    【讨论】:

    • 我忘了提到我无法访问 Windows 函数,只能访问标准库函数。另外,我使用 localtime 只是为了与 gmt 进行比较,而不是因为我认为它会给我正确的值。
    【解决方案4】:

    请注意,对于这种微不足道的转换,您可以使用自己的函数。这里有一份mkgmtime()

    https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/mkgmtime.c

    这允许您将struct tm 转换为time_t 值,而忽略任何时区信息。不包括 cmets,这可能不到 100 行代码,所以很容易将它添加到您的项目中。

    【讨论】:

    • 链接报告“错误 404 很抱歉,我们无法处理此请求。”。建议更新或代码
    • @chux 啊!它移动了几次。我想现在在 github 上查找它更容易。代码有点多,比如 .c 有 258 行代码,.h 有更多代码
    猜你喜欢
    • 2011-05-09
    • 2011-12-16
    • 2012-04-03
    • 2015-04-02
    • 1970-01-01
    • 2012-11-28
    • 2013-09-21
    • 2016-09-12
    • 1970-01-01
    相关资源
    最近更新 更多