From 45354a421c2a790413385174ef642a839a664cd6 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 10 Aug 2021 14:07:15 +0200 Subject: 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. --- src/time/FileTime.hxx | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/time/FileTime.hxx b/src/time/FileTime.hxx index 4ac323331..6319d7749 100644 --- a/src/time/FileTime.hxx +++ b/src/time/FileTime.hxx @@ -71,17 +71,37 @@ FileTimeToChronoDuration(FILETIME ft) noexcept 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 FileTimeToChrono(FILETIME ft) noexcept { - // TODO: eliminate the time_t roundtrip, preserve sub-second resolution - return std::chrono::system_clock::from_time_t(FileTimeToTimeT(ft)); + /* this is guaranteed to be 0 in C++20 */ + 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 + (windows_duration); + + return unix_epoch + sys_duration; } constexpr std::chrono::seconds -- cgit v1.2.3