Convert double to zoned_time using Howard Hinnant's date library - c++

I have a double representing the time in days since midnight (local time zone) 1 Jan 1970 and a string representing the time zone. I would like to convert these to a
date::zoned_time
using Howard Hinnant's date and time zone library.
The background is I need to convert date-times to and from doubles to use in an analytics library. I will also receive date-times as doubles from excel in a local or user-specified time zone.
Here is one attempt I made
using namespace date;
using namespace std::chrono;
typedef date::zoned_time<std::chrono::seconds> datetime;
const double UNIX_MINUS_EXCEL_EPOCH = 25569.0;
const double SECONDS_PER_DAY = 24.0 * 60.0 * 60.0;
datetime timePointFromDouble(double x)
{
double seconds = (x - UNIX_MINUS_EXCEL_EPOCH) * SECONDS_PER_DAY;
system_clock::duration d = duration_cast<system_clock::duration>(duration<double>(seconds));
system_clock::time_point t = system_clock::time_point(d);
auto xx = make_zoned("America/Chicago", t);
return xx;
}
It doesn't compile because the result of make_zoned has the wrong type. Also, I am not convinced it correctly maps the input time in days to the output date-time because of leap seconds and days where daylight saving changes.

Specification:
x is a measure of days since 1899-12-30 00:00:00 in America/Chicago.
Solution:
using datetime = date::zoned_seconds;
datetime
timePointFromDouble(double x)
{
using namespace date;
using namespace std::chrono;
using ddays = duration<double, days::period>;
constexpr auto excel_epoch = local_days{1_d/January/1970} -
local_days{30_d/December/1899};
return datetime{"America/Chicago",
local_seconds{round<seconds>(ddays{x} - excel_epoch)}};
}
Explanation:
The reason your version doesn't compile is because of the conversion to system_clock::time_point, which in practice has a precision of microseconds or finer. But your result type has a precision of seconds, so the library is refusing to implicitly truncate your high-precision t to your lower-precision xx.
The easiest way to fix this is to time_point_cast<seconds>(t). But there's more fun to be had...
<chrono> lives and dies by handling the conversions for you. Any time you're doing the conversions yourself, you should opt for removing those conversions in favor of letting <chrono> do them. This will usually simplify your code, and it may just catch a conversion error.
<chrono> knows about how to convert among various durations, but not about the Excel epoch, so that's one conversion we can't avoid. But we can express that epoch in a higher level language than the mysterious constant 25569.0.
So, from the top:
date::zoned_seconds is a simpler way to write date::zoned_time<std::chrono::seconds>. It is just a convenience typedef.
ddays is a custom duration unit which represents 1 day with a double. This is convenient for converting the scalar input x directly into a <chrono> duration. It is best to get into the <chrono> type system as soon as possible.
The epoch difference is the amount of time between 1970-01-01 and 1899-12-30. The units will be days as I've coded it, but that is an unimportant detail. <chrono> takes care of the units for you.
I'm using local_days as opposed to sys_days to compute the epoch difference. This is a largely symbolic gesture to communicate that the epoch is in a local time, not UTC. It doesn't make a difference in the actual value of the constant that is computed.
Because of the way you worded the question, I assumed you would prefer day-month-year ordering in the code. This is a purely stylistic choice.
If you are writing this in C++11, excel_epoch will have to be made const instead of constexpr. The difference is that C++11 has to compute this constant at run-time and C++14 and later can compute it at compile-time.
When converting from double-based units to integral-based units, I like to use round instead of duration_cast. The difference is that round chooses the nearest representable value, and duration_cast truncates towards zero to the nearest representable value. The round strategy is more likely to result in stable round-trip conversions between the double and integral representations, whereas truncation is more likely to expose one-off differences due to round-off error in the double representation.
The last line has to explicitly get us from double-based to integral-based units, and has to specify seconds to match with the return type, but does not have to worry about converting days into seconds.
The last line uses local_seconds to convert the duration into a time_point because this duration represents a measure in the local time of America/Chicago, as opposed to a measure in UTC. This fixes the epoch to 1899-12-30 00:00:00 in America/Chicago as opposed to 1899-12-30 00:00:00 UTC.
The result does not take leap seconds into account. This is the correct thing to do, because neither does Excel nor system_clock. Just about every computer-based time-keeping protocol out there doesn't count leap seconds. Here is a good description of Unix Time. If you want to convert to a system that counts leap seconds, this library can do that too. It is called utc_clock/utc_time.
The result does take daylight savings time into account for Chicago, including the changes to the daylight savings rules over the years, as best as the IANA database can do (which is exact as far as I know).

