Parsing date and time string into a number to compare C++ - c++

My application receives a date and time string. I need to be able to parse this string and compare it to the current time in seconds.
I am parsing this as below into a struct tm t to get the year, month, day, hour, minute, and second separately.
std::string timestr = "2020-12-18T16:40:07";
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);
I'm not sure if I need to convert this to epoch time, but when I do , I get -1. I'm not sure why.
time_t t_of_day;
t_of_day = mktime(&t);
Do I actually need to convert this to epoch first?
What is the best way for me to get the current time in seconds and then compare it to the time information I get in t? Thanks.

You want C++ parsing:
https://en.cppreference.com/w/cpp/io/manip/get_time
std::stringstream timestr = "2020-12-18T16:40:07";
struct tm t = {0};
timestr >> std::get_time(&t, "%Y-%m-%dT%H:%M:%S");
I should note there is a bug in your code as: tm_year is not the same as year as we know it. This is the number of years since 1900!
https://www.cplusplus.com/reference/ctime/tm/
So your code needs another line:
t.tm_year -= 1900;
Note: std::get_time() already does that compensation.
This is probably why mktime() is returning -1 as the year 3920 is out of range.

Just use the features of chrono library:
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&t));
auto epoch = std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
but you don't need to convert it to epoch. Use std::chrono::time_point comparison like:
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&t));
auto now = std::chrono::system_clock::now();
std::cout << (tp == now) << std::endl;

Related

how many days left between 2 dates using std::chrono

I'm trying to check how many days are left for my application, one from the current time and the second one from a std::string that comes from a database, but every time I try to subtract the 2 dates using
std::chrono::duration<int>
I get "expected unqualified-d before = token", not sure what is chrono expecting below its my code
void Silo::RevisarDiasRestantes(){ // Check how many days are left, if the serial is 00 is for life
// obtain the current time with std::chrono and convert to struct tm * so it can be convert to an std::string
std::time_t now_c;
std::chrono::time_point<std::chrono::system_clock> now;
typedef std::chrono::duration<int> tiempo;
struct tm * timeinfo;
char buffer[80];
now = std::chrono::system_clock::now();
now_c = std::chrono::system_clock::to_time_t(now);
time (&now_c);
timeinfo = localtime(&now_c);
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
std::string str(buffer);
Log("Tiempo usando Chrono " + QString::fromStdString(str));
for (int a{0} ; a<1000 ; ) // just an standard for
{
a++;
}
// convert std::string to std::time_t and then convert to a std::chrono
std::string i = str;
std::chrono::time_point<std::chrono::system_clock> end;
struct std::tm tm;
std::istringstream iss;
iss.str(i);
iss >> std::get_time(&tm,"%Y:%m:%d %H:%M:%S");
std::time_t time = mktime(&tm);
end = std::chrono::system_clock::from_time_t(time);
tiempo = end - now; <-------------------- heres the problem
Log( "Diferencia de tiempo: " + QString::number(tiempo.count()));
}
Edit: one thing I didn't notice until today if I try using istringstream to std::get_time the program compiles but it fails at runtime, asking for a "missing basic_istringstream" in the dynamic library, so I can't use that; is there another alternative to give the string to get_time?
Edit2: I didn't notice until JhonFilleau pointed the problem, no more working at late hours, thannks
There's two problems, one of which is pointed out by JohnFilleau in the comments.
You are assigning to a type instead of to a variable. It is as if you are coding:
 
int = 3;
instead of:
int i = 3;
You need something like:
tiempo t = end - now;
You are trying to implicitly convert from the precision of system_clock::time_point (typically microseconds to nanoseconds), to a precision of seconds. And chrono won't let you do that conversion implicitly because it loses precision.
But you can force it with duration_cast:
tiempo t = std::chrono::duration_cast<std::chrono::seconds>(end - now);
Finally, there's no need for
typedef std::chrono::duration<int> tiempo;
This is just another name for seconds, but stored in an int instead of something less prone to overflow. auto can be more easily used here:
auto t = std::chrono::duration_cast<std::chrono::seconds>(end - now);
and the type of t is std::chrono::duration<I> where I is a signed integral type of at least 35 bits (typically 64 bits). And there is a convenience type alias for this type named std::chrono::seconds.
If you really want this type named tiempo then I recommend:
using tiempo = std::chrono::seconds;
// ...
auto t = std::chrono::duration_cast<tiempo>(end - now);
or:
tiempo t = std::chrono::duration_cast<tiempo>(end - now);

