localtime_s失败,gmtime_s成功,1-1-1970之前的日期

时间:2017-02-16 23:23:41

标签: c++ chrono time-t

我试图使用std::chrono::time_point<std::chrono::system_clock>将当前年份存储在1970年以前的某个日期,但是我遇到了从其内容读取到{{1}的问题结构。

我先将std::tm转换为time_point,之后我会读取其值以获取time_t值。但是,在尝试执行此操作时,代码在使用tm_year时失败,但在我使用localtime_s时成功。这仅适用于1-1-1970之前的日期,使用这两种功能的日期之后的日期。

以下代码重现错误。如果使用gmtime_s调用terstGmTimeVsLocalTime,则可以使用utc=true,如果使用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并未明确指定{1}}在1-1-1970之前的日期失败。

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

1 个答案:

答案 0 :(得分: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(typedef&#39; d到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_t转换为chrono::seconds,然后转换为seconds - precsion time_point,然后转换为days - precsion {{1} },最后到time_point字段类型(year_month_day - 就像)。

最后tm有一个year_month_day getter成员函数,可以流式传输。如果需要,您可以明确地将year()转换为year

int

但我认为最好将年,月和日等事物保存为不同的类型,这样编译器可以帮助您在意外混淆时捕获它们。

最后,如果您真的想要在计算机的本地时区中使用1969-01-01 00:00:00,there's a library也可以这样做。它只是对上述简单程序的一个小修改。

int{timeInfoRead.year()}

输出:

#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_days。< / p>

您可以从sys_days获取当地时间或系统时间。要转换为timePoint,系统时间最有意义:

time_t

现在输出(对我来说)是5小时后(18000年代)。

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

您可以使用-31518000 .get_local_time()返回本地年份或系统(UTC)年份。对我来说没有区别(.get_sys_time())。但是,如果您在"America/New_York",如果您申请UTC年而不是1969年,那么您将获得1968年。通过简单地替换"Australia/Sydney",这一切都很容易模拟或上述计划中的"Australia/Sydney" "America/New_York"

是的,它适用于Windows,VS-2013及更高版本。时区lib需要一些安装:https://howardhinnant.github.io/date/tz.html#Installation