Related

How to generate date::local_time from std::chrono_time_point

I'm using Howard Hinnant's time zone library.
https://howardhinnant.github.io/date/tz.html
My question: Is it possible to construct a date::local_time object from a std::chrono::time_point?
What I want to to:
// 'tp' exists and is some std::chrono::time_point object
auto locTime = date::local_time<std::chrono::milliseconds>(tp);
The constructor for this doesn't exist so a get a compilation error.
How do I do this (in nice and clean C++17)?
Background:
My ultimate goal is to compare a std::filesystem::file_time_type to a date::local_time<std::chrono::milliseconds>.
I do
auto fileTimeTp = std::chrono::clock_cast<std::chrono::system_clock>(someDirectoryEntryObject.last_write_time());
which gives my my std::chrono::time_point for the file date, but that's where I'm stuck...
This is a two part answer ...
Part 1
My question: Is it possible to construct a date::local_time object from a std::chrono::time_point?
I'm going to assume that std::chrono::time_point refers to std::chrono::system_clock::time_point (each clock has its own family of std::chrono::time_point).
Yes, it is possible. Background: system_clock::time_point is defined as Unix Time which is a close approximation to UTC. So to go from system_clock::time_point (also know as sys_time in the date library / C++20) to local_time, you need to pair the sys_time with a time_zone. This could be your computer's current local time zone, or any other IANA time zone.
To get the computer's current local time zone:
auto tz = date::current_zone();
The type of tz is a date::time_zone const*. time_zone has a member function called to_local that will translate a sys_time into a local_time:
auto locTime = tz->to_local(system_clock::now());
The precision of locTime will match whatever the precision of the input sys_time is.
If you would like to use some other time zone, then you can use date::locate_zone to get a date::time_zone const* to that time zone.
auto locTime = date::locate_zone("America/New_York")->local_time(system_clock::now());
Part 2
My ultimate goal is to compare a std::filesystem::file_time_type to a date::local_time<std::chrono::milliseconds>.
Ah, this won't really involve local_time at all. And unfortunately, file_clock is not implemented in the time_zone library.
In C++20, this will be quite easy: Given a file_time and a sys_time you can convert either to the other using clock_cast:
if (clock_cast<system_clock>(ftp) >= system_clock::now())
...
However in C++17 it is harder, and non-portable. The the time_zone library makes it easier, but not easy.
You first have to deduce the epoch of std::filesystem::file_time_type on your platform. This will be different depending on which implementation of std::filesystem::file_time_type you are using.
Existing epochs include:
* 1970-01-01 00:00:00 UTC
* 1601-01-01 00:00:00 UTC
* 2174-01-01 00:00:00 UTC
Then you subtract the sys_time epoch (sys_days{1970_y/1/1}) and the file_time epoch (e.g. sys_days{1601_y/1/1}), and add/subtract that epoch to convert from one measure to the other.
For example:
constexpr auto diff = sys_days{1970_y/1/1} - sys_days{1601_y/1/1};
file_time_type ftp = ...
system_clock::time_point tp{ftp.time_since_epoch() - diff};
That's unfortunately quite messy, and I'm looking forward to clock_cast working with file_clock in C++20.

C++ std::chrono::high_resolution_clock time_since_epoch returns too small numbers. How can I get the correct time since 1970 in microseconds?

