如何比较time_t和标准::文件系统::file_time_type

How to compare time_t and std::filesystem::file_time_type

本文关键字:time 何比较 标准 文件系统 type file 比较      更新时间:2023-10-16

我正在将一些代码从boost::filesystem转换为std::filesystem。以前的代码boost::filesystem::last_write_time()返回一个time_t,因此与我已经持有的time_t对象的直接比较是微不足道的。顺便说一下,我持有的这个time_t是从很久以前保留的文件内容中读取的,所以我坚持使用这种"自 unix 时代以来的时间"类型。

std::filesystem::last_write_time返回一个std::filesystem::file_time_type。有没有一种便携式方法可以将file_time_type转换为time_t,或以其他方式便携式比较两个对象?

#include <ctime>
#include <filesystem>
std::time_t GetATimeInSecondsSince1970Epoch()
{
return 1207609200;  // Some time in April 2008 (just an example!)
}
int main()
{
const std::time_t time = GetATimeInSecondsSince1970Epoch();
const auto lastWriteTime = std::filesystem::last_write_time("c:\file.txt");
// How to portably compare time and lastWriteTime?
}

编辑:请注意,last_write_timecppreference.com 的示例代码指出,它假设时钟是实现to_time_t函数的std::chrono::system_clock。这个假设并不总是正确的,也不在我的平台上(VS2017)。

> Fwiw,当C++20到达这里时,便携式解决方案将是:

clock_cast<file_clock>(system_clock::from_time_t(time)) < lastWriteTime

这会将time_t转换为file_time,反之亦然。 这种方法的优点是file_time通常具有比time_t更高的精度。 将file_time转换为time_t将在转换过程中失去该精度,从而有可能使比较不准确。

我遇到了同样的问题,我使用Visual Studio的专用代码解决了它。

在VS的情况下,我使用_wstati64函数(w宽字符,因为Windows在Utf16中编码Unicode路径)和path类的wstring转换。

整个事情都收集在这个函数中:

#if defined ( _WIN32 )
#include <sys/stat.h>
#endif
std::time_t GetFileWriteTime ( const std::filesystem::path& filename )
{
#if defined ( _WIN32 )
{
struct _stat64 fileInfo;
if ( _wstati64 ( filename.wstring ().c_str (), &fileInfo ) != 0 )
{
throw std::runtime_error ( "Failed to get last write time." );
}
return fileInfo.st_mtime;
}
#else
{
auto fsTime = std::filesystem::last_write_time ( filename );
return decltype ( fsTime )::clock::to_time_t ( fsTime );
}
#endif
}

您链接的文章展示了如何做到这一点:通过file_time_type相应clockto_time_t的成员。

从您自己的链接复制粘贴:

auto ftime = fs::last_write_time(p);
std::time_t cftime = decltype(ftime)::clock::to_time_t(ftime); 

如果您的平台没有为您提供system_clock作为file_time_type时钟,那么就不会有便携式解决方案(至少直到file_time_type时钟标准化时C++20)。在此之前,你必须弄清楚它到底是什么时钟,然后通过duration_cast和朋友适当地投射时间。

这是我的polyfill,适用于Windows,MacOS和Linux。不幸的是,Clang和GCC都没有file_clock支持,对Windows的支持也绑定到最近的Windows 10 +版本。请参阅:https://github.com/microsoft/STL/issues/1911

// Copied from Boost: filesystem/src/operations.cpp
// these constants come from inspecting some Microsoft sample code
#ifdef _WIN32
inline std::time_t to_time_t(FILETIME const& ft) BOOST_NOEXCEPT {
uint64_t t = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
t -= 116444736000000000ull;
t /= 10000000u;
return static_cast<std::time_t>(t);
}
#else
// Copied from Stackoverflow
template<typename TP>
std::time_t to_time_t(TP tp) {
using namespace std::chrono;
auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now() + system_clock::now());
return system_clock::to_time_t(sctp);
}
#endif
// Use this polyfill until everyone supports C++20
// This code is highly depended on the implementation
time_t PortableLastWriteTime(const std::string& File) {
#ifdef _WIN32
// We cannot use the C++20 and filesystem due to this incompatibility/error: https://github.com/microsoft/STL/issues/1911
// This problem makes this request to fail on Windows older than Windows 10 (1903) and somehow Server 2019 completely
HANDLE handle = CreateFile(File.c_str(), GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
FILETIME lwt;
if (!::GetFileTime(handle, NULL, NULL, &lwt)) {
CloseHandle(handle);
return 0;
}
CloseHandle(handle);
return to_time_t(lwt);
#elif __GLIBCXX__
auto ftime = std::filesystem::last_write_time(File);
return to_time_t(ftime);
#elif _LIBCPP_VERSION
auto ftime = std::filesystem::last_write_time(File);
return decltype(ftime)::clock::to_time_t(ftime);
#elif __CORRECT_CPP20_VERSION
// This is the correct C++20 usage when compilers have full compatibility
const auto fileTime = std::filesystem::last_write_time(File);
const auto systemTime = std::chrono::clock_cast<std::chrono::system_clock>(fileTime);
const auto time = std::chrono::system_clock::to_time_t(systemTime);
return time;
#else
Unsupported compiler !
#endif
}