如何在C中从UTC转换为本地时间? [英] How to convert from UTC to local time in C?

查看:32
本文介绍了如何在C中从UTC转换为本地时间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

It's a simple question, but the solution appears to be far from simple. I would like to know how to convert from UTC to local time. I am looking for a solution in C that's standard and more or less guaranteed to work on any computer at any location.

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

I have read the following links carefully but I can't find a solution there:

将包含本地时间的字符串转换为 C 中的 UTC

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

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

I have tried a number of variations, such as (datetime is a string with time and date in UTC):

strptime(datetime, "%A %B %d %Y %H %M %S", tp);
strftime(printtime, strlen(datetime), "%A %B %d %Y %H %M %S", tp);

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

无论我尝试什么,打印时间最终都与 UTC 相同.

No matter what I try printtime ends up being the same as UTC.

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

Edit 11-29-2013: based on the very helpful answer by "R" below I finally got around to create a working example. I found it to be working correct in the two timezones I tested it, CET and 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
", 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
", 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
", 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
", buf);
}

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

It should compile without problems on most systems. I hard coded a time and date in UTC which then will be converted to the local time and date.

推荐答案

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

If you can assume POSIX (and thus the POSIX specification of time_t as seconds since the epoch), I would first use the POSIX formula to convert to seconds since the epoch:

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

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

Next, use localtime((time_t []){0}) to get a struct tm representing the epoch in local time. Add the seconds since the epoch to the tm_sec field of this struct tm, then call mktime to canonicalize it.

实际上,唯一的 POSIX 依赖项是具有 (time_t)0 对应的已知时期.也许你可以找到解决方法,如果你真的需要......例如在 time_t 0 使用对 gmtimelocaltime 的调用..

Actually the only POSIX dependency is having a known epoch which (time_t)0 corresponds to. Perhaps you can find a way around that if you really need to... for instance using calls to both gmtime and localtime at time_t 0..

编辑 2:如何做到这一点的草图:

Edit 2: A sketch of how to do this:

#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);
}

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

Please don't mind the ugly output code. This program takes an argument in the form of "seconds relative to the POSIX epoch" and outputs the resulting time in local time. You can convert any UTC time to seconds since the epoch using the formula I cited above. Note that this code does not in any way depend on POSIX, but it does assume the offset returned by diff_tm combined with the seconds-since-the-epoch value does not overflow int. A fix for this would be to use a long long offset and a loop that keeps adding increments no larger than INT_MAX/2 (or smaller than INT_MIN/2) and calling mktime to renormalize until the offset reaches 0.

这篇关于如何在C中从UTC转换为本地时间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