It's unbelievable how difficult the above is to accomplish in C++. I'm looking for a way to do this as efficiently as possible while still maintaining millisecond precision.
The solutions I have so far have either required a lot of code and function calls making the implementation slow, or they require me to change the code twice a year to account for daylight savings time.
The computer this will be running on is synced using ntp and should have direct access to the local time adjusted for DST. Can somebody with expertise on this share some solutions?
My platform is CentOS5, g++ 4.1.2, Boost 1.45, solution doesn't need to be portable, can be platform specific. It just needs to be quick and avoid twice a year code changing.
New answer for old question.
Rationale for new answer: We have better tools now.
I'm assuming the desired result is "actual" milliseconds since the local midnight (getting the correct answer when there has been a UTC offset change since midnight).
A modern answer based on <chrono> and using this free, open-source library is very easy. This library has been ported to VS-2013, VS-2015, clang/libc++, macOS, and linux/gcc.
In order to make the code testable, I'm going to enable an API to get the time since midnight (in milliseconds) from any std::chrono::system_clock::time_point in any IANA time zone.
std::chrono::milliseconds
since_local_midnight(std::chrono::system_clock::time_point t,
const date::time_zone* zone);
And then to get the current time since midnight in the local time zone is easy to write on top of this testable primitive:
inline
std::chrono::milliseconds
since_local_midnight()
{
return since_local_midnight(std::chrono::system_clock::now(),
date::current_zone());
}
Writing the meat of the matter is relatively straight-forward:
std::chrono::milliseconds
since_local_midnight(std::chrono::system_clock::time_point t,
const date::time_zone* zone)
{
using namespace date;
using namespace std::chrono;
auto zt = make_zoned(zone, t);
zt = floor<days>(zt.get_local_time());
return floor<milliseconds>(t - zt.get_sys_time());
}
The first thing to do is create a zoned_time which really does nothing at all but pair zone and t. This pairing is mainly just to make the syntax nicer. It actually doesn't do any computation.
The next step is to get the local time associated with t. That is what zt.get_local_time() does. This will have whatever precision t has, unless t is coarser than seconds, in which case the local time will have a precision of seconds.
The call to floor<days> truncates the local time to a precision of days. This effectively creates a local_time equal to the local midnight. By assigning this local_time back to zt, we don't change the time zone of zt at all, but we change the local_time of zt to midnight (and thus change its sys_time as well).
We can get the corresponding sys_time out of zt with zt.get_sys_time(). This is the UTC time which corresponds to the local midnight. It is then an easy process to subtract this from the input t and truncate the results to the desired precision.
If the local midnight is non-existent, or ambiguous (there are two of them), this code will throw an exception derived from std::exception with a very informative what().
The current time since the local midnight can be printed out with simply:
std::cout << since_local_midnight().count() << "ms\n";
To ensure that our function is working, it is worthwhile to output a few example dates. This is most easily done by specifying a time zone (I'll use "America/New_York"), and some local date/times where I know the right answer. To facilitate nice syntax in the test, another since_local_midnight helps:
inline
std::chrono::milliseconds
since_local_midnight(const date::zoned_seconds& zt)
{
return since_local_midnight(zt.get_sys_time(), zt.get_time_zone());
}
This simply extracts the system_clock::time_point and time zone from a zoned_time (with seconds precision), and forwards it on to our implementation.
auto zt = make_zoned(locate_zone("America/New_York"), local_days{jan/15/2016} + 3h);
std::cout << zt << " is "
<< since_local_midnight(zt).count() << "ms after midnight\n";
This is 3am in the middle of the Winter which outputs:
2016-01-15 03:00:00 EST is 10800000ms after midnight
and is correct (10800000ms == 3h).
I can run the test again just by assigning a new local time to zt. The following is 3am just after the "spring forward" daylight saving transition (2nd Sunday in March):
zt = local_days{sun[2]/mar/2016} + 3h;
std::cout << zt << " is "
<< since_local_midnight(zt).count() << "ms after midnight\n";
This outputs:
2016-03-13 03:00:00 EDT is 7200000ms after midnight
Because the local time from 2am to 3am was skipped, this correctly outputs 2 hours since midnight.
An example from the middle of Summer gets us back to 3 hours after midnight:
zt = local_days{jul/15/2016} + 3h;
std::cout << zt << " is "
<< since_local_midnight(zt).count() << "ms after midnight\n";
2016-07-15 03:00:00 EDT is 10800000ms after midnight
And finally an example just after the Fall transition from daylight saving back to standard gives us 4 hours:
zt = local_days{sun[1]/nov/2016} + 3h;
std::cout << zt << " is "
<< since_local_midnight(zt).count() << "ms after midnight\n";
2016-11-06 03:00:00 EST is 14400000ms after midnight
If you want, you can avoid an exception in the case that midnight is non-existent or ambiguous. You have to decide before hand in the ambiguous case: Do you want to measure from the first midnight or the second?
Here is how you would measure from the first:
std::chrono::milliseconds
since_local_midnight(std::chrono::system_clock::time_point t,
const date::time_zone* zone)
{
using namespace date;
using namespace std::chrono;
auto zt = make_zoned(zone, t);
zt = make_zoned(zt.get_time_zone(), floor<days>(zt.get_local_time()),
choose::earliest);
return floor<milliseconds>(t - zt.get_sys_time());
}
If you want to measure from the second midnight, use choose::latest instead. If midnight is non-existent, you can use either choose, and it will measure from the single UTC time point that borders the local time gap that midnight is in. This can all be very confusing, and that's why the default behavior is to just throw an exception with a very informative what():
zt = make_zoned(locate_zone("America/Asuncion"), local_days{sun[1]/oct/2016} + 3h);
std::cout << zt << " is "
<< since_local_midnight(zt).count() << "ms after midnight\n";
what():
2016-10-02 00:00:00.000000 is in a gap between
2016-10-02 00:00:00 PYT and
2016-10-02 01:00:00 PYST which are both equivalent to
2016-10-02 04:00:00 UTC
If you use the choose::earliest/latest formula, instead of an exception with the above what(), you get:
2016-10-02 03:00:00 PYST is 7200000ms after midnight
If you want to do something really tricky like use choose for non-existent midnights, but throw an exception for ambiguous midnights, that too is possible:
auto zt = make_zoned(zone, t);
try
{
zt = floor<days>(zt.get_local_time());
}
catch (const date::nonexistent_local_time&)
{
zt = make_zoned(zt.get_time_zone(), floor<days>(zt.get_local_time()),
choose::latest);
}
return floor<milliseconds>(t - zt.get_sys_time());
Because hitting such a condition is truly rare (exceptional), the use of try/catch is justified. However if you want to do it without throwing at all, there exists a low-level API within this library to achieve that.
Finally note that this long winded answer is really about 3 lines of code, and everything else is about testing, and taking care of rare exceptional cases.
It really depends on why you need "milliseconds since midnight" and what you plan to use it for.
Having said that, you need to take into account the fact that 3am doesn't really mean 3 hours since midnight, when DST is involved. If you really need "milliseconds since midnight" for some reason, you can get one Epoch time at midnight, another at 3am, and subtract the two.
But again, the notion of "midnight" may not be that stable in some cases; if a region's rule is to fall back from 1am to midnight when DST ends, you have two midnights within a day.
So I'm really doubtful of your dependence on "midnight". Typically, those broken-down times are for display and human understanding only, and all internal timekeeping is done with Epoch times.
If you're on Linux, gettimeofday gives the number of seconds/microseconds since the Epoch, which may help. But this really doesn't have anything to do with DST, since DST matters only with broken-down times (i.e. year, month, day, hour, minute, second).
To get the broken-down time, use gmtime or localtime with the "seconds" part of the result of gettimeofday:
struct timeval tv;
gettimeofday(&tv, 0);
struct tm *t = localtime(&tv.tv_sec); // t points to a statically allocated struct
localtime gives the broken-down time in your local timezone, but it may be susceptible to DST. gmtime gives the broken-down time in UTC, which is immune to DST.
None of the answers provided really does what I need it to do. I've come up with something standalone that I think should work. If anybody spots any errors or can think of a faster method, please let me know. Present code takes 15 microseconds to run. I challenge SO to make something quicker (and I really hope SO succeeds =P)
inline int ms_since_midnight()
{
//get high precision time
timespec now;
clock_gettime(CLOCK_REALTIME,&now);
//get low precision local time
time_t now_local = time(NULL);
struct tm* lt = localtime(&now_local);
//compute time shift utc->est
int sec_local = lt->tm_hour*3600+lt->tm_min*60+lt->tm_sec;
int sec_utc = static_cast<long>(now.tv_sec) % 86400;
int diff_sec; //account for fact utc might be 1 day ahead
if(sec_local<sec_utc) diff_sec = sec_utc-sec_local;
else diff_sec = sec_utc+86400-sec_local;
int diff_hour = (int)((double)diff_sec/3600.0+0.5); //round to nearest hour
//adjust utc to est, round ns to ms, add
return (sec_utc-(diff_hour*3600))*1000+(int)((static_cast<double>(now.tv_nsec)/1000000.0)+0.5);
}
You can run localtime_r, and mktime after adjusting the result of localtime_r to compute the value of "midnight" relative to the Epoch.
Edit: Pass now into the routine to avoid an unnecessary call to time.
time_t global_midnight;
bool checked_2am;
void update_global_midnight (time_t now, bool dst_check) {
struct tm tmv;
localtime_r(&now, &tmv);
tmv.tm_sec = tmv.tm_min = tmv.tm_hour = 0;
global_midnight = mktime(&tmv);
checked_2am = dst_check || (now >= (global_midnight + 2*3600));
}
Assume global_midnight is initially 0. Then, you would adjust it's value at 2am, and the next day, so that it stays in sync with DST. When you call clock_gettime, you can compute the difference against global_midnight.
Edit: Since the OP wants to benchmark the routine, tweaking code for compilers that assume true to be the fast path, and round to nearest msec.
unsigned msecs_since_midnight () {
struct timespec tsv;
clock_gettime(CLOCK_REALTIME, &tsv);
bool within_a_day = (tsv.tv_sec < (global_midnight + 24*3600));
if (within_a_day)
if (checked_2am || (tsv.tv_sec < (global_midnight + 2*3600))
return ((tsv.tv_sec - global_midnight)*1000
+ (tsv.tv_nsec + 500000)/1000000);
update_global_midnight(tsv.tv_sec, within_a_day);
return ((tsv.tv_sec - global_midnight)*1000
+ (tsv.tv_nsec + 500000)/1000000);
}
I have referred to the post [here] and made a change so that the below function can return the milliseconds since midnight in GMT time.
int GetMsSinceMidnightGmt(std::chrono::system_clock::time_point tpNow) {
time_t tnow = std::chrono::system_clock::to_time_t(tpNow);
tm * tmDate = std::localtime(&tnow);
int gmtoff = tmDate->tm_gmtoff;
std::chrono::duration<int> durTimezone(gmtoff); // 28800 for HKT
// because mktime assumes local timezone, we shift the time now to GMT, then fid mid
time_t tmid = std::chrono::system_clock::to_time_t(tpNow-durTimezone);
tm * tmMid = std::localtime(&tmid);
tmMid->tm_hour = 0;
tmMid->tm_min = 0;
tmMid->tm_sec = 0;
auto tpMid = std::chrono::system_clock::from_time_t(std::mktime(tmMid));
auto durSince = tpNow - durTimezone - tpMid;
auto durMs = std::chrono::duration_cast<std::chrono::milliseconds>(durSince);
return durMs.count();
}
If you want to have local time, it is much more easier.
Related
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:
We could do the comparison in UTC, or
We could do the comparison in local time.
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_points:
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).
Oops! Is the config time already UTC?
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;.
In C++ we can use the mt_structure from the date/time functions (documentation here: https://en.cppreference.com/w/cpp/chrono/c/tm) Here is how I would print the date, and check to see if it's past a certain time
#include <iostream>
#include <ctime>
#include <chrono>
using namespace std;
int main()
{
time_t t = time(0); // get time now
tm* now = localtime(&t);
cout << (now->tm_year + 1900) << '-'
<< (now->tm_mon + 1) << '-'
<< now->tm_mday << ", "
<< now->tm_hour << ":" << now->tm_min
<< "\n";
int hour = 7, minute = 30;
if((now->tm_hour > hour) || (now->tm_hour == hour && now->tm_min >= minute))
cout << "it's past 7:30\n";
else
cout << "it's not past 7:30";
}
prints:
2021-10-27, 20:40
it's past 7:30
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.
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.
Update: Store hours example
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.
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. I've edited to use slightly simpler syntax to eliminate the 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_points 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.
I need to be able to convert Epoch time to Excel time.
Why Excel, because working with the numeric excel time is faster that any parsing done on display formats.
Current time of 2018-06-08 12:46:58 CDTwith UTC 1528480019 should give 0.5326157.
But converted to the New_York time or 2018-06-08 13:46:58 EDT will give 0.574282367.
I only need to convert the time field to Excel style.
Here is my incomplete code:
double GetTime(Datetime currtime, std::string tz = "TZ=America/New_York")
{
std::time_t t = currtime;
//tzset(tz);
std::tm tm = *std::localtime(&t);
return ((tm.tm_hour * 3600 + (tm.tm_min) * 60.0 + tm.tm_sec) / 86400.0);
}
The code works, but only for local time which is "America/Chicago".
I have been unable to use the set the timezone to the one I might need.
Also tm seems to be limited to seconds, but I need to handle milliseconds and microseconds as well.
Furthermore, I need it to be fast and the the current implementation parses the time into separate fields and then I combine it into what I need which seems to do a lot of extra work.
This problem can easily be solved with Howard Hinnant's free, open-source date/time/timezone library, which is very efficient. This library is also in the current C++20 working draft, under namespace std::chrono. So in the future, porting your code to just use the std::lib ought to be as easy as changing a few namespaces.
double
GetTime(std::chrono::system_clock::time_point currtime,
date::time_zone const* tz = date::current_zone())
{
using namespace date;
using namespace std::chrono;
zoned_time<system_clock::duration> zt{tz, currtime};
auto lt = zt.get_local_time();
auto ld = floor<days>(lt);
using ExcelTime = duration<double, days::period>;
ExcelTime tod = lt - ld;
return tod.count();
}
Instead of taking a Datetime it takes a std::chrono::system_clock::time_point, and instead of a std::string, a date::time_zone const*.
On the three big platforms (llvm/gcc/MSVS), the coarsest system_clock::time_point is microseconds, which meets your precision goals.
Step one is to create a zoned_time which is a pairing of a time_point with a time_zone. From this one can get a local_time.
floor<days> truncates the precision of a time_point to days. If one subtracts the day-precision time_point from the finer-precision time_point, one gets the local time-of-day.
If you store this local time-of-day in a chrono::duration that has a double as its representation, and a period of 1 day, then you get the Excel Time-Of-Day format.
This can be used like:
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << std::fixed << std::setprecision(9);
std::cout << GetTime(sys_seconds{1528480019s}, locate_zone("America/Chicago")) << '\n';
zoned_time<system_clock::duration> zt{"America/New_York",
local_days{2018_y/6/8} + 13h + 46min + 58s};
std::cout << GetTime(zt.get_sys_time(), zt.get_time_zone()) << '\n';
}
which outputs:
0.532627315
0.574282407
Above, I struggled to come as close as possible to your existing API. However if you adopt this library, you can make it even simpler, and slightly more efficient, by adopting a "more native" API:
std::chrono::duration<double, date::days::period>
GetTime(const date::zoned_time<std::chrono::system_clock::duration>& zt)
{
using namespace date;
auto lt = zt.get_local_time();
return lt - floor<days>(lt);
}
Now GetTime takes just a single parameter of type zoned_time<system_clock::duration>, and returns a duration<double, days::period>. All that's left for GetTime to do is truncate the local time to days-precision and subtract to get time-of-day.
The demo in main is also simplified:
std::cout << GetTime({"America/Chicago", sys_seconds{1528480019s}}).count() << '\n';
std::cout << GetTime({"America/New_York",
local_days{2018_y/6/8} + 13h + 46min + 58s}).count() << '\n';
And gives the same output as before.
I have a time_t that represents the time in seconds since epoch. Those seconds refer to the local time.
I want to convert them to UTC.
Is there a way to do this in C++?
I'm going to show two ways of doing this:
Using the C API.
Using a modern C++11/14 library based on top of <chrono>.
For the purposes of this demo, I'm assuming that the current number of seconds in the local time zone is 1,470,003,841. My local time zone is America/New_York, and so the results I get reflect that we are currently at -0400 UTC.
First the C API:
This API is not type-safe and is very error prone. I made several mistakes just while coding up this answer, but I was able to quickly detect these mistakes because I was checking the answers against the 2nd technique.
#include <ctime>
#include <iostream>
int
main()
{
std::time_t lt = 1470003841;
auto local_field = *std::gmtime(<);
local_field.tm_isdst = -1;
auto utc = std::mktime(&local_field);
std::cout << utc << '\n'; // 1470018241
char buf[30];
std::strftime(buf, sizeof(buf), "%F %T %Z\n", &local_field);
std::cout << buf;
auto utc_field = *std::gmtime(&utc);
std::strftime(buf, sizeof(buf), "%F %T UTC\n", &utc_field);
std::cout << buf;
}
First I initialize the time_t. Now there is no C API to go from a local time_t to a UTC time_t. However you can use gmtime to go from a UTC time_t to a UTC tm (from serial to field type, all in UTC). So the first step is to lie to gmtime, telling it you've got a UTC time_t. And then when you get the result back you just pretend you've got a local tm instead of a UTC tm. Clear so far? This is:
auto local_field = *std::gmtime(<);
Now before you go (and I personally messed this part up the first time through) you have to augment this field type to say that you don't know if it is currently daylight saving or not. This causes subsequent steps to figure that out for you:
local_field.tm_isdst = -1;
Next you can use make_time to convert a local tm to a UTC time_t:
auto utc = std::mktime(&local_field);
You can print that out, and for me it is:
1470018241
which is 4h greater. The rest of the function is to print out these times in human readable format so that you can debug this stuff. For me it output:
2016-07-31 22:24:01 EDT
2016-08-01 02:24:01 UTC
A modern C++ API:
There exist no facilities in the std::lib to do this. However you can use this free, open source (MIT license) library for this.
#include "date/tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono_literals;
auto zt = make_zoned(current_zone(), local_seconds{1470003841s});
std::cout << zt.get_sys_time().time_since_epoch() << '\n'; // 1470018241s
std::cout << zt << '\n';
std::cout << zt.get_sys_time() << " UTC\n";
}
The first step is to create the local time in terms of seconds since the epoch:
local_seconds{1470003841s}
The next thing to do is to create a zoned_time which is a pairing of this local time and the current time zone:
auto zt = make_zoned(current_zone(), local_seconds(1470003841s));
Then you can simply print out the UTC number of seconds of this pairing:
std::cout << zt.get_sys_time().time_since_epoch() << '\n';
This output for me:
1470018241s
(4h later than the input). To print out this result as I did in the C API:
std::cout << zt << '\n';
std::cout << zt.get_sys_time() << " UTC\n";
which outputs:
2016-07-31 22:24:01 EDT
2016-08-01 02:24:01 UTC
In this modern C++ approach, the local time and the UTC time are different types, making it much more likely that I catch accidental mixing of these two concepts at compile time (as opposed to creating run time errors).
Update for C++20
The second technique will be available in C++20 with the following syntax:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
zoned_time zt{current_zone(), local_seconds{1470003841s}};
std::cout << zt.get_sys_time().time_since_epoch() << '\n'; // 1470018241s
std::cout << zt << '\n';
std::cout << zt.get_sys_time() << " UTC\n";
}
You can use gmtime:
Convert time_t to tm as UTC time Uses the value pointed by timer to
fill a tm structure with the values that represent the corresponding
time, expressed as a UTC time (i.e., the time at the GMT timezone).
(c) http://www.cplusplus.com/reference/ctime/gmtime/
If you are okay with using Abseil's time library, one other way to do this is:
auto civil_second =
absl::LocalTimeZone().At(absl::FromTimeT(<your time_t>)).cs;
time_t time_in_utc = absl::ToTimeT(absl::FromCivil(civil_second, absl::UTCTimeZone()));
(Maybe there is a simpler set of calls in the library to do this, but I have not explored further. :))
Normaly, you would convert from time_t to struct tm and there aren't many examples of converting from time_t to time_t in a different time zone (UTC in case of the OP's question). I wrote these 2 functions for that exact purpose. They may be useful when you are only in a need ot using time_t but in a specific time zone.
time_t TimeAsGMT(time_t t)
{
std::chrono::zoned_time zt{"UTC", std::chrono::system_clock::from_time_t(t)};
return std::chrono::system_clock::to_time_t(zt.get_sys_time());
}
or if you want the current time as UTC in the form of time_t
time_t CurTimeAsGMT()
{
std::chrono::zoned_time zt{"UTC", std::chrono::system_clock::now()}; // Get the time in UTC time zone
return std::chrono::system_clock::to_time_t(zt.get_sys_time()); // return this time as time_t
}
If you run both functions and compare the initial value and the result value, you will see that the difference matches the difference between your current time (at your current time zone) and UTC / GMT time zone.
My ultimate goal is to know the integer number of hours between the current local ("wall clock") time (in New York) and the same in London. I am in New York.
I've read thru the MSDN documentation on "TimeZone" structures available to me. The complication is Daylight Time. I would appreciate code that would return "if" Daylight Savings time in a different city than where my system's clock is set. For example, today it is Daylight Time in New York but not (yet) Daylight Time in London, so I would wish to ultimately know "4 hours." For most of the year, it is 5 hours.
I can't take the route to transiently set my system time to British Time and then difference/read the "Daylight Time" status, yes/no, as my app has many concurrent threads that are extremely "wall clock"-sensitive and would go haywire if the system time were suddenly advanced a few hours, even if for but a few milliseconds. Instead, a 0/1 for current local Standard/Daylight (which I can do myself) and a 0/1 for distant (London) Standard/Daylight (which I need help with) would suffice. Or, of course, simply knowing the local time a system in London would display having elected to make the Daylight adjustment.
I am a relative beginner to C++. My development environment is Win7:VS2013:C++:MFC
GetSystemTime is "expressed in Coordinated Universal Time (UTC)"
This doesn't always match London time. UK has daylight savings and their clock goes back and forth relative to UTC. Most other countries have their own daylight savings which are at different dates.
GetLocalTime gives the time in New York or whichever location you are at, with daylight savings adjustment.
Your program should use GetSystemTime everywhere.
When you want to show the date/time to user, use SystemTimeToTzSpecificLocalTime for conversion.
SYSTEMTIME systemTime;
GetSystemTime(&systemTime);
SYSTEMTIME localTime;
SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime);
If you get user date/time input, use TzSpecificLocalTimeToSystemTime to convert to UTC for internal program use.
Using https://github.com/HowardHinnant/date (works on VS-2013):
#include <iostream>
#include "tz.h"
int
main()
{
using namespace date;
using namespace std::chrono;
auto zLondon = locate_zone("Europe/London");
auto zNew_York = locate_zone("America/New_York");
auto diff = floor<hours>(zLondon->to_local(sys_days{2016_y/mar/21}) -
zNew_York->to_local(sys_days{2016_y/mar/21}));
std::cout << diff.count() << "h\n";
diff = floor<hours>(zLondon->to_local(sys_days{2016_y/jun/21}) -
zNew_York->to_local(sys_days{2016_y/jun/21}));
std::cout << diff.count() << "h\n";
diff = floor<hours>(zLondon->to_local(system_clock::now()) -
zNew_York->to_local(system_clock::now()));
std::cout << diff.count() << "h\n";
}
Outputs today (2016/mar/28):
4h
5h
4h
For determining the local time in a zone that is not your computer's local time one can do this:
auto zLondon = locate_zone("Europe/London");
auto tp = zLondon->to_local(system_clock::now());
auto dp = floor<days>(tp);
weekday wd = dp;
year_month_day ymd = dp;
auto time = make_time(tp-dp);
tp is a "local" std::chrono::time_point shifted to the "Europe/London" timezone.
dp is the same type as tp, but with a precision of days instead of microseconds (or whatever).
wd is the current day of the week in London.
ymd is a year-month-day structure (with year, month and day getters) of the current time in London.
time is an hour-minute-second-subseconds structure (with those getters) marking the current London time.
This all could be used like this to find out if a market is open or not (this simplistic example code neglects holidays but takes weekends into account):
std::cout << "It is currently " << wd << ' ' << ymd << ' ' << time << '\n';
if (wd != sat && wd != sun && 9h <= time.hours() && time.hours() < 16h)
std::cout << "Markets open\n";
else
std::cout << "Markets closed\n";
This just output for me:
It is currently Thu 2016-03-31 16:39:15.656398
Markets closed
You can use GetDynamicTimeZoneInformation function