将包含本地时间的字符串转换为UTC时间(C语言)

5

我有一个包含本地日期/时间的字符串,需要将其转换为time_t值(在UTC中)- 我一直在尝试这样做:

char* date = "2009/09/01/00";
struct tm cal = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
strptime(date, "%Y/%m/%d/%H", &cal);
time_t t = mktime(&cal);

但是我得到的time_t值是我期望的UTC格式,而不是本地时间。也许我误解了strptime的作用,但是在我的时区(英国),在9月1日我们使用BST(即UTC + 1小时),所以我期望得到的值比UTC快1小时。

有没有一种方法可以将字符串解释为本地时间,并自动考虑当天生效的UTC偏移量?请注意,我需要time_t值而不是struct tm类型,在上面的示例中,我希望time_t值对应于2009-09-01 01:00:00 GMT。

3个回答

6
您可以使用mktime在本地时区中解释struct tm。在这样做时,请注意设置tm_isdst标志。它的值为0表示夏令时,1表示冬令时,将其设置为-1则表示由mktime自行判断。以下是一些示例代码:
void main()
{
    char* date = "2009/09/01/00";
    struct tm cal = {};
    // Read string into struct tm
    strptime(date, "%Y/%m/%d/%H", &cal);
    // Tell mktime to figure out the daylight saving time
    cal.tm_isdst = -1;
    printf("%20s: %s", "Before mktime", asctime(&cal));
    // Convert struct tm to time_t
    time_t t = mktime(&cal);
    // Convert time_t to localtime
    struct tm localcal = *localtime(&t);
    printf("%20s: %s", "Local time", asctime(&localcal));
    printf("%20s: %i\n", "Local DST", localcal.tm_isdst);
    // Convert time_t to GMT
    struct tm gmcal = *gmtime(&t);
    printf("%20s: %s", "GM time", asctime(&gmcal));
    printf("%20s: %i\n", "GM DST", gmcal.tm_isdst);
}

这将打印出(我生活在格林尼治标准时间+1区,现在是冬季):

   Before mktime: Tue Sep  1 00:00:00 2009
      Local time: Tue Sep  1 00:00:00 2009
       Local DST: 1
         GM time: Mon Aug 31 22:00:00 2009
          GM DST: 0

看起来mktime基于当前的夏令时将9月份的日期转换了。现在是11月,所以实际上相差一个小时。我还没有找到纠正这个问题的方法。


谢谢,但我感兴趣的是time_t值 - 如果我在您代码中的“localcal”结构上执行mktime(),它会给我与在“cal”上执行mktime()相同的结果。我需要一种方法来生成等同于“2009-09-01 01:00 GMT”的time_t值。 - codebox
1
C语言的惯例是将time_t存储为自1970-01-01以来UTC中的秒数。仅在解释或显示时才更改为本地时间。如果您想引入rob_t作为本地时间变量,可以计算gmtime()和localtime()之间的秒数差,并将其添加到您的time_t中。 :) - Andomar
好的,但我想将字符串解释为本地时间,而不是生成UTC时间字符串的本地时间等效项。在我们的示例中,假设我们生活在GMT+1时区,字符串“2009/09/01/00”是我们的本地时间,因此它相当于GMT中的“2009/09/01/01”,因此代码应返回对应于“2009/09/01/01”的time_t值。这有意义吗? - codebox
这个东西很复杂。原来mktime()确实需要一个本地时间结构体。我例子中的1小时差异是夏令时造成的,cal中被设置为0,但目前是1。我会编辑这个例子。 - Andomar
在某些情况下,如果您不想将时间转换为本地时间,您可以使用timegm作为mktime的UTC等效函数。 - lanoxx

0

这是我的版本,使用tm_gmtoff。希望库能处理夏令时...

#define _BSD_SOURCE
#define _XOPEN_SOURCE
#include <stdio.h>
#include <time.h>

int gmtoffset(void) {
  struct tm *dummy;
  time_t t = 0;
  dummy = localtime(&t);
  return dummy->tm_gmtoff; /* _BSD_SOURCE */
}

int main(void) {
  int off;
  const char *date = "2009/09/01/00";
  struct tm cal = {0};
  time_t t;

  off = gmtoffset();

  strptime(date, "%Y/%m/%d/%H", &cal); /* _XOPEN_SOURCE */
  t = mktime(&cal);
  printf("t     --> %s", ctime(&t)); /* ctime includes a final '\n' */
  t -= off;
  printf("t-off --> %s", ctime(&t));
  return 0;
}
$ /usr/bin/gcc ptime.c
$ ./a.out
t     --> 2009年9月1日 星期二 01:00:00
t-off --> 2009年9月1日 星期二 00:00:00

你传递给 mktimestruct tm 具有将 tm_isdst 设置为 0,因此读取的时间不考虑 DST。 然后你将其传递给 ctime,它通过添加 1 小时来进行 DST 修正。 接下来,你减去偏移量,这也是 1 小时,以获得随机时间。 这些东西让我感到头晕,但我想要彻底搞清楚它 :) - Andomar
这个夏令时(DST)真是有趣。我刚意识到目前并没有实行夏令时,但是在9月1日的时候却实行了!嗯...我猜图书馆无法“知道”特定地点何时开始和结束夏令时。我认为你必须手动管理夏令时(http://timeanddate.com/time/dst2009.html)。 - pmg

0

我想现在我已经解决了,感谢 Andomar - 这段代码做到了我需要的,并且似乎无论当前夏令时状态如何都能正常工作(我改变了电脑上的时钟来验证):

#include <time.h>
#include <assert.h>

time_t parseLocalDate(char* date){
    struct tm cal = {0, 0, 0, 0, 0, 0, 0, 0, -1, 0, NULL};
    strptime(date, "%Y/%m/%d/%H", &cal);
    return mktime(&cal);
}

int main(int argc, char *argv[]){
 // DST is effect, Local Time = GMT+1
    assert(1251759600 == parseLocalDate("2009/09/01/00")); // Mon, 31 Aug 2009 23:00:00 GMT
    assert(1254351600 == parseLocalDate("2009/10/01/00")); // Wed, 30 Sep 2009 23:00:00 GMT
 // DST not in effect, Local Time = GMT
    assert(1257033600 == parseLocalDate("2009/11/01/00")); // Sun, 01 Nov 2009 00:00:00 GMT
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接