ase*_*seq 9 c time posix utc datetime-conversion
这是一个简单的问题,但解决方案似乎远非简单.我想知道如何从UTC转换为本地时间.我正在寻找一个标准的C解决方案,或多或少保证可以在任何位置的任何计算机上工作.
我仔细阅读了以下链接,但我找不到解决方案:
我尝试了很多变体,例如(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(<);
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编写了一个时间和日期,然后将其转换为当地时间和日期.
如果您可以假设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对应的已知时期.也许你可以找到一个办法解决,如果你真的使用这两个调用需要...例如gmtime与localtime在time_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.
啊......我可能只是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.