Alternative to mktime in C++ - 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.

Related

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

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

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.

How to convert gps time to utc in c++?

I am collecting GPS time (in ns) from a sensor and I am looking for a way to convert that to a UTC time in C++.
I have a working code before in python.
time_gps = time_gps * 10**(-9) # Converts ns -> s
gps_epoch = pd.datetime(year=1980, month=1, day=6)
delta = pd.to_timedelta(time_gps, unit='s')
time = gps_epoch + delta - pd.to_timedelta(19, unit='s')
Using the link "Using std::chrono / date::gps_clock for converting a double gps timestamp to utc/tai" helped me figure out how to convert from GPS time to UTC.
uint64_t gps_input_ns = 1281798087485516800;
date::gps_time<std::chrono::nanoseconds> gt_nano{date::round<std::chrono::nanoseconds>(std::chrono::duration<uint64_t, std::nano>{gps_input_ns})};
auto utc_nano = date::clock_cast<date::utc_clock>(gt_nano);
std::cout << utc_nano << " UTC\n";
Output: 2020-08-18 15:01:09.485516800 UTC
My next question is, how can I extract the date and time from the variable "utc_nano"? I'm not very familiar with chrono or the date library and therefore having problems trying to separate the date and time. Any help would be much appreciated.
I'm assuming that leap seconds are important to you since you're dealing with gps time which represents the physical seconds that are labeled leap seconds by UTC. It is fairly tricky to manipulate date/times with leaps seconds, which is why Unix Time is so popular in computer systems.
In the C++20 chrono preview library, Unix Time is modeled by sys_time, whereas true UTC is modeled by utc_time. The only difference between these two models is that sys_time doesn't count leap seconds and utc_time does.
The advantage of sys_time is that there exists a fast and efficient algorithm for translating the time duration since 1970-01-01 00:00:00 into fields: year, month, day, hour, minute, second, subseconds. So if you want to break utc_time into these fields, the trick is to first turn utc_time into sys_time, while remembering whether or not your utc_time is referencing a leap second. Indeed, this is exactly what the streaming operator for utc_time does.
There exists a helper function get_leap_second_info to aid in doing this. This function takes a utc_time and returns a {is leap second, count of leap seconds} struct. The first member is true if the argument refers to a leap second, the second argument tells you how many leap seconds there have been between the argument and 1970. So the first step is to get this information for utc_nano:
auto info = get_leap_second_info(utc_nano);
Now you can create a sys_time with this information. Since sys_time is just like utc_time excluding leap seconds, you can just subtract off the number of leap seconds that have occurred:
sys_time<nanoseconds> sys_nano{utc_nano.time_since_epoch() - info.elapsed};
Now you have a count of nanoseconds in Unix Time. Truncating to days precision gives you a count of days in Unix Time:
auto sys_day = floor<days>(sys_nano);
sys_day is a date. The time of day is simply the difference between the nanoseconds-precision time_point and the days-precision time_point:
auto tod = sys_nano - sys_day;
tod is a time. It is the duration since midnight. It may be short by a second. That information is in info.is_leap_second.
If you want these types as "field types", you could convert sys_day to type year_month_day:
year_month_day ymd = sys_days;
year_month_day has getters for year, month and day.
You can convert tod into a {hours, minutes, seconds, nanoseconds} struct with:
hh_mm_ss hms{tod};
This has getters: hours(), minutes(), seconds(), and subseconds(). The above syntax assumes C++17. If in C++11 or 14, the syntax is:
hh_mm_ss<nanoseconds> hms{tod};
hh_mm_ss doesn't directly support a count of 60s, but that information is still in info.is_leap_second. E.g.
std::cout << hms.seconds().count() + info.is_leap_second << '\n';
That will output 60 if and only if info.is_leap_second is true.
You can even try this code which makes use of C time related functions
uint64_t ns = 1281798087485516800ULL + 315964800000000000ULL; // offset between gps epoch and unix epoch is 315964800 seconds
struct timespec ts;
ts.tv_sec = ns / 1000000000ULL;
ts.tv_nsec = ns % 1000000000ULL;
struct tm stm;
gmtime_r(&ts.tv_sec, &stm);
std::cout << stm.tm_year + 1900 << "-" << stm.tm_mon + 1 << "-" << stm.tm_mday << " " << stm.tm_hour << ":" << stm.tm_min << ":" << stm.tm_sec << std::endl;

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.

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