2017-02-16 48 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