Difference between two timestamps in days C++ - c++

I have timestamps in the format (Year.Month.Day) in an XML file.
I need to find out the difference between two timestamps in days.
Sample Timestamps:
<Time Stamp="20181015">
<Time Stamp="20181012">
How can I find the number of days between the above timestamps?
Number of days = date2 - date1. I am considering all the days (don't need to skip weekends or any other day). Time-zone does not matter as well.
PS: I understand that I have to parse the timestamp from XML. I am stuck in the logic after parsing value.
Update-1: std::chrono::year and other such things are part of C++20. But I get a compilation error:
namespace "std::chrono" has no member "year"

There is the old fashioned way:
#include <ctime>
#include <iomanip> // std::get_time
#include <sstream>
// ...
std::string s1 = "20181015";
std::string s2 = "20181012";
std::tm tmb{};
std::istringstream(s1) >> std::get_time(&tmb, "%Y%m%d");
auto t1 = std::mktime(&tmb);
std::istringstream(s2) >> std::get_time(&tmb, "%Y%m%d");
auto t2 = std::mktime(&tmb);
auto no_of_secs = long(std::difftime(t2, t1));
auto no_of_days = no_of_secs / (60 * 60 * 24);
std::cout << "days: " << no_of_days << '\n';

You can use C++20's syntax today (with C++11/14/17) by downloading Howard Hinnant's free, open-source date/time library. Here is what the syntax would look like:
#include "date/date.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace date;
using namespace std;
istringstream in{"<Time Stamp=\"20181015\">\n<Time Stamp=\"20181012\">"};
const string fmt = " <Time Stamp=\"%Y%m%d\">";
sys_days date1, date2;
in >> parse(fmt, date1) >> parse(fmt, date2);
cout << date2 - date1 << '\n';
int diff = (date2 - date1).count();
cout << diff << '\n';
}
This outputs:
-3d
-3
If you don't need time zone support (as in this example), then date.h is a single header, header-only library. Here is the full documentation.
If you need time zone support, that requires an additional library with a header and source: tz.h/tz.cpp. Here is the documentation for the time zone library.

Related

Same function outputs different results when calculating difference between two timestamps

