std::chrono:将时钟的纪元设置为 1/1/0000
Posted
技术标签:
【中文标题】std::chrono:将时钟的纪元设置为 1/1/0000【英文标题】:std::chrono: Set clock's epoch to 1/1/0000 【发布时间】:2014-01-22 07:13:49 【问题描述】:是否可以手动将纪元日期/时间设置为 0000 年 1 月 1 日,所以我可以使用 std::chrono::time_point::time_since_epoch 来计算给定日期与 0000 年 1 月 1 日之间的差异?
我尝试了以下方法:
#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
【问题讨论】:
【参考方案1】:我会完全避免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 epochdays(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 年”之间的转换是微不足道的。
【讨论】:
+1 表示兼容计时的低级日期算法。非常感谢!【参考方案2】:有可能,您给出的代码(减去一个小修复,tm_mday
以1
开头)产生:
Sat Jan 1 00:00:00 0
Live example
真正的问题是:您使用的是 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 上。
【讨论】:
我正在使用 MacOS X 10.9.1 (Mavericks) - 我的可执行文件是 64 位:文件 src/demo src/demo:Mach-O 64 位可执行文件 x86_64 并且:您的代码仍然给我 Thu Jan 1 00:59:59 1970 ;-(,而现场示例是 Sat Jan 1 00:00:00 0. @JohannHorvat 这可能是你的问题,我现在无法确认,但我记得 MacOS 在这方面存在一些问题。如果您有兴趣:0-01-01 00:00:00
的正确值是 -62167219200
(所有值都是 UTC)。【参考方案3】:
没有。 mktime
和朋友们基于 UNIX 时间,从 1970 年 1 月 1 日开始。
实际上没有 0000 年 1 月 0 日这样的事情,因此您最好找到另一种方法来解决您的实际问题。
【讨论】:
0
是 1st January 1970
,但值可以是负数。
@DanielFrey:这是为了允许两个日期之间的间隔,而不是为了及时回到任意虚构的日期。 (提示:1BC 导致 1AD)以上是关于std::chrono:将时钟的纪元设置为 1/1/0000的主要内容,如果未能解决你的问题,请参考以下文章
如何找到纪元时间戳和 std::chrono::system_clock::now 之间的时间差(以毫秒为单位)
`std::chrono` 时钟与`boost::xtime` 的比较