C++ Specific Date Time Values to Milliseconds and vice versa - c++

I have specific date and time values in milliseconds detail such as;
year = 2020
month = 8 (August)
day = 5
hour = 15
minute = 45
second = 58
milliseconds = 997
I do not want to get the current date and time in milliseconds.
I need to convert these values to a total milliseconds value and add a milliseconds value such as 40 milliseconds to total milliseconds value. Then I need to convert the resulting milliseconds value to date and time again.
How can I do that? I came up with the following code, but there is no millisecond resolution in tm;
tm specTime;
specTime.tm_year = dataLine.year - 1900;
specTime.tm_mon = dataLine.month - 1;
specTime.tm_mday = dataLine.day;
specTime.tm_hour = dataLine.hour;
specTime.tm_min = dataLine.min;
specTime.tm_sec = dataLine.sec;
time_t millis = mktime(&specTime);

Related

Converting time string to seconds in C++ issues

I'm writing a C++ application on an embedded ARM device running Embedded Linux.
I am trying to convert a date and time string to seconds, subtract it from the current time in seconds, and taking action if the number of seconds elapsed is greater than a certain number.
Something that should be quite simple to implement has proved quite tricky, and i'm not sure why.
The time difference i'm calculating turns out to be a massive number, when it should really be a low number. See my below code. I'm manually hardcoding a time and date string for testing.
std::string timestr = "2020-12-21T16:07:00";
struct tm t = {0};
sscanf(timestr.c_str(), "%04d-%02d-%02dT%02d:%02d:%02d",
&t.tm_year, &t.tm_mon, &t.tm_mday,
&t.tm_hour, &t.tm_min, &t.tm_sec);
t.tm_year -= 1900; // This is required because my year should be the number of years since 1900
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&t));
auto now = std::chrono::system_clock::now();
auto now_s = std::chrono::time_point_cast<std::chrono::seconds>(now);
auto tp_s = std::chrono::time_point_cast<std::chrono::seconds>(tp);
std::chrono::duration<double> diff = now-tp; // Huge number
auto elapsed = now_s - tp_s; // This value is massive and not as expected when printed out
For those who are interested, I solved this problem.
Not only should we subtract 1900 from the number of years before calling std::mktime(&t) .
t.tm_year -= 1900;
but also 1 must be subtracted from the number of months t.tm_mon -= 1 .
The months are numbered from 0 to 11 and not 1 to 12 as we would expect.
This explains why there was a big difference in seconds.

Alternative to mktime in C++

uint64_t timestamp_nanoseconds = 634019142119225390;
time_t result = timestamp_nanoseconds / 1000000000;
struct tm * timeinfo = gmtime(&result);
struct tm dateInfo ;
dateInfo.tm_mday = timeinfo->tm_mday ;
dateInfo.tm_mon = timeinfo->tm_mon ;
dateInfo.tm_year = timeinfo->tm_year ;
dateInfo.tm_hour = 0 ;
dateInfo.tm_min = 0 ;
dateInfo.tm_sec = 0 ;
time_t NoOfSecInDate = mktime ( &dateInfo );
From the input timestamp in nanoseconds, we can get the date as shown in the code which got set in dateInfo structure. From that point, We need to find elapsed time in seconds from the midnight of the input date.
We get input as elapsed time in nanoseconds since epoch Jan 1 1970. Say for example , 634019142119225390. From that we extract date with time set to 00:00:00 , we need to find the elapsed time in unsigned integer representing elapsed time in nanoseconds since Unix epoch 00:00 UTC on 1st January 1970 from midnight of that date.
Solution should be for any given timestamp and not for a current date.
In this above code , we find that mktime function takes about 64 microseconds to complete which is lot of time and is not expected
Do you have any other alternative to mktime function which achieves the same result that returns the elapsed time in seconds since epoch but with lesser amount of time.
This can be done highly efficiently with <chrono> and Howard Hinnant's free, open-source, date-time library.
Given:
uint64_t timestamp_nanoseconds = 634019142119225390;
Form a std::chrono::time_point based on system_clock and nanoseconds. The date-time library makes that very easy with a template type alias for such types:
sys_time<nanoseconds> ts{nanoseconds{timestamp_nanoseconds}};
Next you can truncate this nanosecond-precision time_point to a days precision time_point with:
auto sd = floor<days>(ts);
sd is a system_clock time_point counting days since the epoch (as opposed to nanoseconds). Next you can convert sd into a year_month_day, which is exactly what it sounds like: a {year, month, day} struct with getters for each field:
year_month_day ymd = sd;
And the relevant part for this question, this is how you get nanoseconds since midnight:
auto tod = ts - sd;
And if you want that in terms of seconds, it
auto tod = duration_cast<seconds>(ts - sd);
In all, to go from a uint64_t count of nanoseconds, to seconds in current day (UTC), and time it, it is:
auto t0 = steady_clock::now();
sys_time<nanoseconds> ts{nanoseconds{timestamp_nanoseconds}};
auto tod = duration_cast<seconds>(ts - floor<days>(ts));
auto t1 = steady_clock::now();
Given the input of 634019142119225390, and compiling this using clang on macOS at -O3, this results in:
tod == 15942s
and takes about 200ns.
This formulation will work correctly for both positive and negative inputs, due to the use of floor<days>, as opposed to duration_cast<days>. For example an input of -1'000'000'000 gives a time of day of 86399s.
You are trying to get the time at the start of the same UTC day. To do that, you can just do the calculation in seconds:
struct timezone tz = {0, 0};
struct timeval start, end;
gettimeofday(&start, &tz);
uint64_t timestamp_nanoseconds = 634019142119225390ULL;
time_t result = timestamp_nanoseconds / 1000000000ULL;
time_t day = result - (result % 86400);
gettimeofday(&end, &tz);
struct timeval dsec;
timersub(&end, &start, &dsec);
printf("calc took %ld sec %ld usec.\n",
dsec.tv_sec, dsec.tv_usec, s);
printf("UTC: %s",asctime(gmtime(&now)));
printf("Day: %s",asctime(gmtime(&day)));
This takes much less time:
calc took 0 sec 1 usec. total 0.000000
UTC: Sat Feb 3 04:25:42 1990
Day: Sat Feb 3 00:00:00 1990
If you want to get the start of the day in your local timezone you can add an offset to the calculation of day.

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);

