简体   繁体   English

time_t 以提高日期转换给出不正确的结果

[英]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.我有一组 unix 时间戳,我正在转换为 boost (1.65.1) 日期,但是当它们在未来变得太远时,转换似乎会崩溃。 Anything around 2040 and beyond seems to be wrapping in some way back to post 1900. 2040 年左右及以后的任何事情似乎都会以某种方式回到 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.您似乎遇到了 2038 年的问题。

The largest number representable by 32 bit signed integer is 2'147'483'647. 32 位有符号整数可表示的最大数是 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.自 1970 年 1 月 1 日(UNIX 纪元)00:00:00 UTC 以来的 2'147'483'647 秒是 2038 年 1 月 19 日的 03:14:07 UTC。在此之后的任何 UNIX 时间都无法使用 32 位有符号表示整数。

Either std::time_t on the system is 32 bits, or it is converted into 32 bits inside the boost library.系统上的std::time_t要么是 32 位,要么在 boost 库中转换为 32 位。 You can see from the source that boost converts the input into long using static_cast (and still does in version 1.70 ).您可以从源代码中看到 boost 使用static_cast将输入转换为long (并且在1.70版本中仍然如此)。 long is 32 bits for example on windows, even on 64 bit architectures. long是 32 位,例如在 Windows 上,甚至在 64 位架构上。 It is 64 bits on many other systems such as 64 bit Linux.它在许多其他系统上是 64 位的,例如 64 位 Linux。

In C++20 this can now look like:在 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';
    }
}

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.如果您的time_t是 32 位,那么以上内容不足以解决问题。 In that case, you must avoid the C API completely.在这种情况下,您必须完全避免使用 C API。 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 .如果您的供应商尚未发布 C++20 的这一部分,则可以使用适用于 C++11/14/17 的免费开源预览版 1 1

Just add:只需添加:

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

1 Full disclosure: I am the lead author of this library. 1完全披露:我是该库的主要作者。 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).正如 eerorika 所指出的,这是由整数溢出引起的,因为 boost::posix_time::from_time_t 将 64 位 time_t 值转换为 32 位长(在 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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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