如何比较 time_t 和 std::filesystem::file_time_type
How to compare time_t and std::filesystem::file_time_type
我正在将一些代码从 boost::filesystem
转换为 std::filesystem
。以前的代码使用 boost::filesystem::last_write_time()
其中 returns 一个 time_t
所以直接与我已经拥有的 time_t
对象进行比较是微不足道的。顺便说一下,我持有的这个 time_t
是 读取很久以前保存的文件内容 ,所以我坚持使用这个 "time since unix epoch" 类型。
std::filesystem::last_write_time
returns a 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?
}
编辑:请注意sample code at cppreference.com for last_write_time
states that it's assuming the clock is a std::chrono::system_clock
实现了to_time_t
功能。这个假设并不总是正确的,也不在我的平台上 (VS2017)。
您 link 编辑的文章显示了如何执行此操作:通过 file_time_type
的相应 clock
的 to_time_t
成员。
复制粘贴你自己的link:
auto ftime = fs::last_write_time(p);
std::time_t cftime = decltype(ftime)::clock::to_time_t(ftime);
如果您的平台没有给您 system_clock
作为 file_time_type
的时钟,那么就没有可移植的解决方案(至少在 C++20 之前 file_time_type
时钟是标准化的)。在那之前,你必须弄清楚它到底是什么时钟,然后通过 duration_cast
和朋友适当地施放时间。
顺便说一句,当 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 路径)和 wstring
转换path
class.
全部内容都集中在这个函数中:
#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
}
这是我的 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 Whosebug
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
}
我正在将一些代码从 boost::filesystem
转换为 std::filesystem
。以前的代码使用 boost::filesystem::last_write_time()
其中 returns 一个 time_t
所以直接与我已经拥有的 time_t
对象进行比较是微不足道的。顺便说一下,我持有的这个 time_t
是 读取很久以前保存的文件内容 ,所以我坚持使用这个 "time since unix epoch" 类型。
std::filesystem::last_write_time
returns a 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?
}
编辑:请注意sample code at cppreference.com for last_write_time
states that it's assuming the clock is a std::chrono::system_clock
实现了to_time_t
功能。这个假设并不总是正确的,也不在我的平台上 (VS2017)。
您 link 编辑的文章显示了如何执行此操作:通过 file_time_type
的相应 clock
的 to_time_t
成员。
复制粘贴你自己的link:
auto ftime = fs::last_write_time(p);
std::time_t cftime = decltype(ftime)::clock::to_time_t(ftime);
如果您的平台没有给您 system_clock
作为 file_time_type
的时钟,那么就没有可移植的解决方案(至少在 C++20 之前 file_time_type
时钟是标准化的)。在那之前,你必须弄清楚它到底是什么时钟,然后通过 duration_cast
和朋友适当地施放时间。
顺便说一句,当 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 路径)和 wstring
转换path
class.
全部内容都集中在这个函数中:
#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
}
这是我的 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 Whosebug
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
}