将GMT时间转换为Epoch

时间:2018-01-19 03:44:18

标签: c datetime

我在将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服务器,它在文档上显示正确的时间。我正在抓住当前时代并将其与文档中的解析日期进行比较并计算差异。这就是一切都变得混乱的地方!

3 个答案:

答案 0 :(得分:1)

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

要解决此问题,请使用 timegm 。或者从这个答案中推出自己的。

Minimal implementation of gmtime algorithm?

答案 1 :(得分:0)

  

将GMT时间转换为Epoch

mktime()做了Y-M-D.h的繁重工作:m:s到线性时间转换,但假设是当地时间。

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

它为GMT时间戳和1970年1月2日计算2 time_t,就好像它们在当地时间一样。无论difftime()使用的单位如何,它们与time_t的差异都是以秒为单位(类似Unix时间)。每日差异/秒,四舍五入到最接近的整数(用于解决DST和时区调整< 12小时)的商是自Unix纪元以来的日偏移量。 mktime()的本地时间在差异和舍入中被有效取消。 1月2日,而不是1月1日,用于避免1970年1月1日1970年1月1日至1970年1月1日。在这些情况下,有些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年以来一直在国际日期线的哪一侧移动,那么这种方法就会被取消。 E.g kiribati

答案 2 :(得分:0)

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

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