如何在C中将UTC时间转换为本地时间?

发布于 2024-12-29 17:30:00 字数 3101 浏览 2 评论 0原文

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

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

在C中将包含本地时间的字符串转换为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);

或者

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

无论我尝试什么,打印时间最终都与 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);
}

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

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:

Converting string containing localtime into UTC in C

Converting Between Local Times and GMT/UTC in C/C++

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

Or

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

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

南…巷孤猫 2025-01-05 17:30:00

如果您可以假设 POSIX(因此 time_t 的 POSIX 规范为自纪元以来的秒数),我将首先使用 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

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

编辑:实际上,唯一的 POSIX 依赖性是具有 (time_t)0 对应的已知纪元。如果您确实需要的话,也许您可​​以找到解决方法...例如在 time_t 0 处调用 gmtimelocaltime

编辑 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);
}

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

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

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.

Edit: 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..

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

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.

晚雾 2025-01-05 17:30:00

啊……我可能只是 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;
}

它产生了这个输出:

本地时间戳:1412554119

GMT 时间戳:1412546919

小时数差异:2

现在您已了解 UTC 和本地时间之间的差异(以秒为单位)。这应该足以转换它。

对您的代码的一点注释是,aseq:您在这里不需要使用 malloc(您也可以在堆栈上 memset 值,并且 malloc 可能很昂贵,而堆栈分配通常要快得多),并且您不释放它。这是非常非常糟糕的做法。

另一件事:

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

如果将 sizeof(*tp) (或者,如果将 tp 放入堆栈,则将 sizeof(tp))传递给 memset,效果会更好。这确保了即使对象的类型发生变化,它仍然会被完全memset。

Ahm ... I might just be a beginner in C, but I got this working example:

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

Which produces this output:

Local timestamp: 1412554119

GMT timestamp: 1412546919

Difference in hours: 2

Now you have the difference between UTC and local time in seconds. That should be enough to convert it.

One note to your code, aseq: you are using malloc without need here (you can memset values on the stack as well, and malloc can be expensive while stack allocation is often much faster), and you do not free it. That's very, very bad practise.

Another thing:

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

Would be better done if you'd pass sizeof(*tp) (or, if you put tp on the stack, sizeof(tp)) to memset. That ensures that even if the type of your object changes, it will still be fully memset.

烟雨凡馨 2025-01-05 17:30:00

总结一下:将 UTC 中的细分日期(struct tm)转换为(本地)日历时间(time_t)是通过 timegm() 实现的 - 与 mktime() 相反 - 但 timegm() 不是标准函数(这是什么逻辑)。
C 标准只给我们留下了 time()、gmtime()、mktime() 和 difftime()。

在其他文档中找到的解决方法建议通过首先将环境变量 TZ 设置为空字符串来模拟 timegm(),然后调用 mktime() 产生 UTC 日历时间,然后将 TZ 重置为其初始值,但再一次,这是不标准。

基本上,据我了解,本地时间和 UTC 时间之间的差异只是一个偏移量,因此如果我们可以评估该偏移量,我们就可以调整 mktime() 的结果,所以这是我的建议:

time_t my_timegm(struct tm *tm) {
    time_t epoch = 0;
    time_t offset = mktime(gmtime(&epoch));
    time_t utc = mktime(tm);
    return difftime(utc, offset);
}

快速测试:

int main(void) {
    time_t now = time(0);
    struct tm local = *localtime(&now);
    struct tm utc = *gmtime(&now);
    time_t t1 = mktime(&local);
    time_t t2 = my_timegm(&utc);
    assert(t1 == t2);
    printf("t =%lu\nt1=%lu\nt2=%lu\n",now,t1,t2);
    return 0;
}

To sum-up: the conversion of a broken down date (struct tm) in UTC to a (local) calendar time (time_t) is achieved with timegm() - the opposite of mktime() - BUT timegm() is not a standard function (how logic is that).
The C standard leaves us with only time(), gmtime(), mktime() and difftime().

