将纪元时间转换为人类日期时间字符串,无需夏令时转换
Posted
技术标签:
【中文标题】将纪元时间转换为人类日期时间字符串,无需夏令时转换【英文标题】:Convert epoch time to human date time string without daylight saving conversion 【发布时间】:2015-03-06 19:12:50 【问题描述】:我是 C 的新手,我目前正在做一个项目,我需要将纪元时间转换为人类日期和时间字符串。
我的 UTC 时间戳为 1411210794
,用于 20th September 2014 10:59:54
。
然后我在 UTC 时间戳上运行以下函数以转换为人类可读的时间戳:
void UTCoTimeDate(char** date, char** time, long UTC, BOOL includeSeconds)
time_t epch = UTC;
struct tm *timeptr = gmtime(&epch);
asprintf(date, "%d-%.2d-%.2d", 1900 + timeptr->tm_year, timeptr->tm_mon+1, timeptr->tm_mday);
if (includeSeconds)
asprintf(time, "%.2d:%.2d:%.2d", timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
else
asprintf(time, "%.2d:%.2d", timeptr->tm_hour, timeptr->tm_min);
当我通过 gdb 运行它并查看 tm *timeptr
内容时,小时已变为 11
而不是停留在 10。以下是 tm 结构的完整内容
tm_sec = 54, tm_min = 59, tm_hour = 11, tm_mday = 20, tm_mon = 8, tm_year = 114, tm_wday = 6, tm_yday = 262, tm_isdst = 1, tm_gmtoff = 3600,
tm_zone = 0x806c488 "BST"
我原以为人类日期会在 10:59:54 而不是 11:59:54 回来。
【问题讨论】:
如果您修改代码以在调用gmtime()
之前打印UTC
和之后打印timeptr->tm_hour
并且UTC
的值为1411210794 并且小时值为11,那么您的gmtime()
被打破。你实际上在哪个时区? (是 UTC±1,还是与 UTC 有更大的偏移?)有人添加了#define gmtime(x) localtime(x)
是否有任何风险?如果添加#undef gmtime
,结果会改变吗?
【参考方案1】:
这是一个演示程序 (ttt.c
):
#include <stdio.h>
#include <time.h>
static void dump_tm(const char *tag, const struct tm *tm)
printf("%s: tm_sec = %.2d, tm_min = %.2d, tm_hour = %.2d, tm_mday = %.2d,"
" tm_mon = %.2d, tm_year = %.3d, tm_wday = %d, tm_yday = %.3d,"
" tm_isdst = %d, tm_gmtoff = %.5ld, tm_zone = \"%s\"\n",
tag, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon,
tm->tm_year, tm->tm_wday, tm->tm_yday, tm->tm_isdst, tm->tm_gmtoff,
tm->tm_zone);
int main(void)
time_t t0 = 1411210794;
struct tm *ut = gmtime(&t0);
struct tm *lt = localtime(&t0);
dump_tm("UTC", ut);
dump_tm("Local", lt);
return 0;
这是我 Mac 上的输出(Mac OS X 10.10.1、GCC 4.9.1 — 为 SO 添加了额外的换行符):
$ ./ttt
UTC: tm_sec = 54, tm_min = 59, tm_hour = 10, tm_mday = 20, tm_mon = 08, tm_year = 114,
tm_wday = 6, tm_yday = 262, tm_isdst = 0, tm_gmtoff = 00000, tm_zone = "UTC"
Local: tm_sec = 54, tm_min = 59, tm_hour = 03, tm_mday = 20, tm_mon = 08, tm_year = 114,
tm_wday = 6, tm_yday = 262, tm_isdst = 1, tm_gmtoff = -25200, tm_zone = "PDT"
$ TZ="Europe/London" ./ttt
UTC: tm_sec = 54, tm_min = 59, tm_hour = 10, tm_mday = 20, tm_mon = 08, tm_year = 114,
tm_wday = 6, tm_yday = 262, tm_isdst = 0, tm_gmtoff = 00000, tm_zone = "UTC"
Local: tm_sec = 54, tm_min = 59, tm_hour = 11, tm_mday = 20, tm_mon = 08, tm_year = 114,
tm_wday = 6, tm_yday = 262, tm_isdst = 1, tm_gmtoff = 03600, tm_zone = "BST"
$
机器的默认时区设置实际上是 America/Los_Angeles(尽管 TZ 环境变量在第一次调用中正式取消设置)。可以看到,UTC 值是一致的,但是本地时间值有本地时区缩写。
在没有相反信息的情况下,我不得不得出结论,尽管您有其他想法,但您实际上是在调用 localtime()
而不是 gmtime()
。您可以尝试通过在函数调用前添加#undef gmtime
来确保调用gmtime()
函数。如果gmtime()
函数仍然产生错误的时间信息,那么您机器上的gmtime()
已损坏。但是,这似乎不太可能。
【讨论】:
感谢您的工作。我真的不明白为什么。当我搜索整个项目时,我唯一找到的是我正在调用的 gmtime,以及#undef 的 gmtime,我没有找到任何其他可能使 gmtime 以不同方式工作的东西 无法访问您的所有代码,我也不能说。我能做的最好的事情是 (a) 证明gmtime()
应该完成这项工作,并且 (b) 您显示的分解时间(可能)不是 gmtime()
的输出,而是 localtime()
的输出,不知何故。 '很遗憾你使用asprintf()
来进行strftime()
所做的格式化。我很想使用char buffer[32];
和strftime(buffer, sizeof(buffer), "%Y-%m-%d", timeptr);
,然后*date = strdup(buffer);
将输出复制到函数参数中。不过,这不一定像 asprintf()
那样快。
好的,谢谢您的帮助。至少它的工作原理,我已经用这个把头撞在墙上有一段时间了。我会做更多的挖掘,看看我能找到什么以上是关于将纪元时间转换为人类日期时间字符串,无需夏令时转换的主要内容,如果未能解决你的问题,请参考以下文章
在 Mac OSX 上将 unix 纪元时间转换为人类可读的日期 - BSD