简体   繁体   中英

time_t to boost date conversion giving incorrect result

I have a collection of unix timestamps I am converting to boost (1.65.1) dates but the conversions seem to break down when they get too far in the future. Anything around 2040 and beyond seems to be wrapping in some way back to post 1900.

Given the following code...

        {
            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;
        }

... I get the following output...

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

... however I am expecting the following output...

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

Is there something I am doing wrong here?

It appears that you're experiencing the Year 2038 problem.

The largest number representable by 32 bit signed integer is 2'147'483'647. 2'147'483'647 seconds since 00:00:00 UTC on 1st of January 1970 (the UNIX epoch) is 03:14:07 UTC on 19th of January 2038. Any UNIX time after that is unrepresentable using a 32 bit signed integer.

Either std::time_t on the system is 32 bits, or it is converted into 32 bits inside the boost library. You can see from the source that boost converts the input into long using static_cast (and still does in version 1.70 ). long is 32 bits for example on windows, even on 64 bit architectures. It is 64 bits on many other systems such as 64 bit Linux.

In C++20 this can now look like:

#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';
    }
}

Output:

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

If your time_t is 32 bits, then the above isn't quite sufficient to fix the problem. In that case, you must avoid the C API completely. This looks like:

{
    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';
}

If your vendor isn't shipping this part of C++20 yet, a free, open-source preview that works with C++11/14/17 is available . 1

Just add:

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

1 Full disclosure: I am the lead author of this library. I am not pursuing any financial gain from this effort. But sometimes people get grumpy if I don't fully disclose this information.

As noted by eerorika this is caused by integer overflow since the boost::posix_time::from_time_t is casting the 64bit time_t value to a 32 bit long (on Windows).

If you are in a pinch and find yourself in the same position then you can use the following function to perform the conversion:

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();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM