Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 654
aatwo Avatar asked May 20 '19 14:05

aatwo


2 Answers

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.

like image 51
eerorika Avatar answered Nov 16 '22 16:11

eerorika


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.

like image 40
Howard Hinnant Avatar answered Nov 16 '22 15:11

Howard Hinnant