From cc5469c0478a261f4d39c9fbbd7e70432f4e48d0 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 29 Nov 2017 22:20:16 +0900 Subject: [PATCH] mingw: Use SetFileTime to avoid DST adjustment --- src/File.cc | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/File.cc b/src/File.cc index be658cbb..834cab4b 100644 --- a/src/File.cc +++ b/src/File.cc @@ -122,9 +122,9 @@ bool File::remove() #ifdef __MINGW32__ namespace { -HANDLE openFile(const std::string& filename) +HANDLE openFile(const std::string& filename, bool readOnly = true) { - DWORD desiredAccess = GENERIC_READ; + DWORD desiredAccess = GENERIC_READ | (readOnly ? 0 : GENERIC_WRITE); DWORD sharedMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; DWORD creationDisp = OPEN_EXISTING; return CreateFileW(utf8ToWChar(filename).c_str(), desiredAccess, sharedMode, @@ -317,12 +317,50 @@ bool File::utime(const Time& actime, const Time& modtime) const struct timeval times[2] = {{actime.getTimeFromEpoch(), 0}, {modtime.getTimeFromEpoch(), 0}}; return utimes(name_.c_str(), times) == 0; -#else // !HAVE_UTIMES +#elif defined(__MINGW32__) + auto hn = openFile(name_, false); + if (hn == INVALID_HANDLE_VALUE) { + auto errNum = GetLastError(); + A2_LOG_ERROR(fmt(EX_FILE_OPEN, name_.c_str(), + util::formatLastError(errNum).c_str())); + return false; + } + // Use SetFileTime because Windows _wutime takes DST into + // consideration. + // + // std::chrono::time_point::time_since_epoch returns the amount of + // time between it and epoch Jan 1, 1970. OTOH, FILETIME structure + // expects the epoch as Jan 1, 1601. The resolution is 100 + // nanoseconds. + constexpr auto offset = 116444736000000000LL; + uint64_t at = std::chrono::duration_cast( + actime.getTime().time_since_epoch()) + .count() / + 100 + + offset; + uint64_t mt = std::chrono::duration_cast( + modtime.getTime().time_since_epoch()) + .count() / + 100 + + offset; + FILETIME att{static_cast(at & 0xffffffff), + static_cast(at >> 32)}; + FILETIME mtt{static_cast(mt & 0xffffffff), + static_cast(mt >> 32)}; + auto rv = SetFileTime(hn, nullptr, &att, &mtt); + if (!rv) { + auto errNum = GetLastError(); + A2_LOG_ERROR(fmt("SetFileTime failed, cause: %s", + util::formatLastError(errNum).c_str())); + } + CloseHandle(hn); + return rv; +#else // !defined(HAVE_UTIMES) && !defined(__MINGW32__) a2utimbuf ub; ub.actime = actime.getTimeFromEpoch(); ub.modtime = modtime.getTimeFromEpoch(); return a2utime(utf8ToWChar(name_).c_str(), &ub) == 0; -#endif // !HAVE_UTIMES +#endif // !defined(HAVE_UTIMES) && !defined(__MINGW32__) } Time File::getModifiedTime()