如何将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()
,因此它不是一个精确的、保证往返的解决方案,但它可能对您有用,直到图书馆关门了。其依据是同一时钟的时间点之间容易产生差异,不同来源的duration
和time_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);
我使用 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()
,因此它不是一个精确的、保证往返的解决方案,但它可能对您有用,直到图书馆关门了。其依据是同一时钟的时间点之间容易产生差异,不同来源的duration
和time_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);