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