std :: chrono:将时钟的纪元设置为1/1/0000

Joh*_*vat 2 c++ epoch c++11 c++-chrono

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

}
Run Code Online (Sandbox Code Playgroud)

但这给了我

time: Thu Jan  1 00:59:59 1970
Run Code Online (Sandbox Code Playgroud)

How*_*ant 9

我会std::time_t完全避免.使用days_from_civil时间兼容的低级别日期算法,您可以立即计算出公历日历1中的任何差异std::chrono::system_clock::time_point任何日期.

除了days_from_civil需要一年/每月/每天三倍并将其转换为1970-01-01之前/之后的天数(时间兼容的纪元),创建chrono::duration24小时的自定义也很方便:

typedef std::chrono::duration
        <
            int,
            std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
        > days;
Run Code Online (Sandbox Code Playgroud)

现在,您只需创建所需的任何纪元:

constexpr days epoch = days(days_from_civil(0, 1, 1));  // 0000-01-01
Run Code Online (Sandbox Code Playgroud)

在C++ 1y中,这甚至是编译时计算!

你可以std::chrono::duration任何其他减去这个std::chrono::duration:

auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;
Run Code Online (Sandbox Code Playgroud)

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";
}
Run Code Online (Sandbox Code Playgroud)

哪个对我输出:

It has been 735602 days, 19 hours, 14 minutes, 32 seconds  since 0000-01-01
Run Code Online (Sandbox Code Playgroud)

关于溢出的警告:

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));
Run Code Online (Sandbox Code Playgroud)

有类型days,这是一个duration.但它确实不是一个duration.这是一个时间点.这是约会.它time_point具有粗糙的精度.通过引入一个新的typedef,这篇文章中的代码可以稍微清理一下:

typedef std::chrono::time_point<std::chrono::system_clock, days> date_point;
Run Code Online (Sandbox Code Playgroud)

现在而不是写:

constexpr days epoch = days(days_from_civil(0, 1, 1));
Run Code Online (Sandbox Code Playgroud)

人们可以写:

constexpr date_point epoch{days(days_from_civil(0, 1, 1))};
Run Code Online (Sandbox Code Playgroud)

但更重要的是,而不是:

auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;
Run Code Online (Sandbox Code Playgroud)

我们现在可以写:

auto delta = std::chrono::system_clock::now() - epoch;
Run Code Online (Sandbox Code Playgroud)

delta仍然具有与之前完全相同的类型和值,并且演示中的其他所有内容仍然与之前完全一样.

这既是一个小变化,也是一个很大的变化.通过将's和's 的代数epoch视为time_point代替a duration,它们为我们工作,简化类型检查我们的表达式,以帮助我们编写更清晰的代码,减少错误.time_pointduration

例如,可以将两个duration一起添加.但它完全没有任何意义:

epoch + epoch
Run Code Online (Sandbox Code Playgroud)

通过使用time_point而不是duration类型epoch,编译器在编译时捕获这样的非敏感表达式.

1proleptic公历有一个0年在这一年0是儒略历后面2天.使用0年也符合ISO 8601.只要所有相关方知道您使用的是哪个日历,那么一切都很好.如果需要,非正年和"BC年"之间的转换是微不足道的.