I'm currently facing a weird issue where the same function outputs a different result. The function is supposed to calculate the time difference between a provided date and the current time. Since this function is supposed to work with milliseconds, my function currently looks like this:
int calcDelay(std::string dropTime) {
struct tm tm;
std::istringstream iss(dropTime);
iss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
time_t time = mktime(&tm);
SYSTEMTIME t;
GetSystemTime(&t);
struct tm tm1;
memset(&tm1, 0, sizeof(tm1));
tm1.tm_year = t.wYear - 1900;
tm1.tm_mon = t.wMonth - 1;
tm1.tm_mday = t.wDay;
tm1.tm_hour = t.wHour - 1;
tm1.tm_min = t.wMinute;
tm1.tm_sec = t.wSecond;
time_t time2 = mktime(&tm1);
//std::cout << "Input:" << dropTime << " Output:" << (int)(difftime(time, time2) * 1000) - t.wMilliseconds << std::endl;
int retVal = (int)(difftime(time, time2) * 1000) - t.wMilliseconds;
return retVal;
}
The provided date (dropTime) is in UTC/GMT and the WinAPI function GetSystemTime should also return the time in UTC.
I have two different threads that call this function. When the first thread calls this function, it returns the correct time difference. However, when my other thread calls this function with the exactly same input it returns a value that is exactly 3600000 ms larger - this equals the time of exactly one hour.
What's the cause of this bug?
Edit: It seems that the bug is caused by the get_time function. Even though the same string (2021-05-25T21:03:04) is used to parse the time, it sometimes adds a hour and sometimes it doesn't...
Could it be that the get_time function simply cannot be used across multiple threads?
I appreciate all help.
In C++20 your calcDelay can be greatly simplified. And there exists a preview of this functionality in a free, open-source, header-only library1 which works with C++11/14/17.
#include "date/date.h"
#include <chrono>
#include <sstream>
int calcDelay(std::string dropTime) {
using std::chrono::milliseconds;
date::sys_time<milliseconds> time;
std::istringstream iss(dropTime);
iss >> date::parse("%Y-%m-%dT%H:%M:%S", time);
auto time2 = date::floor<milliseconds>(std::chrono::system_clock::now());
return (time - time2).count();
}
As you state in your question, the input is UTC, and the current time is UTC. Time zones are not involved. And unlike the "C version", this version optionally supports millisecond-precision input:
std::cout << calcDelay("2021-05-26T00:41:01.568") << '\n';
Output:
12456
To port the above calcDelay to C++20:
Drop #include "date/date.h"
Change date:: to std::chrono:: (3 places)
You can also (optionally) simplify the parse string from "%Y-%m-%dT%H:%M:%S" to "%FT%T".
Also optional, you could increase type safety in the client code by returning std::chrono::milliseconds instead of int.
1 Full disclosure: I am the lead author of this library. I am not pursuing any financial gain from this effort. But sometimes people get upset if I don't fully disclose this information.
t.wHour - 1 is incorrect. Both the tm and SYSTEMTIME structures use hours from 0...23.
According to std::get_time API, The I/O manipulator std::get_time uses the std::time_get facet of the I/O stream's locale to convert text input to a std::tm object. And maybe all of your threads which are in the same process have the same native locale which is default behavior. So GetSystemTime(&t); has no problem.
The follwing code is API’s example:
#include <iostream>
#include <sstream>
#include <locale>
#include <iomanip>
int main()
{
std::tm t = {};
std::istringstream ss("2011-Februar-18 23:12:34");
ss.imbue(std::locale("de_DE.utf-8"));
ss >> std::get_time(&t, "%Y-%b-%d %H:%M:%S");
if (ss.fail()) {
std::cout << "Parse failed\n";
} else {
std::cout << std::put_time(&t, "%c") << '\n';
}
}

C++ Parsing TimeZone From Date String