hour, minutes,seconds to Time_t

I know the Current system time.
I know the estimated time of arrival of a place in the form of hours minutes and seconds.
I need to find the duration of travel. But the estimated time of arrival is in 12 hour format.
I have to write a program to find the time difference between these two ?
I thought of using difftime(time1,time2)
but this requires the datatype time_t. I know the time in parts. i.e. i know the hours, minutes and seconds separatley. Both current system time and Estimated time of arrival.
I need to find the time difference between the two. The ETA can be after 24 hours. then is there any way i can find out the number of days of travel. Because after 12PM time is set back. hence i'm not able to keep track of the days.
Any solution ?
I work on C++
A straight forward way using C/C++. This is not very robust, but should meet your given requirements.
#include <ctime>
tm source;
memset(&source, 0, sizeof(tm));
tm.tm_hour = hour; // 24 hour format, 0 = midnight, 23 = 11pm
tm.tm_min = min;
tm.tm_sec = sec;
tm.tm_mon = month; // 0 based, 0 = jan, 11 = dec
tm.tm_mday = 10;
tm.tm.year = year; // current - 1900
time_t src_t = mktime(&source);
time_t now = time(NULL);

Odd behavior of mktime()

Continuing on my attempt to create a DateTime class , I am trying to store the "epoch" time in my function:
void DateTime::processComponents(int month, int day, int year,
int hour, int minute, int second) {
struct tm time;
time.tm_hour = hour;
time.tm_min = minute;
time.tm_sec = second;
time.tm_mday = day;
time.tm_mon = month;
time.tm_year = year - 1900;
ticks_ = mktime(&time);
processTm(time);
}
void DateTime::processTm(struct tm time) {
second_ = time.tm_sec;
minute_ = time.tm_min;
hour_ = time.tm_hour;
weekday_ = time.tm_wday;
monthday_ = time.tm_mday;
yearday_ = time.tm_yday;
month_ = time.tm_mon;
year_ = time.tm_year + 1900;
}
For an arbitrary date, processComponents(5,5,1990,1,23,45) (June 6, 1990 1:23:45 am), it sets all values correctly and as expected.
However, upon further testing, I find that for processComponents(0,0,1970,0,0,0) (January 1, 1970, 12:00:00 am), mktime(&time) causes time to be screwed up:
time.tm_mon = 11;
time.tm_mday = 30;
time.tm_year = 69;
time.tm_hour = 23;
time.tm_min = 0;
time.tm_sec = 0;
time.tm_isdst = 0;
time.tm_gmtoff = -18000;
time.tm_zone = "EST";
time.tm_wday = 2;
time.tm_yday = 363;
Translating to a date of December 31, 1969 11:00:00 pm.
I can verify that mktime() is responsible, because by commenting out that line, it reports the date and time correctly as January 1, 1970 12:00:00 am.
Why is mktime() only messing up the epoch? And how should I fix / workaround this?
Thanks!
You're passing 0 as the day parameter and putting that into time.tm_mday. That component (and only that component) of struct tm is 1-based, not 0-based.
Don't ask me why.
To specify 01 Jan 1970, 12:00:00am you'd want to call it like so:
processComponents(0,1,1970,0,0,0);
And as sdtom mentioned, you'll want to make sure that tm_isdst is set appropriately - 0 for not in effect, positive for in effect, and negative for you don't know (in which case mktime() should try to guess).
Just to let you know, when I pass the date you have (0 Jan 1970, 00:00:00) to mktime() in MSVC 9 it returns an error (the passed in struct tm is untouched and the returned time_t value is -1).
Since it is off by one hour I would expect daylight savings time. Is the value of time.tm_isdst getting set somewhere? If you aren't setting it, it could be randomly getting set to 1 or 0 which would affect your results.
Passing all zeros to mktime() is interpreted as "Sun Jan 0 00:00:00 1900". Based on this, there needs to be some adjustments...
// the input is local time
// the output is seconds since the epoch
// The epoch is Jan 1, 1970 # 0:00 GMT
time_t mktime_wrapper( int month, int day, int year,
int hour=0, int min=0, int sec=0, bool isDST=-1
)
{
tm t;
t.tm_sec=sec, t.tm_min=min, t.tm_hour=hour, t.tm_isdst=isDST;
t.tm_mday=day, t.tm_mon=month-1, t.tm_year=year-1900;
return mktime( &t );
}