Search code examples
c++datec++-chrono

How to generate date::local_time from std::chrono_time_point


I'm using Howard Hinnant's time zone library.

https://howardhinnant.github.io/date/tz.html

My question: Is it possible to construct a date::local_time object from a std::chrono::time_point?

What I want to to:

// 'tp' exists and is some std::chrono::time_point object 
auto locTime = date::local_time<std::chrono::milliseconds>(tp);

The constructor for this doesn't exist so a get a compilation error. How do I do this (in nice and clean C++17)?

Background: My ultimate goal is to compare a std::filesystem::file_time_type to a date::local_time<std::chrono::milliseconds>.

I do

auto fileTimeTp = std::chrono::clock_cast<std::chrono::system_clock>(someDirectoryEntryObject.last_write_time());

which gives my my std::chrono::time_point for the file date, but that's where I'm stuck...


Solution

  • This is a two part answer ...

    Part 1

    My question: Is it possible to construct a date::local_time object from a std::chrono::time_point?

    I'm going to assume that std::chrono::time_point refers to std::chrono::system_clock::time_point (each clock has its own family of std::chrono::time_point).

    Yes, it is possible. Background: system_clock::time_point is defined as Unix Time which is a close approximation to UTC. So to go from system_clock::time_point (also know as sys_time in the date library / C++20) to local_time, you need to pair the sys_time with a time_zone. This could be your computer's current local time zone, or any other IANA time zone.

    To get the computer's current local time zone:

    auto tz = date::current_zone();
    

    The type of tz is a date::time_zone const*. time_zone has a member function called to_local that will translate a sys_time into a local_time:

    auto locTime = tz->to_local(system_clock::now());
    

    The precision of locTime will match whatever the precision of the input sys_time is.

    If you would like to use some other time zone, then you can use date::locate_zone to get a date::time_zone const* to that time zone.

    auto locTime = date::locate_zone("America/New_York")->to_local(system_clock::now());
    

    Part 2

    My ultimate goal is to compare a std::filesystem::file_time_type to a date::local_time<std::chrono::milliseconds>.

    Ah, this won't really involve local_time at all. And unfortunately, file_clock is not implemented in the time_zone library.

    In C++20, this will be quite easy: Given a file_time and a sys_time you can convert either to the other using clock_cast:

    if (clock_cast<system_clock>(ftp) >= system_clock::now())
        ...
    

    However in C++17 it is harder, and non-portable. The the time_zone library makes it easier, but not easy.

    You first have to deduce the epoch of std::filesystem::file_time_type on your platform. This will be different depending on which implementation of std::filesystem::file_time_type you are using.

    Existing epochs include:

    * 1970-01-01 00:00:00 UTC
    * 1601-01-01 00:00:00 UTC
    * 2174-01-01 00:00:00 UTC
    

    Then you subtract the sys_time epoch (sys_days{1970_y/1/1}) and the file_time epoch (e.g. sys_days{1601_y/1/1}), and add/subtract that epoch to convert from one measure to the other.

    For example:

    constexpr auto diff = sys_days{1970_y/1/1} - sys_days{1601_y/1/1};
    file_time_type ftp = ...
    system_clock::time_point tp{ftp.time_since_epoch() - diff};
    

    That's unfortunately quite messy, and I'm looking forward to clock_cast working with file_clock in C++20.