我会完全避免std::time_t。使用来自chrono-Compatible Low-Level Date Algorithms 的days_from_civil,您可以立即计算std::chrono::system_clock::time_point 和proleptic Gregorian calendar 中的任何 日期之间的任何差异1。
除了 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++1y 中,这甚至是编译时计算!
你可以从任何其他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 不能保证有足够大的范围来执行此操作。事实证明,在我的系统上确实如此。它是一个有符号的 long long 中的微秒,它将跨越 +/- 292,000 年。如果您需要避免溢出问题,您可以在减去 0000-01-01 之前将 std::chrono::system_clock::time_point::duration 截断为更粗的单位(例如秒或天)以扩大范围。
我开始思考
这通常会导致灾难。但是在这种情况下,我决定无论如何我都应该添加到这篇文章中。这个:
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 的类型,编译器会在编译时捕获此类无意义的表达式。
1proleptic Gregorian calendar 有第 0 年。在第 0 年,它比儒略历晚 2 天。使用第 0 年也符合 ISO 8601。只要所有相关方都知道您使用的是什么日历,那么一切都很好。如果需要,非正数年和“BC 年”之间的转换是微不足道的。