C++ 将时间字符串从纪元转换为秒
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4137748/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ Converting a time string to seconds from the epoch
提问by Martin York
I have a string with the following format:
我有一个具有以下格式的字符串:
2010-11-04T23:23:01Z
2010-11-04T23:23:01Z
The Z indicates that the time is UTC.
I would rather store this as a epoch time to make comparison easy.
Z 表示时间是 UTC。
我宁愿将其存储为纪元时间,以便于比较。
What is the recomended method for doing this?
这样做的推荐方法是什么?
Currently (after a quck search) the simplist algorithm is:
目前(经过快速搜索)最简单的算法是:
1: <Convert string to struct_tm: by manually parsing string>
2: Use mktime() to convert struct_tm to epoch time.
// Problem here is that mktime uses local time not UTC time.
采纳答案by Martin York
Using C++11 functionality we can now use streams to parse times:
使用 C++11 功能,我们现在可以使用流来解析时间:
The iomanip std::get_time
will convert a string based on a set of format parameters and convert them into a struct tz
object.
iomanipstd::get_time
将根据一组格式参数转换字符串并将它们转换为struct tz
对象。
You can then use std::mktime()
to convert this into an epoch value.
然后,您可以使用std::mktime()
将其转换为纪元值。
#include <iostream>
#include <sstream>
#include <locale>
#include <iomanip>
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";
}
return 0;
}
回答by Kirill V. Lyadvinsky
回答by Justin Ethier
回答by Steve Townsend
It's not an exact dup but you will find @Cubbi's answer from hereuseful, I wager. This specifically assumes UTC input.
这不是一个确切的重复,但你会发现@Cubbi 在这里的回答很有用,我敢打赌。这特别假设 UTC 输入。
Boost also support direct conversion from ISO 8601 via boost::posix_time::from_iso_string
which calls boost::date_time::parse_iso_time
, here again you would just strip the trailing 'Z' and treat the TZ as implicit UTC.
Boost 还支持从 ISO 8601 直接转换,通过boost::posix_time::from_iso_string
which 调用boost::date_time::parse_iso_time
,在这里您只需去掉尾随的“Z”并将 TZ 视为隐式 UTC。
#include <iostream>
#include <boost/date_time.hpp>
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<formats_n; ++i)
{
std::istringstream is(s);
is.imbue(formats[i]);
is >> 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");
}
回答by Mark Lakata
Problem here is that mktime uses local time not UTC time.
这里的问题是 mktime 使用本地时间而不是 UTC 时间。
Linux provides timegm
which is what you want (i.e. mktime for UTC time).
Linux 提供timegm
了您想要的(即 UTC 时间的 mktime)。
Here is my solution, which I forced to only accept "Zulu" (Z timezone). Note
that strptime
doesn't actually seem to parse the time zone correctly, even
though glib seems to have some support for that. That is why I just throw an
exception if the string doesn't end in 'Z'.
这是我的解决方案,我被迫只接受“祖鲁语”(Z 时区)。请注意,strptime
尽管 glib 似乎对此有一些支持,但它实际上似乎并没有正确解析时区。这就是为什么我只是在字符串不以“Z”结尾时抛出异常的原因。
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;
}
回答by Howard Hinnant
New answer to an old question. Rationale for new answer: In case you want to use <chrono>
types to solve a problem like this.
老问题的新答案。新答案的基本原理:如果您想使用<chrono>
类型来解决这样的问题。
In addition to C++11/C++14, you'll need this free, open source date/time library:
除了 C++11/C++14,您还需要这个免费的开源日期/时间库:
#include "tz.h"
#include <iostream>
#include <sstream>
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";
}
This program outputs:
该程序输出:
seconds from epoch is 1288912981s
If the parse fails in any way, an exception will be thrown. If you would rather not throw exceptions, don't is.exceptions(std::ios::failbit);
, but instead check for is.fail()
.
如果解析以任何方式失败,将抛出异常。如果您不想抛出异常,请不要is.exceptions(std::ios::failbit);
,而是检查is.fail()
.
回答by Yippie-Ki-Yay
You could utilize the boost::date_time
and write a small manual parser (probably regexp-based)for your strings.
您可以使用boost::date_time
并为您的字符串编写一个小型手动解析器(可能基于正则表达式)。
回答by casablanca
Problem here is that mktime uses local time not UTC time.
这里的问题是 mktime 使用本地时间而不是 UTC 时间。
How about just computing the time difference between UTC and local time, then adding it to the value returned by mktime
?
如何只计算 UTC 和本地时间之间的时差,然后将其添加到 返回的值中mktime
?
time_t local = time(NULL),
utc = mktime(gmtime(&local));
int diff = utc - local;
回答by Dirk Eddelbuettel
What's wrong with strptime()
?
怎么了strptime()
?
And on Linux, you even get the 'seconds east of UTC' field relieving you from any need to parse:
在 Linux 上,您甚至可以获得“UTC 以东的秒数”字段,让您无需解析:
#define _XOPEN_SOURCE
#include <iostream>
#include <time.h>
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;
}
which for me yields
这对我来说产生
/tmp$ g++ -o my my.cpp
/tmp$ ./my
2010-11-04T23:23:01Z -> 04 Nov 2010 23:23:01
Seconds east of UTC 140085769590024
回答by caf
X/Open provides a global timezone
variable which indicates the number of seconds that local time is behind UTC. You can use this to adjust the output of mktime()
:
X/Open 提供了一个全局timezone
变量,用于指示本地时间落后于 UTC 的秒数。您可以使用它来调整输出mktime()
:
#define _XOPEN_SOURCE
#include <stdio.h>
#include <time.h>
/* 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;
}