如何用C转换UTC到当地时间?

ase*_*seq 9 c time posix utc datetime-conversion

这是一个简单的问题,但解决方案似乎远非简单.我想知道如何从UTC转换为本地时间.我正在寻找一个标准的C解决方案,或多或少保证可以在任何位置的任何计算机上工作.

我仔细阅读了以下链接,但我找不到解决方案:

在C中将包含localtime的字符串转换为UTC

在C/C++中在本地时间和GMT/UTC之间转换

我尝试了很多变体,例如(datetime是一个带有时间和日期的字符串,以UTC表示):

strptime(datetime, "%A %B %d %Y %H %M %S", tp);
strftime(printtime, strlen(datetime), "%A %B %d %Y %H %M %S", tp);
Run Code Online (Sandbox Code Playgroud)

要么

strptime(datetime, "%A %B %d %Y %H %M %S", tp);
lt=mktime(tp);
printtime=ctime(&lt);
Run Code Online (Sandbox Code Playgroud)

无论我尝试什么,printtime最终都与UTC相同.

编辑11-29-2013:基于下面"R"的非常有用的答案,我终于开始创建一个工作示例.我发现它在我测试它的两个时区中正常工作,CET和PST:

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

long long diff_tm(struct tm *a, struct tm *b)
{
  return a->tm_sec - b->tm_sec
          +60LL*(a->tm_min - b->tm_min)
          +3600LL*(a->tm_hour - b->tm_hour)
          +86400LL*(a->tm_yday - b->tm_yday)
          +(a->tm_year-70)*31536000LL
          -(a->tm_year-69)/4*86400LL
          +(a->tm_year-1)/100*86400LL
          -(a->tm_year+299)/400*86400LL
          -(b->tm_year-70)*31536000LL
          +(b->tm_year-69)/4*86400LL
          -(b->tm_year-1)/100*86400LL
          +(b->tm_year+299)/400*86400LL;
}


int main()
{
  time_t utc, local;
  char buf[100];
  const char datetime[]="2013 11 30 23 30 26 UTC"; /* hard coded date and time in UTC */

  struct tm *tp=malloc(sizeof(struct tm));
  if(tp==NULL)
    exit(-1);

  struct tm *localt=malloc(sizeof(struct tm));
  if(localt==NULL)
    exit(-1);

  memset(tp, 0, sizeof(struct tm));
  memset(localt, 0, sizeof(struct tm));

  printf("UTC date and time to be converted in local time: %s\n", datetime);

  /* put values of datetime into time structure *tp */
  strptime(datetime, "%Y %m %d %H %M %S %z", tp);

  /* get seconds since EPOCH for this time */
  utc=mktime(tp);
  printf("UTC date and time in seconds since EPOCH: %d\n", utc);

  /* lets convert this UTC date and time to local date and time */

  struct tm e0={ .tm_year = 70, .tm_mday = 1 }, e1, new;
  /* get time_t EPOCH value for e0 (Jan. 1, 1970) */
  time_t pseudo=mktime(&e0);

  /* get gmtime for this value */
  e1=*gmtime(&pseudo);

  /* calculate local time in seconds since EPOCH */
  e0.tm_sec += utc - diff_tm(&e1, &e0);

  /* assign to local, this can all can be coded shorter but I attempted to increase clarity */
  local=e0.tm_sec;
  printf("local date and time in seconds since EPOCH: %d\n", local);

  /* convert seconds since EPOCH for local time into localt time structure */
  localt=localtime(&local);

  /* get nicely formatted human readable time */
  strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S %Z", localt);

  printf("local date and time: %s\n", buf);
}
Run Code Online (Sandbox Code Playgroud)

它应该在大多数系统上编译没有问题.我用UTC编写了一个时间和日期,然后将其转换为当地时间和日期.

R..*_*R.. 7

如果您可以假设POSIX(因此POSIX规范time_t自纪元以来的秒数),我将首先使用POSIX公式转换为自纪元以来的秒数:

tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
    (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
    ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
Run Code Online (Sandbox Code Playgroud)

接下来,使用localtime((time_t []){0})获得struct tm代表在当地时间的时代.将自纪元以来的秒数添加tm_sec到此字段中struct tm,然后调用mktime以对其进行规范化.

编辑:实际上唯一的POSIX依赖是拥有一个(time_t)0对应的已知时期.也许你可以找到一个办法解决,如果你真的使用这两个调用需要...例如gmtimelocaltimetime_t0 ..

编辑2:如何执行此操作的草图:

#include <time.h>
#include <stdio.h>

long long diff_tm(struct tm *a, struct tm *b)
{
        return a->tm_sec - b->tm_sec
                +60LL*(a->tm_min - b->tm_min)
                +3600LL*(a->tm_hour - b->tm_hour)
                +86400LL*(a->tm_yday - b->tm_yday)
                +(a->tm_year-70)*31536000LL
                -(a->tm_year-69)/4*86400LL
                +(a->tm_year-1)/100*86400LL
                -(a->tm_year+299)/400*86400LL
                -(b->tm_year-70)*31536000LL
                +(b->tm_year-69)/4*86400LL
                -(b->tm_year-1)/100*86400LL
                +(b->tm_year+299)/400*86400LL;
}

int main(int argc, char **argv)
{
        char buf[100];
        struct tm e0 = { .tm_year = 70, .tm_mday = 1 }, e1, new;
        time_t pseudo = mktime(&e0);
        e1 = *gmtime(&pseudo);
        e0.tm_sec += atoi(argv[1]) - diff_tm(&e1, &e0);
        mktime(&e0);
        strftime(buf, sizeof buf, "%c", &e0);
        puts(buf);
}
Run Code Online (Sandbox Code Playgroud)

请不要介意丑陋的输出代码.该程序以"相对于POSIX纪元的秒数​​"的形式进行争论,并以当地时间输出结果时间.您可以使用上面引用的公式将任何UTC时间转换为自纪元以来的秒数.请注意,此代码不以任何方式依赖于POSIX,但它确实假设返回的偏移量diff_tm与秒 - 自 - 纪元值一起不会溢出int.对此的修复将是使用long long偏移量和循环,该循环不断增加不大于INT_MAX/2(或小于INT_MIN/2)并且调用mktime重新正规化的增量,直到偏移量达到0.

  • 关于编辑:当然,一般情况下,UTC和本地时间之间的差值与您转换时的差值相同.也许在日本. (2认同)

Dac*_*den 5

啊......我可能只是C的初学者,但我得到了这个有用的例子:

#include <time.h>
#include <stdio.h>
int main(void)
{
        time_t abs_ts,loc_ts,gmt_ts;
        struct tm loc_time_info,gmt_time_info;

        /*Absolute time stamp.*/
        time(&abs_ts);

        /*Now get once the local time for this time stamp,
        **and once the GMT (UTC without summer time) time stamp.*/
        localtime_r(&abs_ts,&loc_time_info);
        gmtime_r(&abs_ts,&gmt_time_info);

        /*Convert them back.*/
        loc_ts=mktime(&loc_time_info);
        gmt_ts=mktime(&gmt_time_info);

        /*Unfortunately, GMT still has summer time. Get rid of it:*/
        if(gmt_time_info.tm_isdst==1)
                {gmt_ts-=3600;}

        printf("Local timestamp: %lu\n"
                "UTC timestamp: %lu\n"
                "Difference in hours: %lu\n\n",
                loc_ts,
                gmt_ts,
                (loc_ts-gmt_ts)/3600);

        return 0;
}
Run Code Online (Sandbox Code Playgroud)

哪个产生这个输出:

当地时间戳:1412554119

格林尼治标准时间戳:1412546919

小时差异:2

现在,你有UTC和本地时间之间的差异,以秒为单位.这应该足以转换它.

你的代码有一个注意事项,aseq:你在这里不需要使用malloc(你也可以在堆栈上设置memset,而malloc可能很贵,而堆栈分配通常要快得多),而且你不会释放它.这是非常非常糟糕的做法.

另一件事:

memset(tp,0,sizeof(struct tm));

如果您将sizeof(*tp)(或者,如果将tp放在堆栈上,sizeof(tp))传递给memset,那会更好.这可以确保即使对象的类型发生变化,它仍然会完全设置为memset.