我想知道自纪元以来的几秒钟.值得注意的是,我不喜欢转换机器的位置,时区字符串应该足够了.
我有这个测试程序,pt.cc:
#include <assert.h>
#include <errno.h>
#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE
#endif
#include <time.h>
using namespace std; // To be brief, don't do this in real life.
int main(int argc, char* argv[]) {
(void)argc; (void)argv; // Skip compile warning.
// I expect both of these to transform to 1440671500.
cout << "1440671500 expected" << endl;
const char utc_example[] = "2015-08-27T11:31:40+0100";
struct tm tm;
memset(&tm, 0, sizeof(struct tm));
char* end = strptime(utc_example, "%Y-%m-%dT%H:%M:%S%z", &tm);
assert(end);
assert(*end == '\0');
time_t seconds_since_epoch = mktime(&tm);
cout << "utc example: " << seconds_since_epoch << " or maybe "
<< seconds_since_epoch - tm.tm_gmtoff + (tm.tm_isdst ? 3600 : 0) << endl;
const char tz_example[] = "2015-08-27T10:31:40Z";
memset(&tm, 0, sizeof(struct tm));
end = strptime(tz_example, "%Y-%m-%dT%H:%M:%S%nZ", &tm);
assert(end);
assert(*end == '\0');
seconds_since_epoch = mktime(&tm);
cout << " tz example: " << seconds_since_epoch << " or maybe "
<< seconds_since_epoch - tm.tm_gmtoff + (tm.tm_isdst ? 3600 : 0) << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是输出:
jeff@birdsong:tmp $ clang++ -ggdb3 -Wall -Wextra -std=c++14 pt.cc -o pt
jeff@birdsong:tmp $ ./pt
1440671500 expected
utc example: 1440671500 or maybe 1440667900
tz example: 1440667900 or maybe 1440664300
jeff@birdsong:tmp $ TZ=America/New_York ./pt
1440671500 expected
utc example: 1440693100 or maybe 1440711100
tz example: 1440689500 or maybe 1440707500
jeff@birdsong:tmp $ TZ=Europe/London ./pt
1440671500 expected
utc example: 1440675100 or maybe 1440675100
tz example: 1440671500 or maybe 1440671500
jeff@birdsong:tmp $
Run Code Online (Sandbox Code Playgroud)
请注意返回值的mktime()变化取决于环境时区.手册页条目mktime()建议将故障时间解释为当地时间.所以我尝试减去GMT偏移并补偿时区,以防它忽略那些值("或者"值).
有关如何正确执行此操作的任何提示?(重要的是,我只需要在linux上工作.)
How*_*ant 10
这个答案使用这个日期/时间库:
http://howardhinnant.github.io/date/date.html
这里采用的方法是完全绕过C日期/时间API.就个人而言,我发现C方法有点混乱,麻烦,有点危险.
话虽这么说,我的日期/时间库中的解析和格式化设施是不存在的.我预计这些设施可能会成为一个单独的图书馆,将来会在我的图书馆之上分层.
与此同时,为这个特定问题推出自己的解析并不困难.方法如下:
#include "chrono_io.h"
#include "date.h"
#include <iostream>
#include <string>
#include <sstream>
using second_point = std::chrono::time_point<std::chrono::system_clock,
std::chrono::seconds>;
std::chrono::minutes
parse_offset(std::istream& in)
{
using namespace std::chrono;
char c;
in >> c;
minutes result = 10*hours{c - '0'};
in >> c;
result += hours{c - '0'};
in >> c;
result += 10*minutes{c - '0'};
in >> c;
result += minutes{c - '0'};
return result;
}
second_point
parse(const std::string& str)
{
std::istringstream in(str);
in.exceptions(std::ios::failbit | std::ios::badbit);
int yi, mi, di;
char dash;
// check dash if you're picky
in >> yi >> dash >> mi >> dash >> di;
using namespace date;
auto ymd = year{yi}/mi/di;
// check ymd.ok() if you're picky
char T;
in >> T;
// check T if you're picky
int hi, si;
char colon;
in >> hi >> colon >> mi >> colon >> si;
// check colon if you're picky
using namespace std::chrono;
auto h = hours{hi};
auto m = minutes{mi};
auto s = seconds{si};
second_point result = sys_days{ymd} + h + m + s;
char f;
in >> f;
if (f == '+')
result -= parse_offset(in);
else if (f == '-')
result += parse_offset(in);
else
;// check f == 'Z' if you're picky
return result;
}
int
main()
{
using namespace date;
std::cout << parse("2015-08-27T11:31:40+0100").time_since_epoch() << '\n';
std::cout << parse("2015-08-27T10:31:40Z").time_since_epoch() << '\n';
}
Run Code Online (Sandbox Code Playgroud)
要完全前期,该解决方案使主要用途的std::istringstream,std::chrono以及实际上只是它的非常小的一部分是我最新的图书馆.
我做了几个设计选择,您可以选择不选择(解析时有很多选项).例如,如果存在任何解析错误,我选择抛出异常,并且我选择不挑选检查分隔符等(-并且:主要是出于简洁的原因).
代码相对不言自明.正如您在问题中所述,本地时区不是(也不应该是)解决方案的一部分.计时库用于在小时,分钟和秒之间管理算术.我的日期库用于处理年/月/日转换chrono::time_point为精度为days.
处理完所有这些算术后,您可以专注于解析整数和字符.可以直接向此示例添加更多检查,并为错误执行任何操作.
此示例输出:
1440671500s
1440671500s
Run Code Online (Sandbox Code Playgroud)
更新
我已经添加了解析功能,现在可以更简单地编写"date.h"上面的parse函数:
date::sys_seconds
parse(const std::string& str)
{
std::istringstream in(str);
date::sys_seconds tp;
in >> date::parse("%FT%TZ", tp);
if (in.fail())
{
in.clear();
in.str(str);
in >> date::parse("%FT%T%z", tp);
}
return tp;
}
int
main()
{
using namespace date;
std::cout << parse("2015-08-27T11:31:40+0100").time_since_epoch() << '\n';
std::cout << parse("2015-08-27T10:31:40Z").time_since_epoch() << '\n';
}
Run Code Online (Sandbox Code Playgroud)
并返回相同的结果.
这是一个使用 Google 的https://github.com/google/cctz执行您想要的操作的答案
#include <chrono>
#include <iostream>
#include <string>
#include "src/cctz.h"
using namespace std;
int main(int argc, char* argv[]) {
const char kFmt[] = "%Y-%m-%dT%H:%M:%S%Ez";
// I expect both of these to transform to 1440671500.
const char utc_example[] = "2015-08-27T11:31:40+0100";
const char tz_example[] = "2015-08-27T10:31:40Z";
cout << "1440671500 expected" << endl;
// Required by cctz::Parse(). Only used if the formatted
// time does not include offset info.
const auto utc = cctz::UTCTimeZone();
std::chrono::system_clock::time_point tp;
if (!Parse(kFmt, utc_example, utc, &tp)) return -1;
cout << "utc example: " << std::chrono::system_clock::to_time_t(tp) << "\n";
if (!Parse(kFmt, tz_example, utc, &tp)) return -1;
cout << " tz example: " << std::chrono::system_clock::to_time_t(tp) << "\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
1440671500 expected
utc example: 1440671500
tz example: 1440671500
Run Code Online (Sandbox Code Playgroud)
请注意,涉及添加/减去 time_t 等偏移量的其他答案正在使用一种称为“纪元移位”的技术,但它实际上不起作用。我在 CppCon 的 12:30 演讲中解释了原因: https: //youtu.be/2rnIHsqABfM?t=12m30s