Hello I have the following date that I am consuming from an api
string sToParse = "2020-04-17T09:30:00-04:00";
which should be in human form "Friday, April 17, 2020 08:30:00" central time
or epoch of 1587130200
however this code
cout << "raw: " << sToParse << endl;
static const std::string dateTimeFormat { "%Y-%m-%dT%H:%M:%S%Z" };
istringstream ss{ sToParse };
tm dt;
ss >> get_time(&dt, dateTimeFormat.c_str());
cout << mktime(&dt) << endl;
Gives me an epoch of 1587137400 which is a human format of "Friday, April 17, 2020 10:30:00 AM" which is two hours difference. How do i get the %Z to process the timezone appropriately?
Thanks in advance for any help you can give
C++20 will do this:
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
int
main()
{
using namespace std;
using namespace std::chrono;
string sToParse = "2020-04-17T09:30:00-04:00";
cout << "raw: " << sToParse << endl;
static const std::string dateTimeFormat { "%Y-%m-%dT%H:%M:%S%z" };
istringstream ss{ sToParse };
sys_seconds dt;
ss >> parse(dateTimeFormat, dt);
cout << dt << '\n';
cout << dt.time_since_epoch() << '\n';
}
Output:
2020-04-17 13:30:00
1587130200s
This gets the "epoch time" you're looking for. The big difference is the use of %z instead of %Z. %z is the command for parsing the offset. %Z parses time zone abbreviation.
This doesn't get the "human time" you're expecting. The time printed out above is UTC. This is clearly correct on inspection: 13:30:00 is 4 hours after 09:30:00.
If you are wanting local time, it would be 2020-04-17 09:30:00, same as the input. To come up with 08:30:00 would require more information than you are providing above (e.g. output in some time zone other than that which has a UTC offset of -4h at this local time).
Also it is in general not possible to go from a UTC offset to a time zone name or abbreviation because more than one time zone will generally have the same UTC offset at any point in time.
If the C++20 <chrono> header isn't available for you (to the best of my knowledge it is not yet available anywhere), you can use a free, open-source preview of C+++20 <chrono>. For this problem, one only needs the header-only "date.h" from this preview. And everything is in namespace date instead of namespace std::chrono. It would look like this:
#include "date/date.h"
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
string sToParse = "2020-04-17T09:30:00-04:00";
cout << "raw: " << sToParse << endl;
static const std::string dateTimeFormat { "%Y-%m-%dT%H:%M:%S%z" };
istringstream ss{ sToParse };
sys_seconds dt;
ss >> parse(dateTimeFormat, dt);
cout << dt << '\n';
cout << dt.time_since_epoch() << '\n';
}
Update
With the new knowledge that the "expected human form" of the time should be US Central Time ("America/Chicago" in IANA terms), I'm updating the example code to show how that can be handled.
#include "date/tz.h"
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
string sToParse = "2020-04-17T09:30:00-04:00";
cout << "raw: " << sToParse << endl;
static const std::string dateTimeFormat { "%FT%T%z" };
istringstream ss{ sToParse };
sys_seconds utc;
ss >> parse(dateTimeFormat, utc);
zoned_seconds cst{"America/Chicago", utc};
cout << utc.time_since_epoch() << '\n';
cout << format("%F %T %Z\n", utc);
cout << format("%F %T %Z\n", cst);
}
A few changes above:
A new header is required to handle the time zone issues: "tz.h" (only in the C++20 preview library, not in C++20).
For parsing I've substituted "%Y-%m-%dT%H:%M:%S%z" for "%FT%T%z". These are equivalent. %F is just shorthand for %Y-%m-%d and %T is shorthand for %H:%M:%S. This change is not required.
I've renamed dt to utc to emphasize that this variable holds a UTC time point. This change is not required.
The new line constructs a zoned_time (with seconds precision) with the desired destination time zone ("America/Chicago"), and the UTC time point. This creates an object that knows all about the local time at this time point and in this time zone.
Then everything is printed out using the date::format function and the formatting string "%F %T %Z". %Z is used to output the time zone abbreviation to make the output more readable. In C++20, this will be std::format, and the formatting string will be "{:%F %T %Z}".
The output is now:
raw: 2020-04-17T09:30:00-04:00
1587130200s
2020-04-17 13:30:00 UTC
2020-04-17 08:30:00 CDT
If your computer's current time zone setting happens to be US Central, then the line that constructs zoned_seconds can also look like:
zoned_seconds cst{current_zone(), utc};
Or conversely, you can use this line to find the local time at utc in any time zone which your computer is set to.
Note that whether the time zone is specified with a name such as "America/Chicago", or with current_zone(), any changes of UTC offset within the specified time zone (such as daylight saving time) will be correctly taken into account.
With the preview C+++20 library, the use of the header "tz.h" is not header-only. It requires a single source file, tz.cpp. Here are instructions on how to compile it. But if you are using a fully conforming C++20 <chrono>, then the above will just work by removing #include "date/tz.h", using namespace date;, and adjusting the formatting string as noted in the 5th bullet above.

Convert Date-Time to Milliseconds - C++ - cross platform

