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

时间:2014-01-04 16:35:40

标签: c++ c++11 epoch 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);

}

但是这给了我

time: Thu Jan  1 00:59:59 1970

3 个答案:

答案 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而不是durationtime_pointduration的代数可以为我们工作,这两者都可以简化类型检查我们的表达式,以帮助我们编写更清晰的代码,减少错误。

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

epoch + epoch

time_point的类型使用duration而不是epoch,编译器会在编译时捕获这些非敏感表达式。

1 proleptic Gregorian calendar有一年0.在第0年,它比Julian日历落后2天。使用0年也符合ISO 8601.只要所有相关方知道您使用的是哪个日历,那么一切都很好。如果需要,非正年和“BC年”之间的转换是微不足道的。

答案 1 :(得分:3)

有可能,您提供的代码(减去一个小修正,tm_mday1开头)产生:

Sat Jan  1 00:00:00 0

Live example

真正的问题是:你是32位还是64位?对于32位系统,time_t也只有32位,您可以限制在1970 +/- 68年。

在64位系统上,限制由std::mktimestd::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日这样的事情,所以你最好找到另一种解决你的实际问题的方法。