How to call std::chrono::is_am with a time_point?

43 Views Asked by At

C++20 adds these free functions to namespace std::chrono:

constexpr bool is_am(const hours& h) noexcept;

Returns: 0h <= h && h <= 11h.

constexpr bool is_pm(const hours& h) noexcept;

Returns: 12h <= h && h <= 23h.

But I don't have hours. I have a std::chrono::time_point. How can I find out if my time_point is am or pm?

1

There are 1 best solutions below

0
Howard Hinnant On

In general, you can't. For example steady_clock::time_point has no relationship whatsoever to human created times, dates or calendars. It is like a stopwatch. So it would be senseless to try to find out if a steady_clock::time_point is am or pm.

However it does make sense for those time_points associated with system_clock (UTC, aka std::chrono::sys_time<Duration>), and those associated with a local time (std::chrono::local_time<Duration>).

To see how to do this, it is instructive to form 4 overloads:

template <class Rep, class Ratio>
bool
is_am(std::chrono::sys_time<std::chrono::duration<Rep, Ratio>> tp);

template <class Rep, class Ratio>
bool
is_am(std::chrono::local_time<std::chrono::duration<Rep, Ratio>> tp);

template <class Rep, class Ratio>
bool
is_pm(std::chrono::sys_time<std::chrono::duration<Rep, Ratio>> tp);

template <class Rep, class Ratio>
bool
is_pm(std::chrono::local_time<std::chrono::duration<Rep, Ratio>> tp);

However, all of these are very similar. So I'll just show how to do the first. And to derive the first, it is instructive to first break it into tiny bits at a time. But in the end, it will wrap up into one line of code.

template <class Rep, class Ratio>
bool
is_am(std::chrono::sys_time<std::chrono::duration<Rep, Ratio>> tp)
{
    using namespace std::chrono;
    auto tpd = floor<days>(tp);
    auto tod = tp - tpd;
    auto h = floor<hours>(tod);
    return is_am(h);
}
  • It is convenient to issue a function-local using directive to cut down on the verbosity of having std::chrono:: sprinkled all over the place. If you prefer the sprinkling, skip the using directive.

  • First start the process of deconstructing the time_point into fields such as {y, m, d, H, M, S}. Though only the hours field is really needed. The first step in this to extract the date as a days-precision time_point, labeled tpd above.

  • Next, find the time-of-day (tod). It is just the "date_time - date".

  • The hours can be extracted from the time-of-day using the floor function again.

  • Finally just return is_am on the extracted hours.

This can be more concisely wrapped up as just:

template <class Rep, class Ratio>
bool
is_am(std::chrono::sys_time<std::chrono::duration<Rep, Ratio>> tp)
{
    using namespace std::chrono;
    return is_am(floor<hours>(tp - floor<days>(tp)));
}

The exact same implementation can be used for the local_time overload. And when you make the is_pm overloads, just remember to switch out is_am for is_pm in the definitions.

These can be called like this:

cout << is_am(system_clock::now()) << '\n';
cout << is_am(current_zone()->to_local(system_clock::now())) << '\n';
cout << is_pm(system_clock::now()) << '\n';
cout << is_pm(current_zone()->to_local(system_clock::now())) << '\n';