【问题标题】:Converting time from one environment into another in C++在 C++ 中将时间从一种环境转换为另一种环境
【发布时间】:2017-10-23 18:51:26
【问题描述】:

我从服务器端(运行 .NET 应用程序)获取 uint64_t 值到我用标准 C++ 编写的应用程序(必须在 Windows 和 Linux 上运行)。

这个数字代表 Windows 文件时间 - 也就是自 1601-01-01 00:00:00 UTC 以来的 100 纳秒间隔。

我需要在我的应用程序中返回时间的字符串表示,精度为纳秒(这是我从服务器获得的值的精度),所以我必须使用 chrono 库。

由于 C++ 的时代是 0001 年 1 月 1 日到 1970 年 1 月 1 日,我首先需要计算从 1970 年到 1601 年的偏移量,然后从我从服务器获得的数字中减去它。为了做到这一点,我首先必须将我从服务器获得的值表示为 chrono::time_point,并以 100 纳秒的间隔计算 1601-01-01 00:00:00 UTC 的纪元,以便它和值我得到的是相同的规模。

在我拥有 addjustedTimeFromServer 之后 - 这是我从服务器获得的值减去(以 chrono::time_point 形式)偏移量,我需要将其转换为 std::time_t 以提取准确的值秒,然后从 chrono::time_point 我需要提取小数秒,这会给我纳秒的精度,我会将它们连接到表示时间的字符串。

这是我的代码。它不能满足我的需要:

using FileTime = duration<int64_t, ratio<1, 10000000>>;
struct std::tm tm;

//create time point for epoch of Windows Filetime (1601-01-01 00:00:00 UTC))
std::istringstream ss("1601-01-01 00:00:00");
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); 
std::time_t tt = mktime(&tm);
std::chrono::system_clock::time_point offset =std::chrono::system_clock::from_time_t(tt); 

//convert the offset into 100-nanosecond intervals scale
auto offset_ns = std::chrono::time_point_cast<std::chrono::nanoseconds>(offset);
auto offset_100ns = FileTime(offset_ns.time_since_epoch());
//substract the offset from i so now it starts from 1970 like the epoch of C++
auto iDuration = FileTime(static_cast<int64_t>(i));
//auto iDuration_ns = std::chrono::time_point_cast<std::chrono::nanoseconds>(iDuration); //doesn't compile - but that's the idea of what i want to do in this line


std::chrono::system_clock::time_point adjustedTime = iDuration/*iDuration_ns*/ - offset /*-offset_100ns*/; //the commented out parts are what i think is the correct thing to do (scale wise) but they don't compile

//convert the time_point into the string representation i need (extract the regular time, up to seconds, with time_t and the nanosecond part with ns.count())
nanoseconds ns = duration_cast<nanoseconds>(adjustedTime.time_since_epoch());
seconds s = duration_cast<seconds>(ns);
std::time_t t = s.count();
std::size_t fractional_seconds = ns.count() % 10000000;
std::cout << std::ctime(&t) << std::endl;
std::cout << fractional_seconds << std::endl;

代码不起作用,我不知道如何修复它。第一个问题(甚至在所有比例转换问题之前)是 mktime(&tm) 给了我一个不正确的值。由于 tm 表示 C++ 纪元之前的值,因此 mktime(&tm) 返回 -1。我需要以某种方式克服它,因为我必须计算 .NET Filetime 时期(1601-01-01 00:00:00 UTC)的 time_point,以便从我从服务器获得的值中减去它。

我会为这个问题和整个程序提供帮助。

P.S 我只是在此代码中打印,但在最终版本中,我会将这两个部分连接到同一个字符串(ctime(&t) 给出的部分和 fractional_seconds 给出的部分)

