【问题标题】:Converting GMT time to Epoch将 GMT 时间转换为 Epoch
【发布时间】:2018-06-28 06:20:44
【问题描述】:

我在将 GMT 时间转换为 unix EPOCH 时间时遇到了很多麻烦。

问题似乎与 mktime 有关。我当前的时区提前 +13 小时。 我认为这是问题所在,mktime 假定为本地时间,当它计算一个纪元时,它返回的值比实际 GMT 时间早大约 12 小时。

这是我的代码。

time_t t;
struct tm tm;
strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm);
tm.tm_isdst = -1; // -1 tells mktime figure it out
t = mktime(&tm);

当 s = "2018-01-19T03:35:12Z" 时,它计算的纪元为 1516286112 而不是应该提前大约 12 小时的时代。

在 BIOS 时钟设置为 GMT 的系统上,不会发生此错误。 所以我只能得出结论,当这种情况属实时,mktime 正在按预期工作。

顺便说一句,该字符串来自在文档上显示正确时间的 nosql 服务器。我正在获取当前时代并将其与文档中的解析日期进行比较并计算差异。这就是一切变得混乱的地方!

【问题讨论】:

  • mktime 采用当地时间...
  • 是的,那就是问题所在。我想我可以计算它并添加偏移秒数。
  • 我写了some code 来做这件事,一次。它不是特别漂亮,但通常有效。 timegm() 不是标准的,但您可能可以使用。
  • 更改时区,然后调用mktime()。
  • 注意:struct tm tm; strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm); tm.tm_isdst = -1; 在技术上是不够的,因为tm 中的其他成员可能存在未初始化。最好先struct tm tm = {0};

标签: c datetime


【解决方案1】:

所以问题是mktime 尊重当地时区。

要解决此问题,请在可用的情况下使用 timegm。或者从这个答案中自己动手。

Minimal implementation of gmtime algorithm?

【讨论】:

    【解决方案2】:

    将格林威治标准时间转换为纪元

    mktime() 完成了 Y-M-D.h:m:s 到线性时间转换的繁重工作,但它假定为本地时间。

    为了好玩,下面是一个不更改时区的便携式解决方案,也不假设 time_t 本身是“自 1970 年 1 月 1 日以来的秒数”。

    它为 GMT 时间戳和 1970 年 1 月 2 日计算 2 time_t,就好像它们在当地时间一样。无论time_t 使用的单位如何,它们与difftime() 的差异都以秒为单位(类似Unix 时间)。每天差/秒的商,四舍五入到最接近的整数(以解决 DST 和时区调整 mktime() 的本地时间性在差分和舍入中被有效地抵消了。 Jan 2,而不是 Jan 1,用于避免 Local Jan 1, 1970 可能早于 GMT Jan 1,1970。在这些情况下,一些mktime() 报告-1。

    #include <stdio.h>
    #include <time.h>
    #include <math.h>
    
    static time_t ymd_to_time_t(int year1900, int monthJan, int day) {
      struct tm tm = {0};
      tm.tm_year = year1900;
      tm.tm_mon = monthJan;
      tm.tm_mday = day;
      return mktime(&tm);
    }
    
    #define SECS_PER_DAY (24.0*60*60)
    
    // Only 6 members of gmt are relevant
    long long GMT_time_to_Unix_Epoch(const struct tm *gmt) {
      time_t t0 = ymd_to_time_t(1970-1900, 1-1, 2);
      if (t0 == (time_t)-1) return t0;
      time_t t1 = ymd_to_time_t(gmt->tm_year, gmt->tm_mon, gmt->tm_mday);
      if (t1 == (time_t)-1) return t1;
      double d = difftime(t1, t0);
      long days = lrint(d/SECS_PER_DAY) + 1;  // + 1 for (Jan 2 - Jan 1)
      long long Unix_Epoch = ((days*24LL + gmt->tm_hour)*60 + gmt->tm_min)*60 + gmt->tm_sec;
      return Unix_Epoch;
    }
    
    int main(void) {
      struct tm tm = {0};
      tm.tm_year = 2018 - 1900;
      tm.tm_mon = 1 -1 ;
      tm.tm_mday = 19;
      tm.tm_hour = 3;
      tm.tm_min = 35;
      tm.tm_sec = 12;
      printf("%lld\n", GMT_time_to_Unix_Epoch(&tm));
    }
    

    输出

    1516332912
    

    从 1970 年起,如果时区改变了它所在的国际日期变更线的哪一侧,那么这种方法就失效了。例如kiribati

    【讨论】:

    • 谢谢 chux。好在我不必担心基里巴斯。
    【解决方案3】:

    你需要使用tm_gmtoff,使用代码如下:

    int64_t time = mktime(&tm_time);
    time += tm_time.tm_gmtoff;
    if (tm_time.tm_isdst == 1){
        time -= 3600;
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-26
      • 2012-12-25
      • 2021-07-28
      • 2012-04-01
      • 2019-12-24
      • 2013-10-29
      • 2021-10-02
      相关资源
      最近更新 更多