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