I'm trying to find the best way to see if the current time is before a specified time. Say I want to see if it's before 14:32. What's the best way to do this in C++? Ideally I'd be able to build some time object that represents 14:32, then compare it with the current time as some object. This is what I'm doing right now. Pretty messy and uses 3 different representations of time.
int hour_ = 14;
int min_ = 32;
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t tt = std::chrono::system_clock::to_time_t(now);
std::tm utc_tm = *gmtime(&tt);
if ((utc_tm.tm_hour < hour_) || (utc_tm.tm_hour == hour_ && utc_tm.tm_min < min_) ) {
std::cout << "It's before " << hour_ << ":" << min_ << std::endl;
}
Here is how you can do it in C++20. Later I will show how to convert this to use a free, open-source C++20 chrono preview library which works with C++11/14/17.
#include <chrono>
bool
is_now_before(std::chrono::minutes local_config_tod)
{
using namespace std::chrono;
auto tz = current_zone();
auto now = system_clock::now();
auto local_day = floor<days>(zoned_time{tz, now}.get_local_time());
auto utc_config = zoned_time{tz, local_day + local_config_tod}.get_sys_time();
return now < utc_config;
}
The parameter has type minutes
which will be interpreted to be the local time of day in minutes. For example 14:32 is represented by minutes{872}
. This representation is compact (one integer), and it is trivial to convert {hours, minutes}
to just minutes
(shown below).
current_zone()
gets the computer's current local time zone. This information is needed twice in this function, so it is best to just get it once. Not only does this save the result, but it also sidesteps the problem of the local time zone changing out from under you (between multiple calls) in a mobile device.
Next the current time is obtained (just once) via system_clock
. This gives the current time in UTC.
Now we have a choice:
Doing the comparison in UTC is less error prone in the corner case that the UTC offset is changing in the current local day (such as going on or off of daylight saving).
To convert the local config time-of-day (local_config_tod
) to a UTC time_point
one first has to find out what the current local day is. In general this can be different than the current UTC day. So the current UTC now
has to be converted to local time, and then truncated to days
-precision:
auto local_day = floor<days>(zoned_time{tz, now}.get_local_time());
Now a local time_point
can be created simply by summing local_day
and local_config_tod
. This local time_point
can then be converted back into UTC (a time_point
based on system_clock
but with seconds
precision):
auto utc_config = zoned_time{tz, local_day + local_config_tod}.get_sys_time();
The line of code above handles the corner cases for you. If there is not a unique (one-to-one) mapping from local time to UTC, then an exception is thrown. The .what()
of the exception type will have a detailed description about how this mapping is either ambiguous, or non-existent.
Assuming the above mapping does not throw an exception, you can simply compare these two UTC time_point
s:
return now < utc_config;
The precision of this comparison is with whatever precision your system_clock
has (typically microseconds to nanoseconds).
This can be exercised like so:
int hour_ = 14;
int min_ = 32;
using namespace std::chrono;
auto b = is_now_before(hours{hour_} + minutes{min_});
If 14 and 32 are literals (and you're in C++14 or later), it can be shortened to:
auto b = is_now_before(14h + 32min);
If you are using a standard prior to C++17, the zoned_time
constructions will require an explicit template parameter:
auto local_day = floor<days>(zoned_time<system_clock::duration>{tz, now}.get_local_time());
auto utc_config = zoned_time<minutes>{tz, local_day + local_config_tod}.get_sys_time();
If you would like to use the free, open-source C++20 chrono preview library, add #include "date/tz.h"
and using namespace date;
. Some installation is required.
If you would like to avoid an exception in the case that local_day + local_config_tod
does not have a unique mapping to UTC, that is also possible with minor changes to is_now_before
. But you will have to decide things such as: Do I want to compare against the first or second local_config_tod
of the local_day
(in case the UTC offset has been decreased).
On re-reading your question it occurred to me that I may have misread your question. If 14:32 is UTC, then things get much, much simpler! And rather than removing my answer showing the local 14:32 interpretation, I thought it would be better to add this, so future readers could pick either solution.
Assuming the config is a UTC time, then time zones play no role at all:
#include <chrono>
bool
is_now_before(std::chrono::minutes utc_config_tod)
{
using namespace std::chrono;
auto now = system_clock::now();
auto utc_day = floor<days>(now);
return now < utc_day + utc_config_tod;
}
The current day in UTC is simply:
auto utc_day = floor<days>(now);
And now the config date-time is simply utc_day + utc_config_tod
. This is just drop-dead simple.
If you can't use C++20, the free, open-source C++20 chrono preview library is also much simpler now as it is header-only, requiring no installation at all. Just #include "date/date.h"
and add using namespace date;
.