Fractional day of the year computation in C++14 - c++

I wrote the following code using Howard Hinnants date.h library, to compute the fractional day of the year of the current time. I was wondering if there are shorter ways of doing it, because my code feels like an overkill of std::chrono and date calls. Can I directly calculate the number of fractional days since the start of the year (at microsecond precision) and avoid my two-step approach?
#include <iostream>
#include <chrono>
#include "date.h"
int main()
{
// Get actual time.
auto now = std::chrono::system_clock::now();
// Get the number of days since start of the year.
auto ymd = date::year_month_day( date::floor<date::days>(now) );
auto ymd_ref = date::year{ymd.year()}/1/1;
int days = (date::sys_days{ymd} - date::sys_days{ymd_ref}).count();
// Get the fractional number of seconds of the day.
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - date::floor<date::days>(now));
double seconds_since_midnight = 1e-6*microseconds.count();
// Get fractional day number.
std::cout << "Fractional day of the year: " << days + seconds_since_midnight / 86400. << std::endl;
return 0;
}

Good question (upvoted).
I think first we need to decide on what the right answer is. There's your answer, and currently the only other answer is Matteo's. For demonstration purposes, I've modified both answers to substitute in a "fake now" so that we can compare apples to apples:
using namespace std::chrono_literals;
auto now = date::sys_days{date::March/27/2019} + 0h + 32min + 22s + 123456us;
(approximately now at the time I'm writing this)
Chiel's code gives:
Fractional day of the year: 85.0225
Matteo's code gives:
Fractional day of the year: 85.139978280740735
They are close, but not close enough to both be considered right.
Matteo's code works with "average years":
auto this_year = date::floor<date::years>(now);
The length of a date::years is 365.2425 days, which is exactly right if you average all civil years over a 400 year period. And working with the average year length can be very useful, especially when dealing with systems that don't care about human made calendars (e.g. physics or biology).
I'm going to guess that because of the way Chiel's code is written, he would prefer a result that refers more precisely to this specific year. Therefore the code presented below is Chiel's's algorithm, resulting in exactly the same result, only slightly more efficient and concise.
// Get actual time.
auto now = std::chrono::system_clock::now();
// Get the number of days since start of the year.
auto sd = date::floor<date::days>(now);
auto ymd = date::year_month_day( sd );
auto ymd_ref = ymd.year()/1/1;
std::chrono::duration<double, date::days::period> days = sd - date::sys_days{ymd_ref};
// Get the fractional number of seconds of the day.
days += now - sd;
// Get fractional day number.
std::cout << "Fractional day of the year: " << days.count() << std::endl;
The first thing I noted was that date::floor<date::days>(now) was being computed in 3 places, so I'm computing it once and saving it in sd.
Next, since the final answer is a double-based representation of days, I'm going to let <chrono> do that work for me by storing the answer in a duration<double, days>. Any time you find yourself converting units, it is better to let <chrono> do it for you. It probably won't be faster. But it definitely won't be slower, or wrong.
Now it is a simple matter to add the fractional day to the result:
days += now - sd;
using whatever precision now has (microseconds or whatever). And the result is now simply days.count().
Update
And with just a little bit more time to reflect ...
I noticed that with the simplified code above, one can more easily see the entire algorithm as a single expression. That is (removing namespace qualification in order to get everything on one line):
duration<double, days::period> days = sd - sys_days{ymd_ref} + now - sd;
And this clearly algebraically simplifies down to:
duration<double, days::period> days = now - sys_days{ymd_ref};
In summary:
using namespace std::chrono;
using namespace date;
// Get actual time.
auto now = system_clock::now();
// Get the start of the year and subract it from now.
using ddays = duration<double, days::period>;
ddays fd = now - sys_days{year_month_day{floor<days>(now)}.year()/1/1};
// Get fractional day number.
std::cout << "Fractional day of the year: " << fd.count() << '\n';
In this case, letting <chrono> do the conversions for us, allowed the code to be sufficiently simplified such that the algorithm itself could be algebraically simplified, resulting in cleaner and more efficient code that is provably equivalent to the original algorithm in the OP's question.

Related

Converting a decimal GPS time to UTC

There's many similar questions out there but I haven't found one specific to the GPS output data I am receiving. The data from my GPS is in decimal form:
GPS Week: 2145 and GPS Time: 330374.741371 (the manual says this is a double that represents the "time of week in seconds")
I'm trying to convert this time into human readable UTC time. I'm using old C++14, not 20, so I can't just use the to_utc() function I don't think. I'm mostly confused about the decimal. On this website: https://www.labsat.co.uk/index.php/en/gps-time-calculator it looks like the data is "secondsOfTheWeek.secondsOfTheDay. I'm not sure how to convert this to UTC time...
I believe this output data is the number of seconds since the GPS epoch time of midnight, Jan. 6 1980. And I know it doesn't count leap seconds so that has to be taken into account too. If I had some guidance on how to start getting this into UTC time I think I could figure out the rest, but I'm not really sure where to start...
Eventually I want to convert the time into a string to set an OS system w that time using "date -s "16 AUG 2021 13:51:00" or something like that. But first I just need to convert this GPS time.
There exists a free, open-source preview to the C++20 chrono bits which works with C++14.
#include "date/tz.h"
#include <chrono>
date::utc_seconds
convert(int gps_week, double gps_time)
{
using namespace date;
using namespace std::chrono;
int upper = static_cast<int>(gps_time);
auto gps_t = gps_seconds{} + weeks(gps_week) + seconds{upper};
return clock_cast<utc_clock>(gps_t);
}
This first forms a gps_time by adding the appropriate number of weeks to the gps epoch, and then the seconds.
Next you use clock_cast to transform this into utc_time (which does include leap seconds).
This can be used like so:
#include <iostream>
int
main()
{
using namespace date;
using namespace std;
cout << convert(2145, 330374.741371) << '\n';
}
Which outputs:
2021-02-17 19:45:56
The clock_cast changes the epoch from 1980-01-06 to 1970-01-01 and adds in the number of leap seconds that occur between the gps epoch and the utc time point. If the gps input happens to correspond to a leap second, this will properly print "60" in the seconds field. For example:
cout << convert(1851, 259216) << '\n'; // 2015-06-30 23:59:60
Some installation is required.
Further information
This Wikipedia article says that the time of week actually comes in units of 1.5 seconds, ranging in value from 0 to 403,199.
static_assert(403'200 * 1.5 == 7 * 24 * 60 * 60);
If one finds themself dealing with the data in this form, here is an alternate convert implementation which can deal with this input data directly:
using gps_tow = std::chrono::duration<int, std::ratio<3, 2>>;
auto
convert(date::weeks gps_week_num, gps_tow tow)
{
using namespace date;
return clock_cast<utc_clock>(gps_seconds{} + gps_week_num + tow);
}
The first step is to define a duration unit of 1.5 seconds. This type is called gps_tow above.
The convert function now takes two strictly typed parameters: a count of weeks, and a count of gps_tow. Then one simply adds these parts together, along with the gps epoch, and clock_cast's it to utc_clock.
It can be used like so:
cout << convert(weeks{1851}, gps_tow{172811}) << '\n';
The output for this example is:
2015-06-30 23:59:60.5

How to find the date of the previous monday in C++

I want to make a program which takes no input and returns the date of the previous Monday. (I don't care about time zones. And I am only worried about Gregorian calendar). I am using date by Howard Hinnant. This is how I am doing it currently:
#include <iostream>
#include <date/date.h>
int main() {
auto todays_day = date::year_month_weekday(date::floor<date::days>(std::chrono::system_clock::now()));
auto todays_date = date::floor<date::days>(std::chrono::system_clock::now());
int offset = 0;
auto weekday = todays_day.weekday();
if(weekday == date::Tuesday)
offset = 1;
else if (weekday == date::Wednesday)
offset = 2;
else if (weekday == date::Thursday)
offset = 3;
else if (weekday == date::Friday)
offset = 4;
else if (weekday == date::Saturday)
offset = 5;
else if (weekday == date::Sunday)
offset = 6;
auto lastMonday = date::year_month_day(todays_date - date::days(offset));
std::cout << lastMonday;
}
Is there a better way to do this without boost::previous_weekday? (It's not a requirement not to use boost. I am just wondering if it is possible)
The key to understanding how to do this more simply is knowing this one fact about Howard Hinnant's date library:
weekday difference is circular (or modulo 7 if you prefer). That is, any weekday subtracted from any weekday results in a number of days in the range [0, 6]. This effectively hides the underlying encoding of weekday.
Thus there is no need to translate [Monday, Sunday] into [0, 6] (or any other encoding):
#include "date/date.h"
#include <iostream>
int
main()
{
auto todays_date = date::floor<date::days>(std::chrono::system_clock::now());
date::year_month_day lastMonday = todays_date -
(date::weekday{todays_date} - date::Monday);
std::cout << lastMonday << '\n';
}
Instead you just have to decide how many days you need to subtract from a sys_days (todays_date in this example). That number of days is today's weekday minus Monday. If today is Monday, the result is days{0}. If today is Sunday, the result is days{6}. We could just as well be talking about finding the previous Friday. The logic would not change.
Also, one can directly convert a sys_days to a weekday. No need to go though year_month_weekday.
The code in the OP's question considers the "previous Monday" to be today if today happens to be a Monday. And that is fine. That is what is desired in many "previous weekday" algorithms. And it is the logic I have coded above.
But it is also common to want the previous-weekday-algorithm to result in last week if the weekday you are seeking is today. I.e. if today is Monday, compute a week ago instead of today. That too is easily doable, and by pretty much the same algorithm. One just has to subtract a day at the beginning of the algorithm if you desire this behavior:
auto todays_date = ...
todays_date -= date::days{1};
date::year_month_day lastMonday = ...

std::chrono: Set clock's epoch to 1/1/0000

Is it possible to manually set the epoch date/time to the January 1, 0000, so I might use the std::chrono::time_point::time_since_epoch to calculate the difference between a given date and January 1, 0000?
I tried the following:
#include <iostream>
#include <chrono>
#include <ctime>
int main(int argc, char*argv[])
{
std::tm epochStart = {};
epochStart.tm_sec = 0;
epochStart.tm_min = 0;
epochStart.tm_hour = 0;
epochStart.tm_mday = 0;
epochStart.tm_mon = 0;
epochStart.tm_year = -1900;
epochStart.tm_wday = 0;
epochStart.tm_yday = 0;
epochStart.tm_isdst = -1;
std::time_t base = std::mktime(&epochStart);
std::chrono::system_clock::time_point baseTp=
std::chrono::system_clock::from_time_t(base);
std::time_t btp = std::chrono::system_clock::to_time_t(baseTp);
std::cout << "time: " << std::ctime(&btp);
}
but this gives me
time: Thu Jan 1 00:59:59 1970
I would avoid std::time_t altogether. Using days_from_civil from chrono-Compatible Low-Level Date Algorithms, you can immediately compute any difference between std::chrono::system_clock::time_point, and any date in the proleptic Gregorian calendar1.
In addition to days_from_civil which takes a year/month/day triple and converts it into a count of days before/since 1970-01-01 (a chrono-compatible epoch), it is also convenient to create a custom chrono::duration to represent 24 hours:
typedef std::chrono::duration
<
int,
std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
> days;
Now you can create any epoch you want with just:
constexpr days epoch = days(days_from_civil(0, 1, 1)); // 0000-01-01
In C++1y this is even a compile-time computation!
And you can subtract this std::chrono::duration from any other std::chrono::duration:
auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;
delta is now a std::chrono::duration representing the amount of time between now, and 0000-01-01. You can then print that out however you want, or otherwise manipulate it. For example here is an entire working demo:
#include "../date_performance/date_algorithms"
#include <iostream>
#include <chrono>
typedef std::chrono::duration
<
int,
std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
> days;
int
main()
{
constexpr days epoch = days(days_from_civil(0, 1, 1));
auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;
days d = std::chrono::duration_cast<days>(delta);
std::cout << "It has been " << d.count() << " days, ";
delta -= d;
auto h = std::chrono::duration_cast<std::chrono::hours>(delta);
std::cout << h.count() << " hours, ";
delta -= h;
auto m = std::chrono::duration_cast<std::chrono::minutes>(delta);
std::cout << m.count() << " minutes, ";
delta -= m;
auto s = std::chrono::duration_cast<std::chrono::seconds>(delta);
std::cout << s.count() << " seconds ";
std::cout << " since 0000-01-01\n";
}
Which for me output:
It has been 735602 days, 19 hours, 14 minutes, 32 seconds since 0000-01-01
A word of warning about overflow:
The std::chrono::system_clock::time_point::duration is not guaranteed to have a range large enough to do this. It turns out that on my system it does. It is microseconds in a signed long long which will span +/- 292,000 years. If you need to avoid an overflow problem, you could truncate your std::chrono::system_clock::time_point::duration to courser units (e.g. seconds or days) to extend the range prior to subtracting 0000-01-01.
I got to thinking
And that usually leads to a disaster. However in this case I decided I should add to this post anyway. This:
constexpr days epoch = days(days_from_civil(0, 1, 1));
has type days, which is a duration. But it really isn't a duration. It is a point in time. It is a date. It is a time_point with a coarse precision. By introducing a new typedef, the code in this post can be cleaned up just a little bit more:
typedef std::chrono::time_point<std::chrono::system_clock, days> date_point;
Now instead of writing:
constexpr days epoch = days(days_from_civil(0, 1, 1));
One can write:
constexpr date_point epoch{days(days_from_civil(0, 1, 1))};
But even more importantly, instead of:
auto delta = std::chrono::system_clock::now().time_since_epoch() - epoch;
we can now write:
auto delta = std::chrono::system_clock::now() - epoch;
This delta still has exactly the same type and value as it did previously, and everything else in the demo still proceeds as exactly as it did before.
This is both a small change, and a big change. By treating epoch as a time_point instead of a duration, the algebra of time_point's and duration's works for us, both simplifying and type-checking our expressions to help us write cleaner code with fewer mistakes.
For example one can add two duration's together. But it doesn't make any sense at all to:
epoch + epoch
By using time_point instead of duration for the type of epoch, the compiler catches such non-sensical expressions at compile time.
1The proleptic Gregorian calendar has a year 0. In the year 0 it is 2 days behind the Julian calendar. Using a year 0 is also consistent with ISO 8601. As long as all parties involved know what calendar you are using, then everything is fine. Conversion between non-positive years and "BC years" is trivial if desired.
It's possible, the code you've given (minus a small fix, tm_mday starts with 1) yields:
Sat Jan 1 00:00:00 0
Live example
The real problem is: Are you on 32-bit or 64-bit? With a 32-bit system, time_t is also only 32 bits and you are limited to 1970 +/- 68 years.
On a 64-bit system, the limits are given by std::mktime and std::strftime, in my own code I have unit test for those strings and the corresponding values:
"-2147481748-01-01 00:00:00" maps to -67768040609740800
"2147483647-12-31 23:59:59" maps to 67767976233532799
I should probably also mention that there are systems where the above does not work because the underlying OS functions are buggy. For the record: I'm on Linux.
No. mktime and friends are based on UNIX time, which starts on 1st January 1970.
There is in fact no such thing as 0th January, 0000, so it seems likely that you would be better off finding another way to solve whatever is your actual problem.

utc seconds since midnight to datetime

I'm getting radar data as "tracks" and the track data indicates the number of UTC seconds since the last midnight, apparently. This is not the number of seconds since the 1st of jan 1970.
Now I want to convert that to date time, knowing that the clock on the computer could be slightly out of sync with the clock on the radar. I'll assume the radar's seconds are the reference, not the computer's.
I want to convert these seconds to a full date time. Things seem to be a little tricky around
midnight.
Any suggestions? I've got some ideas, but I don't want to miss anything.
I'm working with C++ Qt.
// Function to extend truncated time, given the wall time and period, all
// in units of seconds.
//
// Example: Suppose the truncated period was one hour, and you were
// given a truncated time of 25 minutes after the hour. Then:
//
// o Actual time of 07:40:00 results in 07:25:00 (07:40 + -15)
// o Actual time of 07:10:00 results in 07:25:00 (07:10 + +15)
// o Actual time of 07:56:00 results in 08:25:00 (07:56 + +29)
double extendTruncatedTime(double trunc, double wall, int period) {
return wall + remainder(trunc - wall, period);
}
#define extendTruncatedTime24(t) extendTruncatedTime(t, time(0), 24 * 60 * 60)
Some commentary:
The units of wall are seconds, but its base can be arbitrary. In Unix, it typically starts at 1970.
Leap seconds are not relevant here.
You need #include <math.h> for remainder().
The period in extendTruncatedTime() is almost always twenty-four hours, 24 * 60 * 60, as per the OP's request. That is, given the time of day, it extends it by adding the year, month, and day of month, based on the 'wall' time.
The only exception I know to the previous statement is, since you mention radar, is in the Asterix CAT 1 data item I001/141, where the period is 512 seconds, and for which extendTruncatedTime() as given doesn't quite work.
And there is another important case which extendTruncatedTime() doesn't cover. Suppose you are given a truncated time consisting of the day of month, hour, and minute. How can you fill in the year and the month?
The following code snippet adds the year and month to a time derived from a DDHHMM format:
time_t extendTruncatedTimeDDHHMM(time_t trunc, time_t wall) {
struct tm retval = *gmtime_r(&trunc, &retval);
struct tm now = *gmtime_r(&wall, &now);
retval.tm_year = now.tm_year;
retval.tm_mon = now.tm_mon;
retval.tm_mon += now.tm_mday - retval.tm_mday > 15; // 15 = half-month
retval.tm_mon -= now.tm_mday - retval.tm_mday < -15;
return timegm(&retval);
}
As written, this doesn't handle erroneous inputs. For example, if today is July 4th, then the non-nonsensical 310000 will be quietly converted to July 1st. (This may be a feature, not a bug.)
If you can link against another lib, i'd suggest to use boost::date_time.
It seems you want to take current date in seconds from midnight (epoch) then add the radar time to it, then convert the sum back to a date time, and transform it into a string.
Using boost will help you in:
getting the right local time
calculating the date back
incorporating the drift into the calculation
taking leap seconds into account
since you'll have concept like time intervals and durations at your disposal. You can use something like (from the boost examples):
ptime t4(date(2002,May,31), hours(20)); //4 hours b/f midnight NY time
ptime t5 = us_eastern::local_to_utc(t4);
std::cout << to_simple_string(t4) << " in New York is "
<< to_simple_string(t5) << " UTC time "
<< std::endl;
If you want to calculate the drift by hand you can do time math easily similar to constructs like this:
ptime t2 = t1 - hours(5)- minutes(4)- seconds(2)- millisec(1);
I had the exact same problem but I'm using C#. My implementation is included here if anyone needs the solution in C#. This does not incorporate any clock drift.
DateTime UTCTime = DateTime.UtcNow.Date.AddSeconds(secondSinceMidnightFromRadar);

get difference between two time_t into a tm with proper tm_mday

Depending on the amount of input data, I have a program that runs in seconds or in days. At the end of my program, I want to print the elapsed "clock wall" time: in seconds if it is less then one minute, in min and sec if it is less than one hour, in hour-min-sec if it is less than one day, and in day-hour-min-sec otherwise. Here is the code I am using:
#include <cstdio>
#include <ctime>
#include <unistd.h> // for sleep
int main (int argc, char ** argv)
{
time_t startRawTime, endRawTime;
time (&startRawTime);
printf ("%s", ctime (&startRawTime));
sleep (3); // any preprocessing of input data
time (&endRawTime);
printf ("%s", ctime (&endRawTime));
printf ("%.0fs\n", difftime (endRawTime, startRawTime));
time_t elapsed = static_cast<time_t>(difftime (endRawTime, startRawTime));
struct tm * ptm = gmtime (&elapsed);
printf ("%id %ih %im %is\n", ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
return 0;
}
Here is what it prints:
Mon Apr 9 14:43:16 2012
Mon Apr 9 14:43:19 2012
3s
1d 0h 0m 3s
Of course the last line is wrong (it should be "0d"). It seems it can be solved easily by printing ptm->tm_mday - 1. However, ptm->tm_mday will also be "1" when there really was one day elapsed between the two dates. And so in that case, I don't want to make it appear as "0d".
So is there a way to handle this properly? Or should I get the result of difftime as a double (that is, as a number of seconds) and then calculate myself the number of sec/min/hours/days?
Remark: my code is used only on Linux, compiled with gcc -lstdc++.
A time_t value represents a particular moment in time. The result of difftime is the interval, in seconds, between two moments. That's a very different thing.
In your code, difftime() returns 3.0, since there are 3 seconds between the two specified times. Converting that to time_t gives you a moment 3 seconds after the epoch; on most systems, that's going to be 3 seconds past midnight GMT on January 1, 1970. The tm_mday value is 1 because that was the first day of the month.
You might be able to make this work by subtracting 1 from the tm_mday value, since tm_mday is 1-based rather than 0-based. But you'll still get meaningless results for longer intervals. For example, an interval of 31.5 days will give you noon on February 1, because January has 31 days; that's not relevant to the information you're trying to get.
Just treat the result of difftime() as a double (because that's what it is) and compute the number of days, hours, minutes, and seconds by simple arithmetic.
(With some loss of portability, you can just subract the time_t values directly rather than using difftime(). That will make some of the arithmetic a little easier, but it will break on systems where a time_t value is something other than an integer count of seconds since some epoch. difftime() exists for a reason.)
Of course the last line is wrong (it should be "0d"). It seems it can
be solved easily by printing "ptm->tm_mday - 1". However, ptm->tm_mday
will also be "1" when there really was one day elapsed between the two
dates. And so in that case, I don't want to make it appear as "0d".
That's not correct; if the time interval is just over 1 day, ptm->tm_mday will be 2. You can verify this with a small modification to your code:
time (&endRawTime);
endRawTime += 86400; // add this line
printf ("%s", ctime (&endRawTime));
When I make this change, I get this output:
Mon Apr 9 13:56:49 2012
Tue Apr 10 13:56:52 2012
86403s
2d 0h 0m 3s
which could be corrected by subtracting 1 from ptm->tm_mday. But again, that's not the right approach.
Here's an example using the <chrono> library, a typesafe timing library that will prevent you from making the kind of mistake you're making. In chrono time_points and durations are not interchangeable, and if you try to use them that way then you get compiler errors.
#include <chrono>
#include <iostream>
#include <thread>
#include <cassert>
template<typename Rep,typename Period>
void print_duration(std::chrono::duration<Rep,Period> t) {
assert(0<=t.count() && "t must be >= 0");
// approximate because a day doesn't have a fixed length
typedef std::chrono::duration<int,std::ratio<60*60*24>> days;
auto d = std::chrono::duration_cast<days>(t);
auto h = std::chrono::duration_cast<std::chrono::hours>(t - d);
auto m = std::chrono::duration_cast<std::chrono::minutes>(t - d - h);
auto s = std::chrono::duration_cast<std::chrono::seconds>(t - d - h - m);
if(t>=days(1))
std::cout << d.count() << "d ";
if(t>=std::chrono::hours(1))
std::cout << h.count() << "h ";
if(t>=std::chrono::minutes(1))
std::cout << m.count() << "m ";
std::cout << s.count() << "s";
}
int main() {
auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_for(std::chrono::seconds(3));
auto finish = std::chrono::steady_clock::now();
print_duration(finish-start);
std::cout << '\n';
}
Notes for GCC
Older versions of GCC have monotonic_clock instead of steady_clock. 4.7 has steady_clock.
In order to access std::this_thread::sleep_for you may have to define _GLIBCXX_USE_NANOSLEEP for some reason.