I am trying to write a function, which will return the current time in microseconds since 1970. While a debugging I noticed, that the returned numbers are too small. For example: 269104616249. I also added static_assert to check the returned value type is int64_t, which i big enough to hold 292471 years in microseconds. So integer overflow should not be a case here.
What am I doing wrong?
Here is my code:
int64_t NowInMicroseconds() {
static_assert(std::is_same<decltype(duration_cast<microseconds>(high_resolution_clock::now().time_since_epoch()).count()), int64_t>::value);
return duration_cast<microseconds>(high_resolution_clock::now().time_since_epoch()).count();
}
int64_t result = NowInMicroseconds();
There are three chrono-supplied clocks in C++11/14/17 (more in C++20):
system_clock: This measures Unix Time (time since 1970 excluding leap seconds).1
steady_clock: Like a stop-watch. Great for timing, but it can not tell you the time of day.
high_resolution_clock: This has the disadvantages of system_clock and steady_clock, and the advantages of neither. Typically it is a type alias to either system_clock or steady_clock, and which one differs with platform.
You have to use system_clock for measuring time since 1970. Note that this is measured in UTC, not your local time zone. In C++11/14/17 to get the local time since 1970, you will have to either manually take your time zone into account, or use this C++20 chrono preview library.
std::int64_t
NowInMicroseconds()
{
using namespace std::chrono;
return duration_cast<microseconds>(system_clock_clock::now().time_since_epoch()).count();
}
Consider returning a strong type which means "microseconds since 1970" instead of an integral type. Strong type safety helps you find your logic errors at compile time:
std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds>
NowInMicroseconds()
{
using namespace std::chrono;
return time_point_cast<microseconds>(system_clock.now());
}
1 This is unspecified in C++11/14/17, but is true on all implementations. C++20 finally nails this epoch down in the spec.

How do I make "days-since-epoch" values play nice with std::chrono?

I'm obtaining from a magical gremlin an array of non-negative integer values in memory, say of type I. Each of these represents a number of days since some epoch (which is many years before 0 in the ISO 8601 calendar).
Now, I want to wrap these values - essentially through pointer or reinterpretation - by C++ classes, which can interact well with the code of std::chrono, or perhaps I should say be used with code which normally takes std::chrono timepoints and durations.
What would be my best course of action for this? Note I don't want to replace any data in the array nor create a new value for each of the existing ones, elsewhere.
Each of these represents a number of days since the epoch (which is many years before 0 in the ISO 8601 calendar).
Let's say you're referring to the epoch for the Julian Day Number. This epoch is consistent with your description. The link says this epoch is November 24, 4714 BC, in the proleptic Gregorian calendar. Instead of using the "BC" system, I find it convenient to use a system with negative years instead so that there is a smooth mathematical transition across the year 0. In this system the epoch is November 24, -4713.
Using Howard Hinnant's free, open-source, header-only date library, this is very easy to do. If I've got the wrong epoch, just substitute in the correct one in the obvious place.
#include "date/date.h"
#include <iostream>
date::sys_days
to_sys_days(int i)
{
using namespace date;
return sys_days{days{i} -
(sys_days{1970_y/January/1} - sys_days{-4713_y/November/24})};
}
The date::sys_days return type is a std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<int, std::ratio<86400>>>. Or in English: it is a system_clock-based time_point with a precision of days. This time_point will implicitly convert to your platform's system_clock::time_point.
Now you can pass your int to to_sys_days, and pass the result to a function taking a system_clock::time_point. For example:
void
display(std::chrono::system_clock::time_point tp)
{
using date::operator<<;
std::cout << tp << '\n';
}
int
main()
{
display(to_sys_days(2'458'674));
}
This outputs:
2019-07-09 00:00:00.000000
to_sys_days is a very cheap operation. You can afford to do it each time you read a single element of the data. All it does is subtract 2440588 from i. The optimized machine code for to_sys_days (clang++ -O3) is literally:
leal -2440588(%rdi), %eax
I.e. all of the type-changing business happens at compile-time. It's free. The only thing that happens at run-time is the epoch offset adjustment. This is the bare minimum that must be done no matter what to align your epoch with the system_clock epoch.
So if you have an array of int as your data, you don't have to make a copy of the entire array. You just transform each element of it on demand. For example:
int
main()
{
int data[] = {2'458'674, 2'458'675, 2'458'676, 2'458'677, 2'458'678};
for (auto i : data)
display(to_sys_days(i));
}
Output:
2019-07-09 00:00:00.000000
2019-07-10 00:00:00.000000
2019-07-11 00:00:00.000000
2019-07-12 00:00:00.000000
2019-07-13 00:00:00.000000
If you don't want to use the date library, you can still get the job done, it is just a bit more work.
First create a duration type that means days:
using days = std::chrono::duration
<int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
Then figure out the number of days between 1970-01-01 and your epoch.
Then take your integral value i, wrap it in days, and subtract off your epoch difference. Then you can construct a system_clock::time_point with your value of days.
Note: It is important that you do the epoch adjustment in units of days as opposed to converting first to the units of system_clock::time_point and then doing the adjustment. This latter strategy will overflow on some platforms. You are protected from overflow if you do the epoch offset in days precision.
I strongly advise against using the reinterpret_cast tool to get this job done. It seems both unnecessary and dangerous.
Update
I forgot about the part that says the Julian Day epoch is noon instead of midnight. If you want to take this into account, it is very easy with the date lib:
auto
to_sys_days(int i)
{
using namespace date;
using namespace std::chrono;
return sys_time<hours>{days{i} -
(sys_days{1970_y/January/1} - sys_days{-4713_y/November/24} - 12h)};
}
I simply subtracted 12 hours off the epoch difference and let auto deduce the return type for me (which is now a system_clock-based time_point with a precision of hours).
The exact same display function with the same input now outputs:
2019-07-09 12:00:00.000000
If it's really just about the duration then you can simply do
std::chrono::days(integer_value);
and you will get a chrono duration type.

