Как преобразовать `std::filesystem::file_time_type` в строку, используя GCC 9

Я пытаюсь отформатировать время изменения файла в виде строки (UTC). Следующий код компилируется с GCC 8, но не GCC 9.

#include <chrono>
#include <filesystem>
#include <iomanip>
#include <iostream>
#include <sstream>

namespace fs = std::filesystem;

int main() {
    fs::file_time_type file_time = fs::last_write_time(__FILE__);
    std::time_t tt = decltype(file_time)::clock::to_time_t(file_time);
    std::tm *gmt = std::gmtime(&tt);
    std::stringstream buffer;
    buffer << std::put_time(gmt, "%A, %d %B %Y %H:%M");
    std::string formattedFileTime = buffer.str();
    std::cout << formattedFileTime << '\n';
}

Я пробовал оба decltype(file_time)::clock а также std::chrono::file_clockс использованием C++17 и C++ 20, но ни один из них не сработал.

$ g++-9 -std=c++2a -Wall -Wextra -lstdc++fs file_time_type.cpp

file_time_type.cpp: In function ‘int main()’:
file_time_type.cpp:12:50: error: ‘to_time_t’ is not a member of ‘std::chrono::time_point<std::filesystem::__file_clock>::clock’ {aka ‘std::filesystem::__file_clock’}
   12 |     std::time_t tt = decltype(file_time)::clock::to_time_t(file_time);
      |                                                  ^~~~~~~~~

В примере на https://en.cppreference.com/w/cpp/filesystem/file_time_type упоминается, что он не работает на GCC 9, потому что C++ 20 позволит переносить вывод, но я понятия не имею, как его получить работает. Разве это не должно работать с GCC 9, если я просто не использую C++20?

Я бы предпочел решение C++17 с GCC 9, если это возможно.

0 ответов

Как system_clock имеет to_time_t, самый простой способ - преобразовать в него. Это не идеально (из-за проблем с точностью), но в большинстве случаев достаточно хорошо, и то, что я использую в MSVC:

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

Обновление: поскольку я еще не могу комментировать, некоторые пояснения относительно того, почему это работает и что может быть проблемой: это работает, потому что разница между двумя точками времени одних и тех же часов проста, а для второй части существует дополнительный шаблонoperator+по длительности и временной точки из различных источников (2) и коэффициент различий будет заботиться о вstd::common_type.

Остается проблема в том, что два вызова now()не одновременно, существует небольшой риск появления небольшой ошибки преобразования из-за разницы во времени между этими вызовами. Существует еще один вопрос о преобразованиях часов C++11, в котором гораздо более подробно рассматриваются вероятности ошибок и приемы для уменьшения ошибки, но если вам не нужно преобразование в оба конца со сравнением результатов, а просто нужно отформатировать время штамп, этого меньшего решения должно быть достаточно.

Итак, чтобы завершить ответ на исходный вопрос:

#include <chrono>
#include <filesystem>
#include <iomanip>
#include <iostream>
#include <sstream>

namespace fs = std::filesystem;

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

int main() {
    fs::file_time_type file_time = fs::last_write_time(__FILE__);
    std::time_t tt = to_time_t(file_time);
    std::tm *gmt = std::gmtime(&tt);
    std::stringstream buffer;
    buffer << std::put_time(gmt, "%A, %d %B %Y %H:%M");
    std::string formattedFileTime = buffer.str();
    std::cout << formattedFileTime << '\n';
}
Другие вопросы по тегам