time/FileTime: preserve the FILETIME resolution

Don't truncate the FILETIME to second resolution to pass it to
std::chrono::system_clock::from_time_t(); instead, calculate the
offset between the FILETIME epoch and the
std::system_clock::time_point epoch, and use that to initialize the
time_point directly.
This commit is contained in:
Max Kellermann 2021-08-10 14:07:15 +02:00
parent 9fc3c60910
commit 45354a421c

View File

@ -71,17 +71,37 @@ FileTimeToChronoDuration(FILETIME ft) noexcept
return FileTimeDuration(ToInt64(ft)); return FileTimeDuration(ToInt64(ft));
} }
constexpr time_t /**
FileTimeToTimeT(FILETIME ft) noexcept * Calculate a std::chrono::duration specifying the duration between
* the unix epoch and the given FILETIME.
*/
constexpr auto
FileTimeToUnixEpochDuration(FILETIME ft) noexcept
{ {
return (ToInt64(ft) - 116444736000000000) / 10000000; /**
* The number of days between the Windows FILETIME epoch
* (1601-01-01T00:00) and the Unix epoch (1970-01-01T00:00).
*/
constexpr int_least64_t windows_unix_days = 134774;
constexpr int_least64_t windows_unix_hours = windows_unix_days * 24;
constexpr FileTimeDuration windows_unix_delta{std::chrono::hours{windows_unix_hours}};
return FileTimeToChronoDuration(ft) - windows_unix_delta;
} }
inline std::chrono::system_clock::time_point inline std::chrono::system_clock::time_point
FileTimeToChrono(FILETIME ft) noexcept FileTimeToChrono(FILETIME ft) noexcept
{ {
// TODO: eliminate the time_t roundtrip, preserve sub-second resolution /* this is guaranteed to be 0 in C++20 */
return std::chrono::system_clock::from_time_t(FileTimeToTimeT(ft)); const auto unix_epoch = std::chrono::system_clock::from_time_t(0);
const auto windows_duration = FileTimeToUnixEpochDuration(ft);
const auto sys_duration =
std::chrono::duration_cast<std::chrono::system_clock::duration>
(windows_duration);
return unix_epoch + sys_duration;
} }
constexpr std::chrono::seconds constexpr std::chrono::seconds