如何将标准::文件系统::file_time_type转换为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 时,出现以下错误:

错误:"to_time_t"不是"std::chrono::time_point<std::文件系统::_file_clock>::时钟"{aka 'std::filesystem::__file_clock'} 的成员

我搜索了一个解决方案,但我找到的是基于 cplusplus.comstd::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++17 中没有完美的方法可以做到这一点。根据实际用例,使用便携式近似值可能足够好。根据我对"如何使用GCC 9将std::filesystem::file_time_type转换为字符串"的回答,我想建议使用那里使用的辅助函数:

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()每个时钟,因此它不是一个精确的、往返保证的解决方案,但它可能对您有用,直到库中的间隙关闭。它基于这样一个事实,即同一时钟的时间点之间很容易差异,并且duration和不同来源的time_point有一个operator+

为了进一步降低相关错误风险的方法,我想指出 C++11 时钟之间的转换,其中进行了一些统计分析 减少可能错误的想法,但在可以接受的情况下,我只使用上面的代码。

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

在 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时间点。

经过一番挖掘,我设法重写了他们给出的示例,它似乎有效; 关键是转换为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));
}

这是从 std::filesystem::file_time_type 到 time_t 的直接转换,截至 2022 年 12 月,它在 MS Visual C++ 2022、Clang 和 gcc 上以 C++ 标准工作。

// ....
#include <filesystem>
#include <chrono>
using namespace std;
// ....
filesystem::file_time_type  myfmtime =
filesystem::last_write_time("myfile.txt");
// file_clock::file_time_type -> utc_clock::time_point
// -> system_clock::time_point -> time_t
chrono::time_point<chrono::system_clock> ftsys;
#ifdef  __GNUC__
ftsys = chrono::file_clock::to_sys(myfmtime);
#else
// MSVC, CLANG
ftsys = chrono::utc_clock::to_sys(
chrono::file_clock::to_utc(myfmtime));
#endif
time_t ftt = chrono::system_clock::to_time_t(ftsys);
char chrbuf[30];
struct tm ftm; ftm = {0};
localtime_s(&ftm, &ftt);
strftime(chrbuf, 30, "%F %T", &ftm);

必须有一些标准的直接转换例程才能将文件时间转换为time_t反之亦然,而无需使用系统时钟类型。

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

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

根据https://libcxx.llvm.org/DesignDocs/FileTimeType.html

file_time_type tp = last_write_time(p);
time_t secs = duration_cast<seconds>(tp.time_since_epoch()).count();

在 gcc 9.5 上测试编译

这在 C++17 中对我有用。使用FileTimeToSystemTime以人类可读的格式返回文件时间。从那里您可以使用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 = %dn", stSystemTime.wYear, stSystemTime.wMonth, stSystemTime.wDay, stSystemTime.wHour, stSystemTime.wMinute);
}

相关内容

最新更新