time_t加速日期转换,给出错误的结果

时间:2019-05-20 14:48:03

标签: c++ boost

我收集了一些要转换为增强(1.65.1)日期的unix时间戳,但是当它们将来距离太远时,转换似乎会中断。到2040年及以后的一切似乎都在以某种方式回溯到1900年以后。

给出以下代码...

        {
            std::time_t t = 1558220400;
            boost::gregorian::date date = boost::posix_time::from_time_t(t).date();
            std::cout << "Date: " << date << std::endl;
        }

        {
            std::time_t t = 2145500000;
            boost::gregorian::date date = boost::posix_time::from_time_t(t).date();
            std::cout << "Date: " << date << std::endl;
        }

        {
            std::time_t t = 2500000000;
            boost::gregorian::date date = boost::posix_time::from_time_t(t).date();
            std::cout << "Date: " << date << std::endl;
        }

...我得到以下输出...

    Date: 2019-May-18
    Date: 2037-Dec-27
    Date: 1913-Feb-13

...但是我期待以下输出...

Expected output:
    Date: 2019-May-18
    Date: 2037-Dec-27
    Date: 2049-Mar-22

我在这里做错什么了吗?

3 个答案:

答案 0 :(得分:3)

您似乎遇到了2038年的问题。

可由32位带符号整数表示的最大数字为2'147'483'647。自1970年1月1日UTC时间00:00:00(UNIX纪元)以来的2'147'483'647秒为2038年1月19日UTC时间20:38:14。此后的任何UNIX时间都无法使用32位符号表示整数。

系统上的std::time_t为32位,或者在boost库中转换为32位。您可以从source中看到,它使用long将输入转换为static_cast(并且在1.70版本中仍然如此)。 long在Windows上是32位,即使在64位体系结构上也是如此。在许多其他系统(例如64位Linux)上,它是64位。

答案 1 :(得分:2)

在 C++20 中,这现在看起来像:

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std::chrono;

    {
        std::time_t t = 1558220400;
        auto date = floor<days>(system_clock::from_time_t(t));
        std::cout << "Date: " << date << '\n';
    }

    {
        std::time_t t = 2145500000;
        auto date = floor<days>(system_clock::from_time_t(t));
        std::cout << "Date: " << date << '\n';
    }

    {
        std::time_t t = 2500000000;
        auto date = floor<days>(system_clock::from_time_t(t));
        std::cout << "Date: " << date << '\n';
    }
}

输出:

Date: 2019-05-18
Date: 2037-12-27
Date: 2049-03-22

如果您的 time_t 是 32 位,那么以上内容不足以解决问题。在这种情况下,您必须完全避免使用 C API。这看起来像:

{
    auto t = 1558220400;
    auto date = floor<days>(sys_seconds{seconds{t}});
    std::cout << "Date: " << date << '\n';
}

{
    auto t = 2145500000;
    auto date = floor<days>(sys_seconds{seconds{t}});
    std::cout << "Date: " << date << '\n';
}

{
    auto t = 2500000000;
    auto date = floor<days>(sys_seconds{seconds{t}});
    std::cout << "Date: " << date << '\n';
}

如果您的供应商尚未发布 C++20 的这一部分,a free, open-source preview that works with C++11/14/17 is available1

只需添加:

#include "date/date.h"
...
using namespace date;

1 完全披露:我是这个库的主要作者。我不追求从这项努力中获得任何经济利益。但有时如果我不完全披露这些信息,人们会变得脾气暴躁。

答案 2 :(得分:0)

正如eerorika指出的那样,这是由于整数溢出引起的,因为boost :: posix_time :: from_time_t将64位的time_t值强制转换为32位长(在Windows上)。

如果您正处于紧要关头,然后可以使用以下功能执行转换:

boost::gregorian::datetimet_to_date(time_t t)
{
    auto time = boost::posix_time::ptime(boost::gregorian::date(1970,1,1));

    int64_t current_t = t;
    long long_max = std::numeric_limits<long>::max();
    while(current_t > 0)
    {
        long seconds_to_add = 0;
        if(current_t >= long_max)
            seconds_to_add = long_max;
        else
            seconds_to_add = static_cast<long>(current_t);

        current_t -= seconds_to_add;
        time += boost::posix_time::seconds(seconds_to_add);
    }

    return time.date();
}