2017-02-16 49 views
0

我試圖讓當前年份存儲在1970年之前的日期使用std::chrono::time_point<std::chrono::system_clock>,但是我遇到了一個有關從其內容閱讀的問題到一個std::tm結構。localtime_s失敗其中gmtime_s成功與日期之前1-1-1970

我首先將time_point轉換爲time_t,之後我讀取它的值以得到tm_year的值。但是,嘗試這樣做時,代碼在使用localtime_s時失敗,但在使用gmtime_s時成功。這隻適用於1-1-1970之前的日期,在此之後的日期使用這兩個函數都很好。

下面的代碼重現錯誤。如果使用utc=true調用terstGmTimeVsLocalTime,它將起作用,如果使用utc=false調用它,則不會生成正確的輸出。

#include <iomanip> 
#include <time.h> 
#include <iostream> 

void testGmTimeVsLocaltime(const bool& utc) { 
    // Create time 
    std::tm timeInfoWrite = std::tm(); 
    timeInfoWrite.tm_year = 1969 - 1900; // Year to parse, here it is 1969 
    timeInfoWrite.tm_mon = 0; 
    timeInfoWrite.tm_mday = 1; 
    timeInfoWrite.tm_hour = 1; 
    timeInfoWrite.tm_min = 0; 
    timeInfoWrite.tm_sec = 0; 
    timeInfoWrite.tm_isdst = -1; 

    std::chrono::time_point<std::chrono::system_clock> timePoint = std::chrono::system_clock::from_time_t(utc ? _mkgmtime(&timeInfoWrite) : std::mktime(&timeInfoWrite)); 

    // Convert to time_t 
    std::time_t timeT = std::chrono::system_clock::to_time_t(timePoint); 

    // Read values 
    std::tm timeInfoRead; 
    if (utc) { 
     gmtime_s(&timeInfoRead, &timeT); 
    } else { 
     localtime_s(&timeInfoRead, &timeT); 
    } 

    // Output result 
    std::cout << (timeInfoRead.tm_year + 1900) << '\n'; 

    // Wait for input 
    std::getchar(); 
} 

int main() { 
    testGmTimeVsLocaltime(true); // Set to false to show bug 

    return 0; 
} 

utc=true輸出1969,如預期的那樣。然而,utc=false輸出1899(可能是因爲發生錯誤,tm_year設置爲-1)。

有什麼我失蹤? The documentation沒有具體說明localtime_s在1-1-1970之前的日期會失敗。

我在Windows 10 x64上,如果它有所作爲。

+0

MSDN文檔建議它會失敗:「1970年1月1日午夜前。」請參閱https://msdn.microsoft.com/en-us/library/bf12f0hc.aspx –

+0

@RichardCritten嗯,我看到我一定是使用了一個操作系統特定的實現。沒有意識到'localtime_s'不是標準的一部分。 1970年1月1日午夜之前會有另一個功能不會失敗嗎?或者也許是另一種策略 我想我可以檢索UTC和本地時間之間的差異,只需使用'gmtime_s'函數,然後在需要本地時間時將差異添加到結果中,但似乎可以做得更簡單。 – Qub1

+0

您鏈接的文檔說_「自__epoch __轉換給定的時間」_;時間是實施決定時間開始的時間點。在Windows上有操作系統調用,可以在任何時間工作,但我不知道任何標準的API。 –

回答

1

使用Howard Hinnant's free, open-source date lib,可以完全側步笨拙,錯誤和錯誤傾向的C API,並直接與現代<chrono>爲基礎的系統的工作:

#include "chrono_io.h" 
#include "date.h" 
#include <iostream> 

void 
testGmTimeVsLocaltime() 
{ 
    using namespace date; 
    // Create time 
    auto timeInfoWrite = 1969_y/jan/1; 
    sys_days timePoint = timeInfoWrite; // this is a chrono::time_point 
    std::cout << timePoint.time_since_epoch() << '\n'; // -365 days 

    // Convert to time_t 
    // no need 

    // Read values 
    year_month_day timeInfoRead = timePoint; 

    // Output result 
    std::cout << timeInfoRead.year() << '\n'; 
} 

int 
main() 
{ 
    testGmTimeVsLocaltime(); 
} 

輸出:

-365[86400]s 
1969 

有文字可以很容易地填入一個year_month_day結構,它是tm的年,月和日部分的模擬結構。您可以輕鬆將其轉換爲std::chrono::time_point<system_clock, days>sys_days)。這與system_clock::time_point相同,但精確度爲幾天。它本身將隱式轉換爲秒精度time_point(類型定義爲sys_seconds)或system_clock::time_point

上面我剛剛輸出了它的time_since_epoch(),它顯示它是在時代的前365天。

從來沒有真正需要轉換爲C API數據結構,但如果您想要,它很容易。例如,假設是time_t秒因爲1970-01-01:

std::time_t timeT = sys_seconds{timePoint}.time_since_epoch().count(); 
std::cout << timeT << '\n'; 

,其輸出:

-31536000 

的逆變換(回year_month_day)也很容易。如果你想從timeT轉換它只是稍微有點複雜:

year_month_day timeInfoRead = floor<days>(sys_seconds{seconds{timeT}}); 

這首先將time_tchrono::seconds,然後到seconds -precsion time_point,然後到days -precsion time_point,終於到了year_month_day字段類型(tm -like)。

最後year_month_day有一個year()可流式傳輸的getter成員函數。如果需要,可以明確地轉換到yearint

int{timeInfoRead.year()} 

但我認爲,最好讓所有的事情就像幾年,月和日是不同的類型,使編譯器可以幫助您捕捉,當你不小心混合起來。

最後,如果你真的想要在你的計算機的本地時區,there's a library想要1969-01-01 00:00:00也這樣做。這只是上述簡單程序的一個小修改。

#include "tz.h" 
#include <iostream> 

void 
testGmTimeVsLocaltime() 
{ 
    using namespace date; 
    using namespace std::chrono; 
    // Create time 
    auto timeInfoWrite = 1969_y/jan/1; 
    auto timePoint = make_zoned(current_zone(), local_days{timeInfoWrite}); 

    // Convert to time_t 
    std::time_t timeT = timePoint.get_sys_time().time_since_epoch().count(); 
    std::cout << timeT << '\n'; 

    // Read values 
    timePoint = sys_seconds{seconds{timeT}}; 
    year_month_day timeInfoRead{floor<days>(timePoint.get_local_time())}; 

    // Output result 
    std::cout << timeInfoRead.year() << '\n'; 
} 

int 
main() 
{ 
    testGmTimeVsLocaltime(); 
} 

輸出:

-31518000 
1969 

現在你創建一個zoned_seconds使用電腦的時區current_zone(),而轉換到timeInfoWrite代替local_dayssys_days

您可以從當地時間或系統時間中獲得timePoint。對於轉換爲time_t,系統時間是很有道理的:

std::time_t timeT = timePoint.get_sys_time().time_since_epoch().count(); 

而且現在的輸出(對我來說)是5小時後(18000s)。

-31518000 

你可以找回在本地一年,或系統(UTC)一年,或者使用.get_local_time().get_sys_time()。對我來說沒有區別("America/New_York")。但是如果你在"Australia/Sydney",如果你申請UTC年代而不是1969年,你將得到1968.這很容易通過在上述程序中用"Australia/Sydney""America/New_York"代替current_zone()來模擬。

是的,它適用於Windows,VS-2013及更高版本。時區庫lib需要一些安裝:https://howardhinnant.github.io/date/tz.html#Installation