A workaround found in other docs advises to emulate timegm() by setting first the environment variable TZ to a null string, then calling mktime() resulting in an UTC calendar time, then resetting TZ to its initial value, but once again, this is not standard.

Basically, as I understand it, the difference between a local time and UTC time is just an offset so if we can evaluate that offset, we can adjust the result of mktime(), so here's my proposition:

time_t my_timegm(struct tm *tm) {
    time_t epoch = 0;
    time_t offset = mktime(gmtime(&epoch));
    time_t utc = mktime(tm);
    return difftime(utc, offset);
}

A quick test:

int main(void) {
    time_t now = time(0);
    struct tm local = *localtime(&now);
    struct tm utc = *gmtime(&now);
    time_t t1 = mktime(&local);
    time_t t2 = my_timegm(&utc);
    assert(t1 == t2);
    printf("t =%lu\nt1=%lu\nt2=%lu\n",now,t1,t2);
    return 0;
}
冰葑 2025-01-05 17:30:00
//working stand alone function adjusting UTC to local date and time
//globals(unsigned integers): gps.Mth, gps.Yr, gps.Hm (eg:2115 for 21:15)
//adjust date and time according to UTC
//tz(timezone) eg: 1100, for 11 hours, tzdir: 1 forward, 0 backwards            





    void AdjustUTCToTimeZone(u16 tz, u8 tzdir){
    u8 maxDayInAnyMonth[13] = {0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //gps.Mth 1-12 (not zero)
        if(gps.Yr%4==0){maxDayInAnyMonth[2]=29;}//adjust for leapyear
        u8 maxDayUtcMth =maxDayInAnyMonth[gps.Mth];
        u8 maxDayPrevMth=maxDayInAnyMonth[gps.Mth-1];
        if(!maxDayPrevMth){maxDayPrevMth=31;} //month before utc month

        u16 hr=(gps.Hm/100)*100;u16 m=gps.Hm-hr;  //2115 --> 2100 hr and 15 min
        if(tzdir){//adjusting forwards
            tz+=gps.Hm;
            if(tz>2400){gps.Hm=tz-2400;gps.Day++;                //spill over to next day
                  if(gps.Day>maxDayUtcMth){ gps.Day=1;gps.Mth++; //spill over to next month
                      if(gps.Mth>12){gps.Mth=1; gps.Yr++;        //spill over to next year
                      }
                  }
            }else{gps.Hm=tz;}
        }else{//adjusting backwards
            if(tz>gps.Hm){gps.Hm=(2400-(tz-hr))+m;gps.Day--;  // back to previous day
                  if(gps.Day==0){                             //back to previous month
                     gps.Mth--;gps.Day=maxDayPrevMth;
                     if(!gps.Mth){gps.Mth=12;                 //back to previous year
                        gps.Yr--;
                     }
                  }
            }else{gps.Hm-=tz;}
        }
    }
//working stand alone function adjusting UTC to local date and time
//globals(unsigned integers): gps.Mth, gps.Yr, gps.Hm (eg:2115 for 21:15)
//adjust date and time according to UTC
//tz(timezone) eg: 1100, for 11 hours, tzdir: 1 forward, 0 backwards            





    void AdjustUTCToTimeZone(u16 tz, u8 tzdir){
    u8 maxDayInAnyMonth[13] = {0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //gps.Mth 1-12 (not zero)
        if(gps.Yr%4==0){maxDayInAnyMonth[2]=29;}//adjust for leapyear
        u8 maxDayUtcMth =maxDayInAnyMonth[gps.Mth];
        u8 maxDayPrevMth=maxDayInAnyMonth[gps.Mth-1];
        if(!maxDayPrevMth){maxDayPrevMth=31;} //month before utc month

        u16 hr=(gps.Hm/100)*100;u16 m=gps.Hm-hr;  //2115 --> 2100 hr and 15 min
        if(tzdir){//adjusting forwards
            tz+=gps.Hm;
            if(tz>2400){gps.Hm=tz-2400;gps.Day++;                //spill over to next day
                  if(gps.Day>maxDayUtcMth){ gps.Day=1;gps.Mth++; //spill over to next month
                      if(gps.Mth>12){gps.Mth=1; gps.Yr++;        //spill over to next year
                      }
                  }
            }else{gps.Hm=tz;}
        }else{//adjusting backwards
            if(tz>gps.Hm){gps.Hm=(2400-(tz-hr))+m;gps.Day--;  // back to previous day
                  if(gps.Day==0){                             //back to previous month
                     gps.Mth--;gps.Day=maxDayPrevMth;
                     if(!gps.Mth){gps.Mth=12;                 //back to previous year
                        gps.Yr--;
                     }
                  }
            }else{gps.Hm-=tz;}
        }
    }
缺⑴份安定 2025-01-05 17:30:00

我认为这比那更容易; time.h 定义了三个变量:

extern int    daylight;
extern long   timezone;
extern char  *tzname[];

调用时根据 TZ 环境变量加载

 tzset();

如果您有 utc 时间,则

struct tm date;
date.tm_isdst = 0;

使用 mktime 将其转换为 time_t

time_t utc = mktime( &date );

然后将其转换为本地时间

time_t local = utc - timezone + ( daylight?3600:0 );

timezone 是距 utc 的秒数当前时区和夏令时为 1 表示正在执行夏令时,为 0 表示不执行夏令时。

一个小警告:当我为微控制器编写此代码并进行交叉编译时,time.h 用以下划线开头定义了这些变量。

查看 time.h 的手册页

I think it's easier than that; time.h defines three variables:

extern int    daylight;
extern long   timezone;
extern char  *tzname[];

which are loaded based on the TZ env variable when you call

 tzset();

if you have a utc time in

struct tm date;
date.tm_isdst = 0;

convert it to a time_t using mktime

time_t utc = mktime( &date );

then convert it to local time

time_t local = utc - timezone + ( daylight?3600:0 );

timezone is the number of seconds away from utc for the current timezone and daylight is 1 to indicate daylight savings time is in play and zero for not.

A small caution: When I coded this for a microcontroller and cross compiled, it's time.h defined those variables with initial underscores.

See the man page for time.h

轻许诺言 2025-01-05 17:30:00

我发现 OP 提供的解决方案在适用 DST 的情况下不起作用。例如,就我而言,当前时间 DST 无效,但如果我设置应转换为本地时间的初始日期带有 DST,那么它将不起作用,即今天的日期是 3/1/2018 并且 DST 未生效,但如果我将转换日期设置为,例如 8/1/2018 0:00:00(当 DST 生效时),则解决方案给出的将转换为当地时间,但不会考虑 DST。我发现将 e0 初始化为初始日期/时间字符串的日期和小时,并将其成员 tm_isdst 初始化为 -1 解决了问题。然后,我创建了以下具有补充功能的程序,您可以将其包含在代码中。日期和时间的初始格式与 MySQL 使用的相同,因为我需要它用于此类目的。

#include <stdio.h>
#include <time.h>
#include <string.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;
}

void localToUTC(char *buf, const char *localTime) {
 struct tm tp;
 strptime(localTime, "%Y-%m-%d %H:%M:%S", &tp);
 tp.tm_isdst = -1;
 time_t utc = mktime(&tp);
 struct tm res = *gmtime(&utc);
 strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &res);
}

void utcToLocal(char *buf, const char *utcTime) {
 struct tm tp;
 strptime(utcTime, "%Y-%m-%d %H:%M:%S", &tp);
 tp.tm_isdst = -1;
 time_t utc = mktime(&tp);
 struct tm e0 = { .tm_year = tp.tm_year, .tm_mday = tp.tm_mday, .tm_mon = tp.tm_mon, .tm_hour = tp.tm_hour, .tm_isdst = -1 };
 time_t pseudo = mktime(&e0);
 struct tm e1 = *gmtime(&pseudo);
 e0.tm_sec += utc - diff_tm(&e1, &e0);
 time_t local = e0.tm_sec;
 struct tm localt = *localtime(&local);
 strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &localt);
}

int main(void) {
 char mytime_1[20] = "2018-02-28 13:00:00";
 char utctime_1[20], back_1[20];
 localToUTC(utctime_1, mytime_1);
 utcToLocal(back_1, utctime_1);
 printf("My time: %s\n", mytime_1);
 printf("UTC time: %s\n", utctime_1);
 printf("Back: %s\n", back_1);

 printf("-------------------------------------------\n");

 char mytime_2[20] = "2018-07-28 17:00:00";
 char utctime_2[20], back_2[20];
 localToUTC(utctime_2, mytime_2);
 utcToLocal(back_2, utctime_2);
 printf("My time: %s\n", mytime_2);
 printf("UTC time: %s\n", utctime_2);
 printf("Back: %s\n", back_2);

 printf("-------------------------------------------\n");

 return 0;
}

