簡體   English   中英

time_t 以提高日期轉換給出不正確的結果

[英]time_t to boost date conversion giving incorrect result

我有一組 unix 時間戳,我正在轉換為 boost (1.65.1) 日期,但是當它們在未來變得太遠時,轉換似乎會崩潰。 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

我在這里做錯了嗎?

您似乎遇到了 2038 年的問題。

32 位有符號整數可表示的最大數是 2'147'483'647。 自 1970 年 1 月 1 日(UNIX 紀元)00:00:00 UTC 以來的 2'147'483'647 秒是 2038 年 1 月 19 日的 03:14:07 UTC。在此之后的任何 UNIX 時間都無法使用 32 位有符號表示整數。

系統上的std::time_t要么是 32 位,要么在 boost 庫中轉換為 32 位。 您可以從源代碼中看到 boost 使用static_cast將輸入轉換為long (並且在1.70版本中仍然如此)。 long是 32 位,例如在 Windows 上,甚至在 64 位架構上。 它在許多其他系統上是 64 位的,例如 64 位 Linux。

在 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 的這一部分,則可以使用適用於 C++11/14/17 的免費開源預覽版 1

只需添加:

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

1完全披露:我是該庫的主要作者。 我不追求從這項努力中獲得任何經濟利益。 但有時如果我不完全披露這些信息,人們會變得脾氣暴躁。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM