转换为 unix 时间戳不正确
Posted
技术标签:
【中文标题】转换为 unix 时间戳不正确【英文标题】:Conversion to unix timestamp incorrect 【发布时间】:2011-06-08 14:46:33 【问题描述】:我有一个我写的函数(如果有好的标准替代品,请告诉我...)
time_t get_unix_time(string time_str)
time_t loctime;
time(&loctime);
struct tm *given_time;
time_str = time_str.substr(0, time_str.find_first_of('.'));
replace(time_str.begin(), time_str.end(), ':', ',');
replace(time_str.begin(), time_str.end(), '-', ',');
replace(time_str.begin(), time_str.end(), '/', ',');
replace(time_str.begin(), time_str.end(), ' ', ',');
given_time = localtime(&loctime);
vector<string> trecord = split_string(time_str, ',');
given_time->tm_year = atoi(trecord.at(0).c_str()) - 1900;
given_time->tm_mon = atoi(trecord.at(1).c_str()) - 1;
given_time->tm_mday = atoi(trecord.at(2).c_str());
given_time->tm_hour = atoi(trecord.at(3).c_str());
given_time->tm_min = atoi(trecord.at(4).c_str());
given_time->tm_sec = atoi(trecord.at(5).c_str());
return mktime(given_time);
函数的输入 (time_str) 格式为 1970-01-01 00:00:00.0。 split_string() 函数将字符串time_str
拆分成一个向量,其中包含:
1970, 01, 01, 00, 00, 00
用于填充given_time结构。
我写了一个函数来测试它,并准确地传递了那个输入(纪元的开始)。但是,它返回给我的时间是 21600,即 1970-01-01 06:00:00,或 UTC+6。预期的输出是 0(纪元的开始)。
注意:我在美国中部时区,即 UTC - 6。在 1970 年 1 月 1 日午夜,@UTC 时间将是 1970 年 1 月 1 日 06:00:00。
我的函数中是否有任何内容使其特定于我的时区?我在这个函数中做错了什么,或者我可以做一些不同的事情来使它独立于区域,或者至少总是 UTC。
【问题讨论】:
另外,我不能使用 boost 库或任何其他附加库。我只能使用标准的 C++ 东西。 C++ 库中的时区处理非常晦涩难懂。如果您启动程序时将环境变量TZ
设置为值 UTC
,则此代码 sn-p 将起作用。
mktime 将给定时间解释为本地时间。所以如果你给 1970-01-01 00:00:00 它将是你的本地时间,所以 mktime 将返回 UTC-0 时间,即 1970-01-01 06:00:00
【参考方案1】:
如果您使用glibc,则可以使用timegm
函数,这是mktime
的一个版本,它总是将时间解释为在GMT
时区中。不幸的是,该函数的文档基本上表明它不能使用标准库调用来实现。所以除非你拥有它,否则你有点不走运。
【讨论】:
谢谢!那工作得很好。我正在查看 gmtime 的联机帮助页,但在任何地方都没有看到。【参考方案2】:mktime
在本地时区中花费时间。所以,如果你传递它 1970-01-01 00:00:00 local time,它会返回 1970-01-01 06:00:00 UTC,因为它应该.
如果您使用的是 glibc,也可以致电 timegm。如果您不使用 glibc,则在调用 mktime 时,您会暂时将本地时间更改为 UTC,方法是弄乱 TZ 环境变量,如 timegm 手册页中所述:
time_t my_timegm (struct tm *tm)
time_t ret;
char *tz;
tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
此外,您无需调用localtime
,您可能应该设置given_time->tm_isdst
,以避免可能出现的夏令时问题。
【讨论】:
谢谢。我检查了 timegm,它做了我想要的,但我一定会检查并设置 dst 问题。【参考方案3】:也许您应该使用gmtime
而不是time
,以摆脱时区问题。
编辑: 我真的不明白为什么你用当前时间填充结构,然后覆盖它的所有组件。为什么不只是:
time_t get_unix_time(const string& time_str)
vector<string> trecord = split_string(time_str, ',');
tm given_time;
given_time.tm_year = atoi(trecord.at(0).c_str()) - 1900;
given_time.tm_mon = atoi(trecord.at(1).c_str()) - 1;
given_time.tm_mday = atoi(trecord.at(2).c_str());
given_time.tm_hour = atoi(trecord.at(3).c_str());
given_time.tm_min = atoi(trecord.at(4).c_str());
given_time.tm_sec = atoi(trecord.at(5).c_str());
return mktime(&given_time);
另一个修改:
呃,mktime
也考虑当地时间。除了将您的时区语言环境设置为 UTC 之外,我不太确定如何解决这个问题。
【讨论】:
- 替换当前时间并替换为我需要的时间并不是真正的问题。我试过你的功能,它仍然给我 21600 而不是 0。21600 是 = UTC + 6。 @Sagar:“呃,mktime 也考虑本地时间。除了将时区语言环境设置为 UTC 之外,我不确定如何解决这个问题。”是你做的吗?还是你只是把代码放进去,什么都不做,然后寄希望于最好的结果? 不幸的是,虽然我可以在我的开发机器上更改时区,但在客户端机器上是不可能的。 @Sagar:不是这样。您可以控制影响流程的环境变量。我相信命令是setenv
。【参考方案4】:
避免使用这些尴尬的函数,自己算算。 POSIX 指定 time_t
是自“纪元”(格林威治标准时间 1970-01-01 00:00:00)以来以秒为单位的算术类型,没有任何闰秒废话(所有天都是 86400 日历秒 em>,与 SI 秒 相差很小),所以除了一点闰年逻辑之外,计算非常简单。
像这样的日历计算是标准的入门编程练习,所以我相信您可以计算出来或在网上找到解决方案。
顺便说一句,也许 ISO C 和 POSIX 省略了这样一个函数的原因是,与涉及时区的转换可以任意复杂并且只有主机的库可以在不同的应用程序中可靠和一致地执行不同,GMT 转换是纯算术没有外部参数。
【讨论】:
如果找不到解决方案,我考虑过这样做,但想如果已经有这样的功能,为什么不尝试寻找..它为我节省了一些额外的测试工作。 顺便说一下,这里是另一个方向的公式参考:pubs.opengroup.org/onlinepubs/9699919799/basedefs/…【参考方案5】:当您调用 mktime 时,它会将参数解释为本地时间。 您还使用了诸如“本地时间”之类的似乎没用的功能,我认为您可以放弃它们。
【讨论】:
【参考方案6】:您可以围绕strptime
编写一个包装器来进行解析。
struct tm given_time;
strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &given_time);
return mktime(&given_time);
@Josh Kelley 的回答彻底解释了时区问题。
【讨论】:
以上是关于转换为 unix 时间戳不正确的主要内容,如果未能解决你的问题,请参考以下文章
从 Avro 将 unix 时间戳(以秒为单位)导入 Bigquery 中的正确时间戳