I found that the solution the OP gave did not work in cases when DST applies. For example, in my case, at the current time, DST was not in effect, but if I set the initial date which should convert to local time with DST, then it would not work, i.e. today's date is 3/1/2018 and DST is not in effect, but if I set the date for conversion to, say, 8/1/2018 0:00:00 when DST is in effect, then the solution given would convert to local time, but would not take DST into account. I found that initializing e0 to the date and hour of the initial date/time string and its member tm_isdst to -1 solved the problem. I then created the following program with complementary functions which you can include in your code. The initial format of the date and time is the same that MySQL uses, because I needed it for such purposes.

#include <stdio.h>
#include <time.h>
#include <string.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;
}

void localToUTC(char *buf, const char *localTime) {
 struct tm tp;
 strptime(localTime, "%Y-%m-%d %H:%M:%S", &tp);
 tp.tm_isdst = -1;
 time_t utc = mktime(&tp);
 struct tm res = *gmtime(&utc);
 strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &res);
}

void utcToLocal(char *buf, const char *utcTime) {
 struct tm tp;
 strptime(utcTime, "%Y-%m-%d %H:%M:%S", &tp);
 tp.tm_isdst = -1;
 time_t utc = mktime(&tp);
 struct tm e0 = { .tm_year = tp.tm_year, .tm_mday = tp.tm_mday, .tm_mon = tp.tm_mon, .tm_hour = tp.tm_hour, .tm_isdst = -1 };
 time_t pseudo = mktime(&e0);
 struct tm e1 = *gmtime(&pseudo);
 e0.tm_sec += utc - diff_tm(&e1, &e0);
 time_t local = e0.tm_sec;
 struct tm localt = *localtime(&local);
 strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &localt);
}

int main(void) {
 char mytime_1[20] = "2018-02-28 13:00:00";
 char utctime_1[20], back_1[20];
 localToUTC(utctime_1, mytime_1);
 utcToLocal(back_1, utctime_1);
 printf("My time: %s\n", mytime_1);
 printf("UTC time: %s\n", utctime_1);
 printf("Back: %s\n", back_1);

 printf("-------------------------------------------\n");

 char mytime_2[20] = "2018-07-28 17:00:00";
 char utctime_2[20], back_2[20];
 localToUTC(utctime_2, mytime_2);
 utcToLocal(back_2, utctime_2);
 printf("My time: %s\n", mytime_2);
 printf("UTC time: %s\n", utctime_2);
 printf("Back: %s\n", back_2);

 printf("-------------------------------------------\n");

 return 0;
}
养猫人 2025-01-05 17:30:00

我按照@Dachschaden 的答案做了一个例子,它也显示了人类可读的输出,并且我删除了 DST 选项以表示 UTC 和本地时间之间的秒数差异。这里是:

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

#define DATE_MAX_STR_SIZE 26
#define DATE_FMT "%FT%TZ%z"

