2

是否可以手动将纪元日期/时间设置为 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
4

3 回答 3

9

我会std::time_t完全避免。使用days_from_civilfrom chrono-Compatible Low-Level Date Algorithmsstd::chrono::system_clock::time_point ,您可以立即计算, 和预测公历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 年。如果您需要避免溢出问题,您可以std::chrono::system_clock::time_point::duration在减去 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仍然具有与以前完全相同的类型和值,并且演示中的其他所有内容仍然与以前完全相同。

这既是一个小变化,也是一个大变化。通过将's 和'的代数epoch视为 atime_point而不是 a ,我们可以简化检查我们的表达式,以帮助我们编写更简洁的代码和更少的错误。durationtime_pointduration

例如,可以将两个duration' 加在一起。但这根本没有任何意义:

epoch + epoch

通过使用time_point而不是durationfor 的类型epoch,编译器会在编译时捕获此类无意义的表达式。

1预测的公历有 0 年。在 0 年,它比儒略历晚 2 天。使用第 0 年也符合 ISO 8601。只要所有相关方都知道您使用的是什么日历,那么一切都很好。如果需要,非正数年和“BC 年”之间的转换是微不足道的。

于 2014-01-04T19:28:08.673 回答
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 上。

于 2014-01-04T16:47:48.350 回答
1

mktime,朋友们基于 UNIX 时间,从 1970 年 1 月 1 日开始。

事实上,没有像 0000 年 1 月 0 日这样的事情,所以看起来你最好找到另一种方法来解决你的实际问题。

于 2014-01-04T16:37:55.443 回答