如何将std::filesystem::file_time_type转换成time_t?

How to convert std::filesystem::file_time_type to time_t?

我使用 MSVC2015 为 windows 编写了一个解决方案,其中以下代码将 std::filesystem::last_write_time 结果转换为 time_t:

time_t ftime = std::file_time_type::clock::to_time_t(fs::last_write_time("/Path/filename"))

效果很好。然后,当我尝试使用 gcc 9.3 (-std=C++2a) 将解决方案移植到 Linux 时,出现以下错误:

Error: 'to_time_t' is not member of 'std::chrono::time_point::clock' {aka 'std::filesystem::__file_clock'}

我搜索了一个解决方案,但我找到的是基于 cplusplus.com 的 std::filesystem::last_write_time 示例中包含的解决方案。解决方法如下:

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

不幸的是,它对我不起作用。实际上,该示例有一条评论说它不适用于 MSVC(在 MSVC2015 上工作)或 GCC 9; C++20 将允许可移植输出。

现在,我卡住了...如何使用 gcc 进行此转换?

在 C++20 之前?没有可移植的方法来做到这一点。它在那个版本的 Visual Studio 中工作,因为那个版本的 VS 文件系统实现恰好使用 system_clock 作为文件系统时钟类型。这不是 C++ 标准所要求的,但它是允许的。所以你的代码恰好可以工作。

C++20 之前,没有将两个时钟对齐在一起的机制,因此一个时钟的时间可以转换为另一个时钟的时间。因此,如果文件系统时钟 不是 system_clock,那你就不走运了。您必须使用有关该实现如何实现其文件系统时钟的知识(基本上知道它使用的是什么纪元)来编写特定于实现的代码,以便您可以手动将其转换为 system_clock::time_point.

C++20 为 system_clock 提供了一个在所有实现(UNIX 时间)中使用的固定纪元,并要求 file_clock 能够将其时间点转换为 system_clock时间点。

如前所述,在 C++17 中没有完美的方法来做到这一点。根据实际用例,使用便携式近似值可能就足够了。根据我对 "How to convert std::filesystem::file_time_type to a string using GCC 9" 的回答,我想建议那里使用的辅助函数:

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);
}

请注意,它在每个时钟上调用 now(),因此它不是一个精确的、保证往返的解决方案,但它可能对您有用,直到图书馆关门了。其依据是同一时钟的时间点之间容易产生差异,不同来源的durationtime_point存在operator+

关于进一步降低相关错误风险的方法,我想指出 进行了一些统计分析 减少可能的错误的想法,但如果可以接受,我只使用上面的代码。

这不是问题的直接答案,可能不是可移植的解决方案, 但我遇到了将 std::filesystem::file_time_type 转换为 std::chrono::system_clock:: 的类似问题time_point(可以很容易地转换为 time_t)在 Visual Studio 2017 年,这对我有用,可能会帮助也在搜索中的其他人...

using namespace std::chrono;

// a dummy file_time_type to work with
const auto ftp = std::filesystem::file_time_type::clock::now();

// get the ticks and subtract the file time epoch adjustment
const auto ticks = ftp.time_since_epoch().count() - __std_fs_file_time_epoch_adjustment;

// create a time_point from ticks
const auto tp = system_clock::time_point(system_clock::time_point::duration(ticks));

// finaly convert to time_t
std::time_t tt = system_clock::time_point::clock::to_time_t(tp);

经过一些挖掘,我设法重写了他们给出的示例,并且它似乎有效;正在转换为 system_clock 然后转换为 time_t.

的密钥
#include <iostream>
#include <chrono>
#include <iomanip>
#include <fstream>
#include <filesystem>
namespace fs = std::filesystem;
using namespace std::chrono_literals;
int main()
{
  fs::path p = fs::current_path() / "example.bin";
  std::ofstream(p.c_str()).put('a'); // create file
  auto ftime = fs::last_write_time(p);
  auto cftime = std::chrono::system_clock::to_time_t(std::chrono::file_clock::to_sys(ftime));

  std::cout << "File write time is " << std::asctime(std::localtime(&cftime)) << '\n';

  fs::last_write_time(p, ftime + 1h); // move file write time 1 hour to the future
  ftime = fs::last_write_time(p);     // read back from the filesystem

  cftime = std::chrono::system_clock::to_time_t(std::chrono::file_clock::to_sys(ftime));
  std::cout << "File write time is " << std::asctime(std::localtime(&cftime)) << '\n';
  fs::remove(p);
}

另一个不直接的解决方案:如果你恰好在使用boost...

    inline std::string filestamp(const std::string& path) {
        std::time_t t = boost::filesystem::last_write_time(path);
        return std::asctime(std::localtime(&t));
    }

这在 C++17 中对我有用。使用 FileTimeToSystemTime 到 return 人类可读格式的文件时间。从那里你使用 mktime 转换为 time_t

auto filetime = entry.last_write_time();
SYSTEMTIME  stSystemTime;
if (FileTimeToSystemTime((const FILETIME*)&filetime, &stSystemTime))
{
printf("Year = %d,  Month = %d,  Day = %d,  Hour = %d,  Minute = %d\n", stSystemTime.wYear, stSystemTime.wMonth, stSystemTime.wDay, stSystemTime.wHour, stSystemTime.wMinute);
}

C++20 解决方案

const auto fileTime = std::filesystem::last_write_time(filePath);
const auto systemTime = std::chrono::clock_cast<std::chrono::system_clock>(fileTime);
const auto time = std::chrono::system_clock::to_time_t(systemTime);