在 C++ 中将时间从一种环境转换为另一种环境

pri*_*ion 1 c++ time datetime date c++-chrono

我从服务器端(运行 .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,因此它和值我得到了相同的规模。

After i have the addjustedTimeFromServer - which is the value i get from the server minus (in the chrono::time_point form) the offset, i need to convert it into std::time_t in order to extract the value with accuracy of seconds, and then from the chrono::time_point i need to extract the fractional_seconds which will give me the accuracy of nanoseconds, and i will concatenate them to the string representing the time.

Here is my code. It doesn't do what i need:

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;
Run Code Online (Sandbox Code Playgroud)

The code doesn't work and i am not sure how to fix it.The first problem (even before all the scale conversion issue) is that mktime(&tm) gives me an incorrect value. Since tm represents a value that's before C++ epoch, mktime(&tm) returns -1. I need to somehow overcome it, since i have to calculate the time_point of the .NET Filetime epoch (1601-01-01 00:00:00 UTC) in order to subtract it from the value i get from the server.

I will appriciate help with this issue and with the program in general.

P.S I just print in this code but in the final version i will concatenate both parts to the same string (the part given by ctime(&t) and the part in fractional_seconds)

How*_*ant 5

这是执行此操作的代码,它实际上是由 MSVC std::lib 团队(比利·奥尼尔)的成员捐赠给该站点的。

这里重复:

// 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)};
}
Run Code Online (Sandbox Code Playgroud)

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

使用我的日期/时间库,可以轻松地system_clock::time_point以您想要的任何格式完全精确地进行格式化。

此外,这里不使用 WindowsFILETIME结构:

#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';
}
Run Code Online (Sandbox Code Playgroud)

这只是对我的输出:

2017-05-23 17:54:25.915446
Run Code Online (Sandbox Code Playgroud)

请注意,这仅达到微秒精度。在 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)));
Run Code Online (Sandbox Code Playgroud)

我的输出:

2017-05-23 17:54:25.915446000
Run Code Online (Sandbox Code Playgroud)

在此更新中,输出的精度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';
}
Run Code Online (Sandbox Code Playgroud)

输出:

2017-05-23 17:54:25.9154461
Run Code Online (Sandbox Code Playgroud)