I'm looking for a solution from the STL, for dealing with "time of day". I am working on a simple unit test exercise, with behavior depending on whether current time is in the morning, evening or night.
For a first iteration I used a humble integer as a stand-in for some "time object":
using TimeOfDay = int;
constexpr bool isBetween(TimeOfDay in, TimeOfDay min, TimeOfDay max) noexcept {
return in >= min && in <= max;
}
constexpr bool isMorning(TimeOfDay in) noexcept {
return isBetween(in, 6, 12); }
constexpr bool isEvening(TimeOfDay in) noexcept {
return isBetween(in, 18, 22);
}
constexpr bool isNight(TimeOfDay in) noexcept {
return isBetween(in, 22, 24) || isBetween(in, 0, 6);
}
constexpr string_view getGreetingFor(TimeOfDay time) noexcept {
if (isMorning(time)) {
return "Good morning "sv;
}
if (isEvening(time)) {
return "Good evening "sv;
}
if (isNight(time)) {
return "Good night "sv;
}
return "Hello "sv;
}
This works but has a couple of smells:
int
simply isn't the right type to represent a 24 hour clockisNight()
requires an unnecessarily complicated comparison, due to wrapping (22-06)std::chrono::system_clock::now()
returns a std::chrono::time_point
, so my ideal type should probably be something that can be compared to a time_point
, or easily constructed from a time_point
.Any pointers would be very appreciated!
(I am working in Visual Studio with C++Latest (preview of the C++ working draft, so roughly C++23))
I'm going to start with the assumption that you're looking for the local time of day. chrono::system_clock
represents UTC. So I recommend a helper function that takes std::chrono::system_clock::time_point
and returns a std::chrono::system_clock::duration
which represents the time elapsed since the most recent local midnight:
using TimeOfDay = std::chrono::system_clock::duration;
TimeOfDay
get_local_time_of_day(std::chrono::system_clock::time_point tp) noexcept
{
using namespace std::chrono;
auto time = current_zone()->to_local(tp);
return time - floor<days>(time);
}
chrono::current_zone()
returns a pointer to the chrono::time_zone
that your computer is currently set to. This is used to convert the system_clock::time_point
to a local time_point
. This local time_point
is a chrono::time_point
but with a different clock that means "local time".
The expression floor<days>(time)
truncates the time_point
to precision days
, which effectively gives you a pointer to the most recently passed local midnight. Subtracting that from time
gives you a chrono::duration
since the local midnight. This duration
will have the same precision as tp
.
This function can not be made constexpr
because the rules for transforming UTC to local time is in the hands of your politicians and is subject to change (in many countries twice a year!).
Now you can write isMorning
(et al) like this:
bool isMorning(std::chrono::system_clock::time_point in) noexcept {
using namespace std::literals;
return isBetween(get_local_time_of_day(in), 6h, 12h); }
And call it like this:
if (isMorning(system_clock::now())) ...
Your logic for isNight
looks fine to me. And your code for isBetween
can stay like it is (picking up the new definition of TimeOfDay
).