Using std::chrono / date::gps_clock for converting a double gps timestamp to utc/tai

I get a timestamp from a GPS device in a gps_data struct as a double.
I'd like to convert this GPS timestamp to UTC and TAI times, something simple as:
void handle_gps_timestamp(double timestamp)
{
double utc = utc_from_gps(timestamp);
double tai = tai_from_gps(timestamp);
do_stuff(gps, utc, tai);
}
Luckily I found Howard Hinnant's date and timezone library (proposed for C++20) that seems to provide this exact functionality. Unfortunately, at least from what I can see, the date/tz/chrono library has no convenient methods that allow this simple usage.
I must first somehow "transfer" my double into a known chrono/date type. But OK, since I understand the overall concept, namely that the timepoint is defined as a duration after (or before) the epoch of a clock, and I think that this is a beautiful model.
Assumption
I should be able to very easily translate that model to fit my problem, right?
In my case, I have a timestamp that is a point in time, specified as the duration since the gps epoch. Now, there should be a class type of a clock that abstracts and handles all of this for me, I'd like to think. And yes! There is a date::gps_clock and a date::gps_time, which surely should do the work.
Problem
I cannot make it work for me. I'm sure the solution is trivial.
Question
Can someone give me a helping hand, showing how I should use Howard's date library applied to my problem?
It is difficult to answer this question precisely because the input to the problem is underspecified:
I get a timestamp from a GPS device in a gps_data struct as a double ... specified as the duration since the gps epoch.
Therefore I'm going to make some assumptions. I'll state all of my assumptions, and hopefully it will be clear how to alter my answer for other guesses/facts about what that double represents.
Let's say that the double is a non-integral count of milliseconds since the gps epoch. Let's furthermore assume that I want to capture the precision of this input down to microseconds.
#include "date/tz.h"
#include <cstdint>
#include <iostream>
int
main()
{
double gps_input = 1e+12 + 1e-3;
using namespace date;
using namespace std::chrono;
using dms = duration<double, std::milli>;
gps_time<microseconds> gt{round<microseconds>(dms{gps_input})};
auto utc = clock_cast<utc_clock>(gt);
auto tai = clock_cast<tai_clock>(gt);
std::cout << gt << " GPS\n";
std::cout << utc << " UTC\n";
std::cout << tai << " TAI\n";
}
I've arbitrarily created an example input and stored it in gps_input.
Some using directives make the code a lot less verbose.
A custom chrono::duration type that exactly matches the documented specification for what the double represents makes things much simpler, and lessens the chance for errors. In this case I've made a chrono::duration that stores milliseconds in a double and named that type dms.
Now you simply convert the double to dms, and then using round, convert the dms to microseconds, and store those microseconds in a gps time point with precision microseconds or finer. One could use duration_cast in place of round, but when converting from floating point to integral, I usually prefer round, which means round-to-nearest-and-to-even-on-tie.
Now that you have a gps_time, one can use the clock_cast function to convert to other times such as utc_time and tai_time.
This program outputs:
2011-09-14 01:46:40.000001 GPS
2011-09-14 01:46:25.000001 UTC
2011-09-14 01:46:59.000001 TAI
Adjust the milliseconds and microseconds units above as needed. For example if the input represents seconds, the easiest thing to do is to default the second template argument on dms:
using dms = duration<double>;
This library works with C++11/14/17. And with minor modifications it is now part of the official C++20 specification.
The other answer isn't bad but it does require you to have c++17, curl, run cmake, and acquire some custom libraries.
Something that is much easier to drop in as a .h and .cpp would be http://www.leapsecond.com/tools/gpsdate.c.
That doesn't handle the TAI conversion but that might also be on that list.