Convert a time (UTC ) given as a string to local time

I have a time string like this "132233" (Time only no date) and i want to convert it into local time.
So, in order to use the function localtime(), I first converted my string into time_t using mktime() (thanks to How to convert a string variable containing time to time_t type in c++? )and then printed the time after conversion using strftime as shown in (http://www.cplusplus.com/reference/ctime/strftime/)
I am getting a serious run time error. Can any one please tell me whats wrong. Thanks in advance
int main()
{
string time_sample="132233";
std::string s_hrs (time_sample.begin(), time_sample.begin()+2);
std::string s_mins (time_sample.begin()+2,time_sample.begin()+4);
std::string s_secs (time_sample.begin()+4,time_sample.begin()+6);
int hrs = atoi(s_hrs.c_str());
int mins = atoi(s_mins.c_str());
int secs = atoi(s_secs.c_str());
struct tm time_sample_struct = {0};
time_sample_struct.tm_hour = hrs;
time_sample_struct.tm_min = mins;
time_sample_struct.tm_sec = secs;
time_t converted_time;
converted_time = mktime(&time_sample_struct);
struct tm * timeinfo;
char buffer[80];
timeinfo = localtime(&converted_time);
strftime(buffer,80,"%I:%M:%S",timeinfo);
puts(buffer);
cout<<endl;
getch();
return 0;
}
Your problem is that if time_t is a 32 bit value, the earliest possible date it's capable of encoding (given a 1970-1-1 epoch) is 1901-12-13.
However you're not setting the date fields of your tm struct, which means it is defaulting to 0-0-0 which represents 1900-1-0 (since tm_day is 1-based, you actually end up with an invalid day-of-month).
Since this isn't representable by a 32-bit time_t the mktime function is failing and returning -1, a situation you're not checking for.
Simplest fix is to initialise the date fields of the tm struct to something a time_t can represent:
time_sample_struct.tm_year = 114;
time_sample_struct.tm_mday = 1;

Comparing two timestamp strings in C++

I have two timestamps stored as string variables. The timestamps are in the format dd/mm/yyyy-hh:mm:ss
I am trying to find the difference in seconds between the two timestamps (ignoring the dates).
(I haven't assigned strings to a and b but they hold a timestamp each)
It always outputs 0 for the number of seconds difference and I can't work out why.
std::string a, b; // hold timestamps
struct tm t, t1;
double seconds;
t.tm_hour = stoi(a.substr(11,2)); // stoi() cast substring to int
t.tm_min = stoi(a.substr(14,2));
t.tm_sec = stoi(a.substr(17,2));
t1.tm_hour = stoi(b.substr(11,2));
t1.tm_min = stoi(b.substr(14,2));
t1.tm_sec = stoi(b.substr(17,2));
seconds = difftime(mktime(&t1), mktime(&t));
std::cout<<seconds;
Don't use hardcoded substring values (1 minute vs 11 minute might make you go off if 01 notation isn't used... and you have months ,days and hours also to take into account).
Instead of hardcoding the offset try to go after the unique characters (for you to get the "seconds" , take account the only the string after the 2nd occurrence of ":" ).
Add following code after the defintions and before the assignments
// initialize time structures with all the details for 'now'
time_t ts;
time( &ts );
t = * localtime( &ts );
t1 = t;
I suggest use CTime to work with timestamp.
http://www.cplusplus.com/reference/ctime/
You can use this for storage and later, if you need, convert to string.
This would be a great reason to start with the Boost libraries, because Boost.Date_Time has exactly what you need. See the documentation about time durations.
Here is an example program:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
int main()
{
boost::posix_time::time_duration duration1 = boost::posix_time::duration_from_string("10:11:12");
boost::posix_time::time_duration duration2 = boost::posix_time::duration_from_string("10:12:15");
std::cout << (duration2 - duration1).total_seconds() << "\n";
}
Output: 63
Since you are already using substr and std::stoi, it should be easy for you to get the proper substrings from a and b to be passed to boost::posix_time::duration_from_string.

fully separated date with milliseconds from std::chrono::system_clock

My current pattern (for unix) is to call gettimeofday, cast the tv_sec field to a time_t, pass that through localtime, and combine the results with tv_usec. That gives me a full date (year, month, day, hour, minute, second, nanoseconds).
I'm trying to update my code to C++11 for portability and general good practice. I'm able to do the following:
auto currentTime = std::chrono::system_clock::now( );
const time_t time = std::chrono::system_clock::to_time_t( currentTime );
const tm *values = localtime( &time );
// read values->tm_year, etc.
But I'm stuck on the milliseconds/nanoseconds. For one thing, to_time_t claims that rounding is implementation defined (!) so I don't know if a final reading of 22.6 seconds should actually be 21.6, and for another I don't know how to get the number of milliseconds since the previous second (are seconds guaranteed by the standard to be regular? i.e. could I get the total milliseconds since the epoch and just modulo it? Even if that is OK it feels ugly).
How should I get the current date from std::chrono::system_clock with milliseconds?
I realised that I can use from_time_t to get a "rounded" value, and check which type of rounding occurred. This also doesn't rely on every second being exactly 1000 milliseconds, and works with out-of-the-box C++11:
const auto currentTime = std::chrono::system_clock::now( );
time_t time = std::chrono::system_clock::to_time_t( currentTime );
auto currentTimeRounded = std::chrono::system_clock::from_time_t( time );
if( currentTimeRounded > currentTime ) {
-- time;
currentTimeRounded -= std::chrono::seconds( 1 );
}
const tm *values = localtime( &time );
int year = values->tm_year + 1900;
// etc.
int milliseconds = std::chrono::duration_cast<std::chrono::duration<int,std::milli> >( currentTime - currentTimeRounded ).count( );
Using this free, open-source library you can get the local time with millisecond precision like this:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << make_zoned(current_zone(),
floor<milliseconds>(system_clock::now())) << '\n';
}
This just output for me:
2016-09-06 12:35:09.102 EDT
make_zoned is a factory function that creates a zoned_time<milliseconds>. The factory function deduces the desired precision for you. A zoned_time is a pairing of a time_zone and a local_time. You can get the local time out with:
local_time<milliseconds> lt = zt.get_local_time();
local_time is a chrono::time_point. You can break this down into date and time field types if you want like this:
auto zt = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
auto lt = zt.get_local_time();
local_days ld = floor<days>(lt); // local time truncated to days
year_month_day ymd{ld}; // {year, month, day}
time_of_day<milliseconds> time{lt - ld}; // {hours, minutes, seconds, milliseconds}
// auto time = make_time(lt - ld); // another way to create time_of_day
auto y = ymd.year(); // 2016_y
auto m = ymd.month(); // sep
auto d = ymd.day(); // 6_d
auto h = time.hours(); // 12h
auto min = time.minutes(); // 35min
auto s = time.seconds(); // 9s
auto ms = time.subseconds(); // 102ms
Instead of using to_time_t which rounds off you can instead do like this
auto tp = std::system_clock::now();
auto s = std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
auto t = (time_t)(s.count());
That way you get the seconds without the round-off. It is more effective than checking difference between to_time_t and from_time_t.
I read the standard like this:
It is implementation defined whether the value is rounder or truncated, but naturally the rounding or truncation only occurs on the most detailed part of the resulting time_t. That is: the combined information you get from time_t is never more wrong than 0.5 of its granularity.
If time_t on your system only supported seconds, you would be right that there could be 0.5 seconds systematic uncertainty (unless you find out how things were implemented).
tv_usec is not standard C++, but an accessor of time_t on posix. To conclude, you should not expect any rounding effects bigger than half of the smallest time value difference your system supports, so certainly not more than 0.5 micro seconds.
The most straight forward way is to use boost ptime. It has methods such as fractional_seconds()
http://www.boost.org/doc/libs/1_53_0/doc/html/date_time/posix_time.html#date_time.posix_time.ptime_class
For interop with std::chrono, you can convert as described here: https://stackoverflow.com/a/4918873/1149664
Or, have a look at this question: How to convert std::chrono::time_point to calendar datetime string with fractional seconds?