int main() {

    time_t now_time, now_time_local;
    struct tm now_tm_utc, now_tm_local;
    char str_utc[DATE_MAX_STR_SIZE];
    char str_local[DATE_MAX_STR_SIZE];

    time(&now_time);
    gmtime_r(&now_time, &now_tm_utc);
    localtime_r(&now_time, &now_tm_local);

    /* human readable */
    strftime(str_utc, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_utc);
    strftime(str_local, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_local);

    printf("\nUTC: %s", str_utc);
    printf("\nLOCAL: %s\n", str_local);

    /* seconds (epoch) */
    /* let's forget about DST for time difference calculation */
    now_tm_local.tm_isdst = 0;
    now_tm_utc.tm_isdst = 0;
    now_time_local = now_time + (mktime(&now_tm_local) - mktime(&now_tm_utc));

    printf("\nUTC in seconds: %lu", now_time);
    printf("\nLOCAL in seconds: %lu\n", now_time_local);

    return 0;
}

我的机器上的输出是:

UTC: 2016-05-05T15:39:11Z-0500
LOCAL: 2016-05-05T11:39:11Z-0400

UTC in seconds: 1462462751
LOCAL in seconds: 1462448351

请注意,在这种情况下 DST 已开启(UTC 和 LOCAL 之间存在 1 小时时区偏移差异)。

I followed the answer by @Dachschaden and I made an example which also shows human-readable output and I remove the DST option for the difference in seconds between UTC and local time. Here it is:

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

#define DATE_MAX_STR_SIZE 26
#define DATE_FMT "%FT%TZ%z"

int main() {

    time_t now_time, now_time_local;
    struct tm now_tm_utc, now_tm_local;
    char str_utc[DATE_MAX_STR_SIZE];
    char str_local[DATE_MAX_STR_SIZE];

    time(&now_time);
    gmtime_r(&now_time, &now_tm_utc);
    localtime_r(&now_time, &now_tm_local);

    /* human readable */
    strftime(str_utc, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_utc);
    strftime(str_local, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_local);

    printf("\nUTC: %s", str_utc);
    printf("\nLOCAL: %s\n", str_local);

    /* seconds (epoch) */
    /* let's forget about DST for time difference calculation */
    now_tm_local.tm_isdst = 0;
    now_tm_utc.tm_isdst = 0;
    now_time_local = now_time + (mktime(&now_tm_local) - mktime(&now_tm_utc));

    printf("\nUTC in seconds: %lu", now_time);
    printf("\nLOCAL in seconds: %lu\n", now_time_local);

    return 0;
}

Output on my machine is:

UTC: 2016-05-05T15:39:11Z-0500
LOCAL: 2016-05-05T11:39:11Z-0400

UTC in seconds: 1462462751
LOCAL in seconds: 1462448351