【问题讨论】:

    标签: c++ date datetime time chrono


    【解决方案1】:

    Here is code to do this 实际上是由 MSVC std::lib 团队的成员 (Billy O'Neal) 捐赠给本网站的。

    在此重复:

    // filetime_duration has the same layout as FILETIME; 100ns intervals
    using filetime_duration = duration<int64_t, ratio<1, 10'000'000>>;
    // January 1, 1601 (NT epoch) - January 1, 1970 (Unix epoch):
    constexpr duration<int64_t> nt_to_unix_epoch{INT64_C(-11644473600)};
    
    system_clock::time_point
    FILETIME_to_system_clock(FILETIME fileTime)
    {
        const filetime_duration asDuration{static_cast<int64_t>(
            (static_cast<uint64_t>(fileTime.dwHighDateTime) << 32)
                | fileTime.dwLowDateTime)};
        const auto withUnixEpoch = asDuration + nt_to_unix_epoch;
        return system_clock::time_point{
            duration_cast<system_clock::duration>(withUnixEpoch)};
    }
    

    这将转换为system_clock::time_point,在 Linux 上具有纳秒精度,在 Windows 上具有 100 纳秒精度。

    使用my date/time library,可以轻松地将system_clock::time_point 格式化为您想要的任何格式的全精度。

    另外,这里没有使用 Windows FILETIME 结构:

    #include "date.h"
    #include <string>
    #include <iostream>
    
    std::chrono::system_clock::time_point
    FILETIME_to_system_clock(std::uint64_t fileTime)
    {
        using namespace std;
        using namespace std::chrono;
        // filetime_duration has the same layout as FILETIME; 100ns intervals
        using filetime_duration =duration<int64_t, ratio<1, 10000000>>;
        // January 1, 1601 (NT epoch) - January 1, 1970 (Unix epoch):
        constexpr duration<int64_t> nt_to_unix_epoch{INT64_C(-11644473600)};
    
        const filetime_duration asDuration{static_cast<int64_t>(fileTime)};
        const auto withUnixEpoch = asDuration + nt_to_unix_epoch;
        return system_clock::time_point{
               duration_cast<system_clock::duration>(withUnixEpoch)};
    }
    
    int
    main()
    {
        std::string s = date::format("%F %T", FILETIME_to_system_clock(131400356659154460));
        std::cout << s << '\n';
    }
    

    这只是为我输出:

    2017-05-23 17:54:25.915446
    

    请注意,这只是微秒精度。在 Linux 上,这将格式化为纳秒精度,因为 system_clock::time_point 在该平台上具有纳秒精度。

    如果不是这样,您可以像这样强制纳秒精度:

        using namespace std::chrono;
        std::string s = date::format("%F %T",
            time_point_cast<nanoseconds>(FILETIME_to_system_clock(131400356659154460)));
    

    为我输出:

    2017-05-23 17:54:25.915446000
    

    在此更新中,输出 chrono::time_point 的精度与 Window 的 FILETIME 相同:100-ns 精度:

    #include "date.h"
    #include <string>
    #include <iostream>
    
    using filetime_duration = std::chrono::duration<std::int64_t, std::ratio<1, 10000000>>;
    using FileTime = std::chrono::time_point<std::chrono::system_clock, filetime_duration>;
    // or more simply:
    // using FileTime = date::sys_time<filetime_duration>;
    
    FileTime
    FILETIME_to_system_clock(std::uint64_t fileTime)
    {
        using namespace std;
        using namespace std::chrono;
        // filetime_duration has the same layout as FILETIME; 100ns intervals
        // January 1, 1601 (NT epoch) - January 1, 1970 (Unix epoch):
        constexpr seconds nt_to_unix_epoch{-11644473600};
    
        const filetime_duration asDuration{static_cast<int64_t>(fileTime)};
        return FileTime{asDuration + nt_to_unix_epoch};
    }
    
    int
    main()
    {
        using namespace std::chrono;
        std::string s = date::format("%F %T", FILETIME_to_system_clock(131400356659154461));
        std::cout << s << '\n';
    }
    

    输出:

    2017-05-23 17:54:25.9154461
    

    【讨论】:

    • FILETIME 类型在我的 grogram 中无法识别。因为我正在编写纯 C++ 代码,所以我没有 .NET 对象。我从代表该 FILETIME 的服务器 uint64_t 值中获取。有没有办法在这段代码中使用 uint64_t?
    • 完成。为了从system_clock::time_point 轻松格式化,我使用了上面链接的日期/时间库。这避免了在通过time_t 时必须处理的笨拙的截断秒数。但是,如果您想通过time_t,请成为我的客人。 :-)
    • 谢谢它很好用。只是一件小事 - 当我使用 ns.count() % 1000000000 时它给了我 .915446000 但我只需要点后的 7 位数字 - 也就是 9154460,但是当我使用 ns.count() % 10000000 时它会下降 91 而不是 00并返回 .5446000 - 有没有办法修复它,所以我得到 9154460?
    • 啊,所以你想要 100 ns 的精度(相当于 FILETIME)。没问题,正在编辑...
    • 我仍然有同样的问题(可能是由于我尝试提取纳秒部分的方式)。当我调试时,我看到 ns 包含以下内容:ns 1495562065915446100,并且是 std::chrono::duration<__int64> > 类型。这就是为什么当我执行 ns.count()%10000000 时我得到尾随零。如何使 ns 的比率?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-06
    • 2015-08-17
    • 1970-01-01
    • 1970-01-01
    • 2016-05-10
    相关资源
    最近更新 更多