是否可以手动将纪元日期/时间设置为0000年1月1日,所以我可能会使用std :: chrono :: time_point :: time_since_epoch来计算给定日期与1月1日0000之间的差异?
我尝试了以下内容:
#include <iostream>
#include <chrono>
#include <ctime>
int main(int argc, char*argv[])
{
std::tm epochStart = {};
epochStart.tm_sec = 0;
epochStart.tm_min = 0;
epochStart.tm_hour = 0;
epochStart.tm_mday = 0;
epochStart.tm_mon = 0;
epochStart.tm_year = -1900;
epochStart.tm_wday = 0;
epochStart.tm_yday = 0;
epochStart.tm_isdst = -1;
std::time_t base = std::mktime(&epochStart);
std::chrono::system_clock::time_point baseTp=
std::chrono::system_clock::from_time_t(base);
std::time_t btp = std::chrono::system_clock::to_time_t(baseTp);
std::cout << "time: " << std::ctime(&btp);
}
但是这给了我
time: Thu Jan 1 00:59:59 1970
答案 0 :(得分:9)
我会完全避免std::time_t
。使用chrono-Compatible Low-Level Date Algorithms中的days_from_civil
,您可以立即计算proleptic Gregorian calendar 1 std::chrono::system_clock::time_point和任何日期之间的任何差异>
除days_from_civil
需要一年/每月/每天三倍并将其转换为1970-01-01之前/之后的天数(与时间兼容的纪元),创建它也很方便自定义chrono::duration
代表24小时:
typedef std::chrono::duration
<
int,
std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
> days;
现在您只需创建所需的纪元:
constexpr days epoch = days(days_from_civil(0, 1, 1)); // 0000-01-01
在C ++中,这甚至是编译时的计算!
您可以从任何其他std::chrono::duration
中减去此std::chrono::duration
:
auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;
delta
现在是std::chrono::duration
,表示从现在开始到0000-01-01之间的时间量。然后,您可以根据需要打印出来,或以其他方式操纵它。例如,这是一个完整的工作演示:
#include "../date_performance/date_algorithms"
#include <iostream>
#include <chrono>
typedef std::chrono::duration
<
int,
std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
> days;
int
main()
{
constexpr days epoch = days(days_from_civil(0, 1, 1));
auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;
days d = std::chrono::duration_cast<days>(delta);
std::cout << "It has been " << d.count() << " days, ";
delta -= d;
auto h = std::chrono::duration_cast<std::chrono::hours>(delta);
std::cout << h.count() << " hours, ";
delta -= h;
auto m = std::chrono::duration_cast<std::chrono::minutes>(delta);
std::cout << m.count() << " minutes, ";
delta -= m;
auto s = std::chrono::duration_cast<std::chrono::seconds>(delta);
std::cout << s.count() << " seconds ";
std::cout << " since 0000-01-01\n";
}
对我来说输出:
It has been 735602 days, 19 hours, 14 minutes, 32 seconds since 0000-01-01
关于溢出的警告:
std::chrono::system_clock::time_point::duration
不能保证范围足够大以完成此操作。事实证明,在我的系统上它确实如此。签名长的长度为微秒,将超过+/- 292,000年。如果您需要避免溢出问题,可以截断std::chrono::system_clock::time_point::duration
到courser单位(例如秒或天)以在减去0000-01-01之前扩展范围。
我开始思考
这通常会导致灾难。但是在这种情况下,无论如何我决定加入这篇文章。这样:
constexpr days epoch = days(days_from_civil(0, 1, 1));
的类型为days
,即duration
。但它确实不是duration
。这是一个时间点。这是约会。它是一个具有粗精度的time_point
。通过引入一个新的typedef,这篇文章中的代码可以稍微清理一下:
typedef std::chrono::time_point<std::chrono::system_clock, days> date_point;
现在而不是写作:
constexpr days epoch = days(days_from_civil(0, 1, 1));
可以写:
constexpr date_point epoch{days(days_from_civil(0, 1, 1))};
但更重要的是,而不是:
auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;
我们现在可以写:
auto delta = std::chrono::system_clock::now() - epoch;
此delta
仍具有与之前完全相同的类型和值,并且演示中的其他所有内容仍然与之前完全一样。
这既是一个微小的变化,也是一个很大的变化。通过将epoch
视为time_point
而不是duration
,time_point
和duration
的代数可以为我们工作,这两者都可以简化和类型检查我们的表达式,以帮助我们编写更清晰的代码,减少错误。
例如,可以将两个duration
加在一起。但它根本没有任何意义:
epoch + epoch
对time_point
的类型使用duration
而不是epoch
,编译器会在编译时捕获这些非敏感表达式。
1 proleptic Gregorian calendar有一年0.在第0年,它比Julian日历落后2天。使用0年也符合ISO 8601.只要所有相关方知道您使用的是哪个日历,那么一切都很好。如果需要,非正年和“BC年”之间的转换是微不足道的。
答案 1 :(得分:3)
有可能,您提供的代码(减去一个小修正,tm_mday
以1
开头)产生:
Sat Jan 1 00:00:00 0
真正的问题是:你是32位还是64位?对于32位系统,time_t
也只有32位,您可以限制在1970 +/- 68年。
在64位系统上,限制由std::mktime
和std::strftime
给出,在我自己的代码中,我对这些字符串和相应的值进行单元测试:
"-2147481748-01-01 00:00:00" maps to -67768040609740800
"2147483647-12-31 23:59:59" maps to 67767976233532799
我可能还应该提到,有些系统上面的功能不起作用,因为底层的操作系统功能有问题。记录:我在Linux上。
答案 2 :(得分:2)
没有。 mktime
和朋友都是基于UNIX时间,从1970年1月1日开始。
实际上没有0000年1月0日这样的事情,所以你最好找到另一种解决你的实际问题的方法。