Note that DST is on in this case (there's a 1 hour time zone offset difference between UTC and LOCAL).

风透绣罗衣 2025-01-05 17:30:00

试试这个,测试输出:
utcEpochTime: 1487652688,localEpochTime: 1487699488,diff: 46800

$ python
>>>46800 / 60 / 60
13

差异是 13 小时,这很好,因为我的时区是 UTC+8。

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

int main(int argc, char *argv[])
{
    time_t utcEpochTime = time(0);
    time_t localEpochTime = 0;

    struct tm tm = {0};
    localtime_r(&utcEpochTime, &tm);
    tm.tm_isdst = -1;
    localEpochTime = timegm(&tm);

    printf("utcEpochTime: %d, localEpochTime: %d, diff: %d\n", (int)utcEpochTime, (int)localEpochTime, (int)(localEpochTime - utcEpochTime));
    return 0;
}

try this, test output:
utcEpochTime: 1487652688, localEpochTime: 1487699488, diff: 46800

$ python
>>>46800 / 60 / 60
13

the diff is 13 hours, which is good, as my timezone is UTC+8.

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

int main(int argc, char *argv[])
{
    time_t utcEpochTime = time(0);
    time_t localEpochTime = 0;

    struct tm tm = {0};
    localtime_r(&utcEpochTime, &tm);
    tm.tm_isdst = -1;
    localEpochTime = timegm(&tm);

    printf("utcEpochTime: %d, localEpochTime: %d, diff: %d\n", (int)utcEpochTime, (int)localEpochTime, (int)(localEpochTime - utcEpochTime));
    return 0;
}
独守阴晴ぅ圆缺 2025-01-05 17:30:00

一种简单而有效的方法:添加(或减去)您所在时区和 UTC 之间的秒数(考虑夏令时)。

举个一分钟前运行良好的示例,即 2017 年 12 月 30 日,采用美国山区标准时间(无夏令时),比 UTC 晚 7 小时:

time_t     current_time_UTC;
time_t     current_time_MST;

struct tm *current_broken_time_MST;

uint32_t seven_hours_in_seconds = 25200; // Get this any way you want

current_time_UTC = time (NULL);                                 // UTC
current_time_MST = current_time_UTC - seven_hours_in_seconds;   // MST

current_broken_time_MST = localtime (¤t_time_MST);        // MST

享受。

A simple and effective way: Add (or subtract) the number of seconds between your time zone and UTC (considering daylight saving time).

As an example that worked just fine a minute ago, on December 30, 2017, with U.S. Mountain Standard Time (no DST), which is 7 hours behind UTC:

time_t     current_time_UTC;
time_t     current_time_MST;

struct tm *current_broken_time_MST;

uint32_t seven_hours_in_seconds = 25200; // Get this any way you want

current_time_UTC = time (NULL);                                 // UTC
current_time_MST = current_time_UTC - seven_hours_in_seconds;   // MST

current_broken_time_MST = localtime (¤t_time_MST);        // MST

Enjoy.

自此以后,行同陌路 2025-01-05 17:30:00
void   CTestDlg::OnBtnTest()   
{ 
HANDLE   hFile; 
WIN32_FIND_DATA   wfd; 
SYSTEMTIME   systime; 
FILETIME   localtime; 
char   stime[32];     //
memset(&wfd,   0,   sizeof(wfd)); 

if((hFile=FindFirstFile( "F:\\VC\\MFC\\Test\\Release\\Test.exe ",        &wfd))==INVALID_HANDLE_VALUE) 
{ 
char   c[2]; 
DWORD   dw=GetLastError(); 
wsprintf(c,   "%d ",   dw); 
AfxMessageBox(c);   
return   ;//
} 
FileTimeToLocalFileTime(&wfd.ftLastWriteTime,&localtime); 
FileTimeToSystemTime(&localtime,&systime); 
sprintf(stime, "%4d-%02d-%02d   %02d:%02d:%02d ", 
      systime.wYear,systime.wMonth,systime.wDay,systime.wHour, 
      systime.wMinute,systime.wSecond); 
AfxMessageBox(stime);   
} 
void   CTestDlg::OnBtnTest()   
{ 
HANDLE   hFile; 
WIN32_FIND_DATA   wfd; 
SYSTEMTIME   systime; 
FILETIME   localtime; 
char   stime[32];     //
memset(&wfd,   0,   sizeof(wfd)); 

if((hFile=FindFirstFile( "F:\\VC\\MFC\\Test\\Release\\Test.exe ",        &wfd))==INVALID_HANDLE_VALUE) 
{ 
char   c[2]; 
DWORD   dw=GetLastError(); 
wsprintf(c,   "%d ",   dw); 
AfxMessageBox(c);   
return   ;//
} 
FileTimeToLocalFileTime(&wfd.ftLastWriteTime,&localtime); 
FileTimeToSystemTime(&localtime,&systime); 
sprintf(stime, "%4d-%02d-%02d   %02d:%02d:%02d ", 
      systime.wYear,systime.wMonth,systime.wDay,systime.wHour, 
      systime.wMinute,systime.wSecond); 
AfxMessageBox(stime);   
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文