1

A while ago, I managed to do this, to go from filesystem to system clocks, thanks to SO:

int64_t toUSsecSinceEpochUTC(std::filesystem::file_time_type ftime) {
    // see https://stackoverflow.com/a/35282833/103724
    using namespace std::chrono;
    auto sys_now = system_clock::now();
    auto fil_now = decltype(ftime)::clock::now(); // i.e. C++20's file_clock
    auto sys_ftime = time_point_cast<system_clock::duration>(ftime - fil_now + sys_now);
    auto sys_ftime_usec = time_point_cast<microseconds>(sys_ftime);
    return sys_ftime_usec.time_since_epoch().count();
}

But now I want to do the reverse, and I'm struggling... here's my feeble attempt:

std::filesystem::file_time_type fromUSsecSinceEpochUTC(int64_t usec_utc) {
    std::filesystem::file_time_type ftime;

    using namespace std::chrono;
    auto sys_now = system_clock::now();
    auto fil_now = decltype(ftime)::clock::now(); // i.e. C++20's file_clock

    std::chrono::microseconds usec(usec_utc);
    ftime = ????
    return ftime;
}

MSVC2019 complains on auto res = sys_now - usec + fil_now; with

error C2676: binary '+': 'std::chrono::time_point<std::chrono::system_clock,std::chrono::duration<std::chrono::system_clock::rep,std::chrono::system_clock::period>>' does not define this operator or a conversion to a type acceptable to the predefined operator

i.e. the code from https://stackoverflow.com/a/35282833/103724 between steady and system clocks does not appear to work for the filesystem clock. Although it could be just me not following.

Any <chrono> expert who might be able to help?

Laurel
  • 5,965
  • 14
  • 31
  • 57
ddevienne
  • 1,802
  • 2
  • 17
  • 28

1 Answers1

1

To fill out fromUSsecSinceEpochUTC you would:

std::filesystem::file_time_type
fromUSsecSinceEpochUTC(int64_t usec_utc) {
    std::filesystem::file_time_type ftime;

    using namespace std::chrono;
    auto sys_now = system_clock::now();
    auto fil_now = decltype(ftime)::clock::now(); // i.e. C++20's file_clock

    microseconds usec(usec_utc);
    time_point<system_clock, microseconds> tp_sys{usec};
    return tp_sys - sys_now + fil_now;
}

That being said, the relationship between the epoch of system_clock and file_clock is going to be a constant. I believe the Windows file_clock epoch is 1601-01-01 00:00:00 UTC. The difference between that and the system_clock epoch (1970-01-01 00:00:00 UTC) is 13,4774 days or 3'234'576h.

This knowledge allows you to write conversions (for Windows) without calls to now().

std::filesystem::file_time_type
to_file_time(std::chrono::system_clock::time_point sys_tp)
{
    using namespace std::literals;
    return std::filesystem::file_time_type{sys_tp.time_since_epoch() + 3'234'576h};
}

std::chrono::system_clock::time_point
to_sys_time(std::filesystem::file_time_type f_tp)
{
    using namespace std::literals;
    return std::chrono::system_clock::time_point{f_tp.time_since_epoch() - 3'234'576h};
}

Above I've taken advantage of the a-prior knowledge that file_clock::duration is the same type as system_clock::duration on Windows. Add casts as necessary or desired.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • I do most of my dev on Windows, but the code needs to be portable to Linux/GCC9, and also Linux/Intel too (not Clang yet). Thus I can't use the second solution as-is. – ddevienne Sep 19 '22 at 07:21
  • Writing a round-trip test, I'm unsurprisingly finding the input/output file times different very slightly. `last_modified_ {_MyDur={_MyRep=132357530193401596 } }` on input, vs `last_modified_ {_MyDur={_MyRep=132357530193401591 } }` on output. Most likely from the calls to `now()`. How would you tweak the code to record the Epoch skew between the two clocks once-and-for-all in the process, using a static? – ddevienne Sep 19 '22 at 08:14
  • I wouldn't, mainly because of the problem of accuracy you have discovered. Instead I would discover gcc's `file_clock` epoch, and `#ifdef` on compiler to supply the correct epoch for each platform. On gcc I believe the epoch is 2174-01-01 00:00:00 UTC. – Howard Hinnant Sep 19 '22 at 14:44
  • 1
    I actually ended up doing that, before reading your comment... I computed the `fil_now.time_since_epoch() - sys_now.time_since_epoch()` a number of times (101), and took the median, then `std::chrono::round()` that to the millisecond, to get a consistent value across processes (was varying at the microsecond level...). That 2174 Epoch explains why I was getting `-6437664000000msec` on Linux/GCC9! Thanks for the heads up. – ddevienne Sep 19 '22 at 15:23