Reputation: 581
TL;DR: How to use a std::chrono::system_clock::time_point
to compare based on only certain parameters (e.g. I just want hours, minutes and seconds, but not day, month, etc.).
Also: After converting the std::chrono::system_clock::time_point
to a std::tm
, the std::tm.tm_hours
contains a value one higher than originally input to the std::chrono::system_clock::time_point
.
My theoretical approach on getting a std::chrono::system_clock::time_point
to work:
typedef std::chrono::system_clock::time_point TimePoint;
TimePoint MainWindow::createTimePoint(int h, int m)
{
TimePoint createdTime = std::chrono::system_clock::time_point{std::chrono::hours(h) + std::chrono::minutes(m)};
time_t tt = std::chrono::system_clock::to_time_t(createdTime);
tm timeExtracted = *localtime(&tt);
std::cout << "input:\t\t" << "H = " << h << ", M = " << m << std::endl;
std::cout << "timeExtracted:\t" << "H = " << timeExtracted.tm_hour << ", M = " << timeExtracted.tm_min << std::endl;
return createdTime;
}
If I run this, the hours of timeExtracted
are always +1 from the input h
.
Why is that so? And how to fix this? I went over a few other posts that showed this, but they couldnt help me. Probably also because of this:
I think that when I create a TimePoint
, the day, month, etc. is also set to a random value or initiated to a certain value. The point is: I want them to always be the same value, so that my TimePoint
(after converting) basically shows this:
timeExtracted.tm_sec = 0
timeExtracted.tm_min = m
timeExtracted.tm_hour = h
timeExtracted.tm_mon = 0
timeExtracted.tm_wday = 0
timeExtracted.tm_mday = 0
timeExtracted.tm_yday = 0
timeExtracted.tm_year = 0
timeExtracted.tm_isdst = 0
How can I compare two of these TimePoint
utilising using the compare operations of std::chrono on them, but only compare the hour and minute.
If my question is unclear, I'm sorry, it's late in the evening. I'll check again next morning. Thank you.
Upvotes: 2
Views: 951
Reputation: 218770
I'm going to start an answer, but this isn't going to be a complete answer because I'm not yet sure of the complete question. However, I can help.
TimePoint createdTime = system_clock::time_point{hours(h) + minutes(m)};
(I've clipped the std::chrono::
qualifiers so that this is easier to read and discuss)
This creates a time stamp that is 1970-01-01 hh:mm:00 UTC. In a nutshell, system_clock::time_point
is measuring the duration of time (in some units like microseconds or nanoseconds) since New Years 1970, UTC. Technically the above is an approximation, system_clock
doesn't count leap seconds, but we can (and should) ignore that detail for now.
This:
tm timeExtracted = *localtime(&tt);
is going to introduce UTC offset corrections based on your computer's setting for the local time zone. The time zone adjustment rules are (hopefully) going to be based on what was in effect in 1970 in your area.
There exist techniques and libraries for taking a system_clock::time_point
and breaking it up into fields such as {year, month, day, hours, minutes, seconds, microseconds}
. But that conversion also depends on if you want these fields in UTC, local time, or some other arbitrary time zone.
And the very first step is to apply the UTC offset associated with some time zone if desired. It may be that your {h, m}
input needs a UTC offset adjustment prior to putting them into system_clock::time_point
if the intent is that {h, m}
represent local time instead of UTC.
This example will use my free, open-source time zone library, because I feel it is much easier to work with and allows for more readable and expressive code.
This example takes as input a system_clock::time_point
and compares it to a list of open/close times for each day of the week and determines if the input time is inside or outside of those time-of-day ranges for the weekday associated with the input time t
. The store hours are presumed to be stated with respect to the store's local time zone, which is also the current time zone set for the computer running this code.
#include "date/tz.h"
#include <algorithm>
#include <cassert>
#include <chrono>
bool
is_store_open_at(std::chrono::system_clock::time_point tp)
{
using namespace date;
using namespace std::chrono;
struct day_schedule
{
weekday wd;
minutes open;
minutes close;
};
// hours are expressed in terms of local time
static constexpr day_schedule store_hours[]
{
// week day open-time close-time
{Monday, 0h, 0h}, // closed all day
{Tuesday, 8h, 18h},
{Wednesday, 8h, 18h},
{Thursday, 8h, 18h},
{Friday, 8h, 18h},
{Saturday, 8h, 15h+30min},
{Sunday, 9h+30min, 15h}
};
auto local_tp = current_zone()->to_local(tp);
auto local_day = floor<days>(local_tp);
auto local_time_of_day = local_tp - local_day;
weekday local_weekday{local_day};
auto ds = std::find_if(std::begin(store_hours), std::end(store_hours),
[local_weekday](day_schedule const& x)
{
return x.wd == local_weekday;
});
assert(ds != std::end(store_hours));
return ds->open <= local_time_of_day && local_time_of_day < ds->close;
}
#include <iostream>
int
main()
{
std::cout << is_store_open_at(std::chrono::system_clock::now()) << '\n';
}
The function begins by defining some handy data structures to store the open and close times for each day of the week. The open
and close
members of day_schedule
measure "minutes since midnight" in local time.
The input time tp
is in terms of UTC, since its type is system_clock::time_point
. This is not currently specified by the C++ standard, but will be for next year's C++20.
I've edited to use slightly simpler syntax to eliminate the zoned_seconds
is used to convert the UTC time t
into local time according to the computers time zone setting obtained by calling current_zone()
. I've truncated t
to seconds to simplify some of the syntax. This isn't strictly necessary.zoned_seconds
. zoned_seconds
can be really useful in other examples, but in this one was more trouble than it was worth. auto local_tp = current_zone()->to_local(tp)
is a simpler way to translate UTC to a local time point.
local_tp
is a chrono::time_point
that is considered "local time", and is distinct from the family of chrono::time_point
s associated with system_clock
. The advantage of doing this is so that if local time and UTC time are accidentally mixed, it is a compile-time error.
local_days
is simply local_tp
truncated to days
precision. It is still a chrono::time_point
, just a coarse one that points to the beginning of the day as described by the local time zone.
The time duration since the local midnight is simply local_tp - local_day
.
The day of the week (as defined by the local time zone) can be obtained by converting local_day
to type weekday
. This is the local day of the week associated with tp
.
Now it is a simple matter to search store_hours
for the entry that matches local_weekday
.
The store is open if local_time_of_day
is at or past the open
time and has not yet reached the close
time.
If the "store hours" are specified in UTC instead of local time, then this program simplifies somewhat, but is still similar.
Upvotes: 1