C ++将时间字符串转换为纪元的秒数
我有一个格式如下的字符串:
2010-11-04T23:23:01Z
Z表示时间是UTC。
我宁愿把它存储为一个时间点,以便比较容易。
这样做的推荐方法是什么?
目前(在quck搜索之后),简化算法是:
1: 2: Use mktime() to convert struct_tm to epoch time. // Problem here is that mktime uses local time not UTC time.
这是ISO8601格式。 您可以使用strptime
函数使用%FT%T%z
参数strptime
进行解析。 它不是C ++标准的一部分,尽管您可以使用它的开源实现(例如,这)。
使用C ++ 11function,我们现在可以使用流来解析时间:
iomanip std::get_time
将根据一组格式参数转换字符串,并将它们转换为struct tz
对象。
然后,您可以使用std::mktime()
将其转换为纪元值。
#include #include #include #include int main() { std::tm t = {}; std::istringstream ss("2010-11-04T23:23:01Z"); if (ss >> std::get_time(&t, "%Y-%m-%dT%H:%M:%S")) { std::cout << std::put_time(&t, "%c") << "\n" << std::mktime(&t) << "\n"; } else { std::cout << "Parse failed\n"; } }
您可以使用strptime等函数将字符串转换为struct tm
,而不是手动解析它。
这不是一个确切的重复,但你会发现@ Cubbi的回答从这里开始有用,我打赌。 这特别假设UTC输入。
Boost还支持从ISO 8601通过boost::posix_time::from_iso_string
直接转换,它调用boost::date_time::parse_iso_time
,这里再次你只需删除尾随’Z’并将TZ视为隐式UTC。
#include #include namespace bt = boost::posix_time; const std::locale formats[] = { std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")), std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")), std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")), std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))}; const size_t formats_n = sizeof(formats)/sizeof(formats[0]); std::time_t pt_to_time_t(const bt::ptime& pt) { bt::ptime timet_start(boost::gregorian::date(1970,1,1)); bt::time_duration diff = pt - timet_start; return diff.ticks()/bt::time_duration::rep_type::ticks_per_second; } void seconds_from_epoch(const std::string& s) { bt::ptime pt; for(size_t i=0; i> pt; if(pt != bt::ptime()) break; } std::cout << " ptime is " << pt << '\n'; std::cout << " seconds from epoch are " << pt_to_time_t(pt) << '\n'; } int main() { seconds_from_epoch("2004-03-21 12:45:33"); seconds_from_epoch("2004/03/21 12:45:33"); seconds_from_epoch("23.09.2004 04:12:21"); seconds_from_epoch("2003-02-11"); }
这里的问题是mktime使用本地时间而不是UTC时间。
Linux提供了你想要的timegm
(即UTC时间的mktime)。
这是我的解决方案,我不得不接受“Zulu”(Z时区)。 请注意, strptime
实际上似乎并没有正确解析时区,即使glib似乎对此有一些支持。 这就是为什么我只是在字符串不以’Z’结尾时抛出exception。
static double EpochTime(const std::string& iso8601Time) { struct tm t; if (iso8601Time.back() != 'Z') throw PBException("Non Zulu 8601 timezone not supported"); char* ptr = strptime(iso8601Time.c_str(), "%FT%T", &t); if( ptr == nullptr) { throw PBException("strptime failed, can't parse " + iso8601Time); } double t2 = timegm(&t); // UTC if (*ptr) { double fraction = atof(ptr); t2 += fraction; } return t2; }
对旧问题的新答案。 新答案的基本原理:如果您想使用
类型来解决这样的问题。
除了C ++ 11 / C ++ 14之外,您还需要这个免费的开源日期/时间库 :
#include "tz.h" #include #include int main() { std::istringstream is("2010-11-04T23:23:01Z"); is.exceptions(std::ios::failbit); date::sys_seconds tp; date::parse(is, "%FT%TZ", tp); std::cout << "seconds from epoch is " << tp.time_since_epoch().count() << "s\n"; }
该计划输出:
seconds from epoch is 1288912981s
如果解析以任何方式失败,将抛出exception。 如果您不想抛出exception,请不要使用is.exceptions(std::ios::failbit);
,而是检查is.fail()
。
你可以使用boost::date_time
并为你的字符串编写一个小的手动解析器(可能是基于regexp的) 。
这里的问题是mktime使用本地时间而不是UTC时间。
如何计算UTC和本地时间之间的时差,然后将其添加到mktime
返回的值?
time_t local = time(NULL), utc = mktime(gmtime(&local)); int diff = utc - local;
strptime()
什么问题?
在Linux上,您甚至可以获得“UTC以东几秒钟”字段,从而使您无需解析:
#define _XOPEN_SOURCE #include #include int main(void) { const char *timestr = "2010-11-04T23:23:01Z"; struct tm t; strptime(timestr, "%Y-%m-%dT%H:%M:%SZ", &t); char buf[128]; strftime(buf, sizeof(buf), "%d %b %Y %H:%M:%S", &t); std::cout << timestr << " -> " << buf << std::endl; std::cout << "Seconds east of UTC " << t.tm_gmtoff << std::endl; }
这对我来说是收益
/tmp$ g++ -o my my.cpp /tmp$ ./my 2010-11-04T23:23:01Z -> 04 Nov 2010 23:23:01 Seconds east of UTC 140085769590024
X / Open提供全局timezone
变量,表示本地时间落后于UTC的秒数。 您可以使用它来调整mktime()
的输出:
#define _XOPEN_SOURCE #include #include /* 2010-11-04T23:23:01Z */ time_t zulu_time(const char *time_str) { struct tm tm = { 0 }; if (!strptime(time_str, "%Y-%m-%dT%H:%M:%SZ", &tm)) return (time_t)-1; return mktime(&tm) - timezone; }