I want to convert a string in the format of "20160907-05:00:54.123" into milliseconds.
I know that strptime is not available in Windows and I want to run my program in both windows and linux. I can't use third party libraries as well.
I can tokenize the string and convert it. But is there a more elegant way like using the strptime to do so?
What about std::sscanf?
#include <iostream>
#include <cstring>
int main() {
const char *str_time = "20160907-05:00:54.123";
unsigned int year, month, day, hour, minute, second, miliseconds;
if (std::sscanf(str_time, "%4u%2u%2u-%2u:%2u:%2u.%3u", &year, &month,
&day, &hour, &minute, &second,&miliseconds) != 7)
{
std::cout << "Parse failed" << std::endl;
}
else
{
std::cout << year << month << day << "-" << hour << ":"
<< minute << ":" << second << "." << miliseconds
<< std::endl;
}
}
Output (ideone):
201697-5:0:54.123.
However, you should make sure the input is valid (for example, day can be in the range of [0,99]).
Too bad about no 3rd party libraries, because here is one (MIT license) that is just a single header, runs on linux and Windows, and handles the milliseconds seamlessly:
#include "date.h"
#include <iostream>
#include <sstream>
int
main()
{
date::sys_time<std::chrono::milliseconds> tp;
std::istringstream in{"20160907-05:00:54.123"};
date::parse(in, "%Y%m%d-%T", tp);
std::cout << tp.time_since_epoch().count() << '\n';
}
This outputs:
1473224454123
Error checking is done for you. The stream will fail() if the date is invalid.
date::sys_time<std::chrono::milliseconds> is a type alias for std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>. I.e. it is from the family of system_clock::time_point, just milliseconds precision.
Fully documented:
https://howardhinnant.github.io/date/date.html
Doesn't get much more elegant than this.
Given the format of your string, it is fairly easy to parse it as follows (although a regex or get_time might be more elegant):
tm t;
t.tm_year = stoi(s.substr(0, 4));
t.tm_mon = stoi(s.substr(4, 2));
t.tm_mday = stoi(s.substr(6, 2));
t.tm_hour = stoi(s.substr(9, 2));
t.tm_min = stoi(s.substr(12, 2));
t.tm_sec = 0;
double sec = stod(s.substr(15));
Finding the time since the epoch can be done with mktime:
mktime(&t) + sec * 1000
Note that the fractional seconds need to be handled differently - unfortunately, tm has only integer seconds.
(See the full code here.)
Edit
As Mine and Panagiotis Kanavos correctly note in the comments, Visual C++ apparently supports get_time for quite a while, and it's much shorter with it (note that the fractional seconds need to be handled the same way, though).

Get current number of hours and minutes using chrono::time_point

I have been trying to find an example using std::chrono which simply gets a chrono::time_point and extracts the number of hours and number of minutes as integers.
I have:
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
but I cannot find out how to then extract the hours and the minutes (since midnight)? I am looking for something like:
int hours = now.clock.hours();
Here is a free, open-source date library which will do this for you. Feel free to inspect the code if you want to know exactly how it is done. You can use it to get the current hours and minutes since midnight in the UTC timezone like this:
#include "date/date.h"
#include <iomanip>
#include <iostream>
int
main()
{
auto now = date::floor<std::chrono::minutes>(std::chrono::system_clock::now());
auto dp = date::floor<date::days>(now);
auto time = date::make_time(now - dp);
int hours = time.hours().count();
int minutes = time.minutes().count();
std::cout.fill('0');
std::cout << std::setw(2) << hours << ':' << std::setw(2) << minutes << '\n';
}
If you want the information in some other timezone, you will need this additional IANA time zone parser (or you can write your own timezone management system). The above code would be modified like so to get the hours and minutes since midnight in the local timezone:
#include "date/tz.h"
#include <iomanip>
#include <iostream>
int
main()
{
auto zt = date::make_zoned(date::current_zone(),
std::chrono::system_clock::now());
auto now = date::floor<std::chrono::minutes>(zt.get_local_time());
auto dp = date::floor<date::days>(now);
auto time = date::make_time(now - dp);
int hours = time.hours().count();
int minutes = time.minutes().count();
std::cout.fill('0');
std::cout << std::setw(2) << hours << ':' << std::setw(2) << minutes << '\n';
}
These libraries are available on github here:
https://github.com/HowardHinnant/date
Here is a video presentation of the date library:
https://www.youtube.com/watch?v=tzyGjOm8AKo
And here is a video presentation of the time zone library:
https://www.youtube.com/watch?v=Vwd3pduVGKY
The problem is that there isn't really any such functionality in the standard library. You have to convert the time point to a time_t and use the old functions to get a tm structure.

C++ convert date formats