Converting epoch time to "real" date/time

What I want to do is convert an epoch time (seconds since midnight 1/1/1970) to "real" time (m/d/y h:m:s)
So far, I have the following algorithm, which to me feels a bit ugly:
void DateTime::splitTicks(time_t time) {
seconds = time % 60;
time /= 60;
minutes = time % 60;
time /= 60;
hours = time % 24;
time /= 24;
year = DateTime::reduceDaysToYear(time);
month = DateTime::reduceDaysToMonths(time,year);
day = int(time);
}
int DateTime::reduceDaysToYear(time_t &days) {
int year;
for (year=1970;days>daysInYear(year);year++) {
days -= daysInYear(year);
}
return year;
}
int DateTime::reduceDaysToMonths(time_t &days,int year) {
int month;
for (month=0;days>daysInMonth(month,year);month++)
days -= daysInMonth(month,year);
return month;
}
you can assume that the members seconds, minutes, hours, month, day, and year all exist.
Using the for loops to modify the original time feels a little off, and I was wondering if there is a "better" solution to this.
Be careful about leap years in your daysInMonth function.
If you want very high performance, you can precompute the pair to get to month+year in one step, and then calculate the day/hour/min/sec.
A good solution is the one in the gmtime source code:
/*
* gmtime - convert the calendar time into broken down time
*/
/* $Header: gmtime.c,v 1.4 91/04/22 13:20:27 ceriel Exp $ */
#include <time.h>
#include <limits.h>
#include "loc_time.h"
struct tm *
gmtime(register const time_t *timer)
{
static struct tm br_time;
register struct tm *timep = &br_time;
time_t time = *timer;
register unsigned long dayclock, dayno;
int year = EPOCH_YR;
dayclock = (unsigned long)time % SECS_DAY;
dayno = (unsigned long)time / SECS_DAY;
timep->tm_sec = dayclock % 60;
timep->tm_min = (dayclock % 3600) / 60;
timep->tm_hour = dayclock / 3600;
timep->tm_wday = (dayno + 4) % 7; /* day 0 was a thursday */
while (dayno >= YEARSIZE(year)) {
dayno -= YEARSIZE(year);
year++;
}
timep->tm_year = year - YEAR0;
timep->tm_yday = dayno;
timep->tm_mon = 0;
while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) {
dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
timep->tm_mon++;
}
timep->tm_mday = dayno + 1;
timep->tm_isdst = 0;
return timep;
}
The standard library provides functions for doing this. gmtime() or localtime() will convert a time_t (seconds since the epoch, i.e.- Jan 1 1970 00:00:00) into a struct tm. strftime() can then be used to convert a struct tm into a string (char*) based on the format you specify.
see: http://www.cplusplus.com/reference/clibrary/ctime/
Date/time calculations can get tricky. You are much better off using an existing solution rather than trying to roll your own, unless you have a really good reason.
An easy way (though different than the format you wanted):
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
Output:
Wed Sep 21 10:27:52 2011
Notice that the returned result will be automatically concatenated with "\n".. you can remove it using:
std::string::size_type i = res.find("\n");
if (i != std::string::npos)
res.erase(i, res.length());
Taken from: http://en.cppreference.com/w/cpp/chrono/c/time
time_t t = unixTime;
cout << ctime(&t) << endl;
This code might help you.
#include <iostream>
#include <ctime>
using namespace std;
int main() {
// current date/time based on current system
time_t now = time(0);
// convert now to string form
char* dt = ctime(&now);
cout << "The local date and time is: " << dt << endl;
// convert now to tm struct for UTC
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "The UTC date and time is:"<< dt << endl;
}
To convert a epoch string to UTC
string epoch_to_utc(string epoch) {
long temp = stol(epoch);
const time_t old = (time_t)temp;
struct tm *oldt = gmtime(&old);
return asctime(oldt);
}
and then it can be called as
string temp = "245446047";
cout << epoch_to_utc(temp);
outputs:
Tue Oct 11 19:27:27 1977
If your original time type is time_t, you have to use functions from time.h i.e. gmtime etc. to get portable code. The C/C++ standards do not specify internal format (or even exact type) for the time_t, so you cannot directly convert or manipulate time_t values.
All that is known is that time_t is "arithmetic type", but results of arithmetic operations are not specified - you cannot even add/subtract reliably. In practice, many systems use integer type for time_t with internal format of seconds since epoch, but this is not enforced by standards.
In short, use gmtime (and time.h functionality in general).