How to get the local current time in seconds since epoch in C++ (MSVS)?

I need the local (with timezone offset) current time in seconds since epoch. The following code looks a bit clumzy because it creates an intermediate temporary structure tm which is superfluous. Why do I have to get time_t then convert it to tm in order to return to time_t? Is there a better way?
time_t ct = time(0);
tm lct = tm();
localtime_s(&lct, &ct);
ct = _mkgmtime(&lct);
If you want to get the local time (with time zone and DST applied) in portable C, then yes, it's generally a two-step procedure: starting with your time-since-the-epoch, first call localtime, then do something with the resulting broken-down struct tm. (Usually what I do next is call strftime.)
You can also call ctime to get a local time string directly.
The reason there are a lot of different function calls involved is that, unfortunately, there are several different time formats in use. (And the reason for that is that dates and times are complicated!) You can represent time as seconds-since-1970. You can represent it as a struct tm. You can represent it as a string (in one of several zillion formats). In Unix and Linux, you can represent it as a struct timeval or a struct timespec.
But one thing there isn't a straightforward or standard way to do, as you've discovered, is get local time as seconds-since-1970. But the reason for that is that it's not a very useful representation. In general, there are two things you might want to do with a date/time value: (1) perform computations on it or (2) display it to the user. If you want to display it to the user, you probably want to display it in local time, so there are lots of ways of converting to local time in human-readable format in any format you want. (As I said, the usual way is to call localtime, then strftime.) But if you want to perform computations, really the only way to do those is using seconds-since-1970 in UTC, because that makes all the other hairy problems go away. How many days are there in the month? Is it a leap year? What time zone are we in? Is daylight saving time in effect?
If you try to represent local time as seconds-since-1970, though, you're probably fibbing. For example, right now, the time is 1460383736, which is 14:08:56 UTC. Where I'm sitting, that's 10:08:56 EDT (U.S. Eastern time, DST in effect). So I suppose I could say that's 1460369336 seconds since 1970, local time. But, again where I'm sitting, 1460369336 seconds ago was not midnight on January 1, 1970 -- it was actually 11 pm on December 31, 1969. It's off by an hour, and the reason is that DST was not in effect on January 1, 1970.
So, bottom line, I would encourage you to rethink the way you're handling local times, because while it's possible to compute this "seconds-since-1970 as local time" value, it's an odd thing to do, and it's likely to cause you various problems which will be much harder to work around than if you used a more straightforward scheme.
But, if you really want to, here are two ways you might be able to determine the offset between UTC and local time, without calling gmtime or _mkgmtime:
Call localtime, and look at the tm_gmtoff field. (Unfortunately, this field is nonstandard, and not present on all systems.)
Call the obsolete ftime function, and look at the timezone field of struct timeb. (Here there are several gotchas: not only is ftime obsolete and nonstandard, but the timezone field is in minutes, and it's positive for zones west of Greenwich, while tm_gmtoff is negative.)
But, anyway, those would more or less directly give you the number to add to or subtract from your UTC seconds-since-1970 value to get "local" seconds-since-1970.
Here is a way to do this computation using the C++11/14 <chrono> library plus this free, open-source timezone library to do the conversion to local time.
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto now = floor<seconds>(system_clock::now());
auto s = current_zone()->to_local(now) - local_days{1970_y/jan/1};
cout << s.count() << '\n';
}
You first discover your current IANA timezone with current_zone(). Then you get the current time with system_clock::now() and truncate it to seconds. Next you can convert that to your local time, and then subtract the result from any epoch you desire (1970-01-01 in this example).
The result is of type std::chrono::seconds.
All this being said, I share the same reservations about doing this as described in Steve Summit's answer.
If you instead decide to represent the timestamp as a string, that is also easily done:
auto now = make_zoned(current_zone(), floor<seconds>(system_clock::now()));
auto str = format("%F %T %z", now);
str has type std::string. This just output for me:
2016-04-11 11:42:50 -0400
which is my current local time (truncated to seconds), and my current local UTC offset.
If in the future you decide that seconds-precision is too coarse, you can easily change the above code to any other precision by just changing one line:
floor<milliseconds>(system_clock::now());
and now the contents of str would look like:
2016-04-11 11:42:50.368 -0400