I would like to convert an int date like:
20111201
to string:
01DEC2011
Is there a fast date format conversion built into C++ (or maybe a bash system command I can execute instead) to do this or am I stuck making a switch for all of the months?
You could use the strptime to convert your string to a struct tm, then use strftime to reformat it:
#include <ctime>
#include <iostream>
#include <sstream>
int main()
{
std::ostringstream date1;
date1 << 20111201;
struct tm tm;
strptime(date1.str().c_str(), "%Y%m%d", &tm);
char date2[10];
strftime(date2, sizeof(date2), "%d%b%Y", &tm);
std::cout << date1.str() << " -> " << date2 << std::endl;
}
Output is:
20111201 -> 01Dec2011
Just need to convert the Dec to upper case if it's necessary.
Don't use bash here. The way to go is to use Boost in C++ for more reasons than I've time to list here, but ultimately it will be just as fast as most other solutions you'll encounter and unless your functionality is absolutely time critical, it won't make a great deal of difference anyway.
Also, It's going to be far more flexible and maintainable than all those crappy little hard coded date conversion routines that you always encounter.
The following code will do what you want.
#include <iostream>
#include <sstream>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/algorithm/string.hpp>
using namespace boost::gregorian;
using namespace std;
int main(int argc, char **argv)
{
int dateIn = 20111201;
// Read the date in from ISO format as an int.
ostringstream ss;
ss << dateIn;
date d(from_undelimited_string( ss.str() ));
// Set the output format
date_facet *fct = new date_facet("%d%b%Y"); // [1]
locale loc = locale(locale::classic(), fct);
// Render the date as a string;
ss.str("");
ss.imbue(loc);
ss << d;
string dateOut( ss.str() );
boost::to_upper( dateOut );
cout << dateOut << endl;
}
This gives the following output:
01DEC2011
Just changing the format string "%d%b%Y" at ref [1] will change to a different output format but remember I've converted it to uppercase as well.
There's nothing directly built-in, since this format for dates
is relatively rare. The simplest solution here would be to
break the date up into year month day using % and /
operators (e.g. month is value / 100 % 100), then format the
three values normally, using std::ostream, and looking up the
date in a table. (This would obviously require some error
checking, since not all integral values yield valid dates.)
New answer to old question. This answer traffics through the C++11/14 <chrono> library instead of C's tm or boost::date_time. Otherwise it is very similar to the existing answers. It requires this free, open-source library for the parsing and formatting.
#include "tz.h"
#include <iostream>
#include <locale>
#include <sstream>
int
main()
{
auto date1 = 20111201;
std::stringstream stream;
stream.exceptions(std::ios::failbit);
stream << date1;
std::chrono::system_clock::time_point tp;
date::parse(stream, "%Y%m%d", tp);
auto str = date::format("%d%b%Y", tp);
auto& ct = std::use_facet<std::ctype<char>>(std::locale::classic());
ct.toupper(&str.front(), &str.back()+1);
std::cout << str << '\n';
}
I've included stream.exceptions(std::ios::failbit); to noisily detect invalid "integer dates". And I've included old C++98 code to convert the string to uppercase (the locale dance at the end).
01DEC2011
One of the advantages of using a modern C++ date/time library is the ease with which changes can be made. For example, what if now you need to parse the timestamp not with day-precision, but with millisecond precision? Here is how that might be done:
auto date1 = 20111201093357.275L;
std::stringstream stream;
stream.exceptions(std::ios::failbit);
stream << std::fixed << date1;
std::chrono::system_clock::time_point tp;
date::parse(stream, "%Y%m%d%H%M%S", tp);
auto str = date::format("%d%b%Y %T", tp);
auto& ct = std::use_facet<std::ctype<char>>(std::locale::classic());
ct.toupper(&str.front(), &str.back()+1);
std::cout << str << '\n';
which outputs:
01DEC2011 09:33:57.275000
Or perhaps these timestamps are known to originate from Chatham Island off the coast of New Zealand and you need them in UTC. Just add one line after the parse:
tp = date::locate_zone("Pacific/Chatham")->to_sys(tp);
And now the output is:
30NOV2011 19:48:57.275000
Taking into account arbitrary timezones and subsecond precision is currently beyond the capabilities of all other C++ libraries.