如何比较 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 的相应 clockto_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
}