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

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

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++ get start of this current year in unixtime

I'm looking for a way to retrieve the start of the current year as a unix timestamp.
For example if we're on 2017-10-16 the unix timestamp is 1523318400. I have to retrieve 1483228800 (2017-01-01) instead. And it must work for the next years too of course.
There are functions to add and subtract a number of months, days, minutes and seconds from a time_t, which can be used to calculate the the time_t for a point in the past, but it looks quite awkward to find the correct number of units to remove. (cpp reference : time_point). I also looked at original C function mktime. However, whilst creating a time_t and then a struct tm*, the issue is correctly generating a timezone correct version.
So my solution is something like this....
int getYear(time_t now)
{
struct tm * tnow = std::gmtime(&now);
return tnow->tm_year + 1900;
}
std::time_t calculateYear( int currentYear )
{
int epochYear = currentYear - 1970;
int leapYears = (epochYear + 1) / 4;
time_t result = epochYear * 24 * 60 * 60 * 365;
result += leapYears * 24 * 60 * 60;
return result;
}
The code is good for years between 1970 (first time_t value) and 2100, which is not a leap year from a 100 year rule.
The number of leap years is strange, as whilst 2012 is a leap year, it is 2013 which is the first year beginning to count it.
You could use Howard Hinnant's free, open-source C++11/14/17 date/time library. It would be as simple as this:
#include "date/date.h"
date::sys_seconds
start_of_year(date::sys_seconds t)
{
using namespace date;
return sys_days{year_month_day{floor<days>(t)}.year()/jan/1};
}
You could use it like this:
#include <iostream>
int
main()
{
using date::operator<<;
using namespace std::chrono_literals;
std::cout << start_of_year(date::sys_seconds{1523318400s}) << '\n';
}
This outputs:
1514764800s
Note that this is not the answer you said you wanted. However it is correct. You can debug this discrepancy with this library as well:
std::cout << date::sys_seconds{1523318400s} << '\n';
This outputs:
2018-04-10 00:00:00
instead of 2017-10-16. You can find the Unix Time stamp for 2017-10-16 with:
using namespace date::literals;
std::cout << date::sys_seconds{date::sys_days{2017_y/10/16}}.time_since_epoch() << '\n';
which outputs:
1508112000s
And:
std::cout << start_of_year(date::sys_seconds{1508112000s}).time_since_epoch() << '\n';
will output:
1483228800s
You can also use this library to find the current year:
date::year
current_year()
{
using namespace date;
using namespace std::chrono;
return year_month_day{floor<days>(system_clock::now())}.year();
}
And you could rewrite (or overload) start_of_year to take a date::year instead of (or in addition to) date::sys_seconds:
date::sys_seconds
start_of_year(date::year y)
{
using namespace date;
return sys_days{y/jan/1};
}
And now you can write:
int
main()
{
using date::operator<<;
std::cout << start_of_year(current_year()).time_since_epoch() << '\n';
}
which currently outputs:
1483228800s

How to get datetime in ISO 8601 format on Windows?

What's a standard way to get a date time in ISO8601 format on Windows using C++? Specifically, I would like it to be formatted as:
2017-02-22T10:00:00.123-05:00
2017-02-22T10:00:00.123 >>> -05:00 <<< # how to print the offset?
I was looking into combining the output of GetLocalTime and GetTimeZoneInformation, but this looks esoteric. There are similar questions on SO, however, I've not found a single one that prints UTC offset in a desired format. Is there a better approach?
The format specifier %z gives you the timezone offset as described in the documentation (e.g. MSDN on strftime) but lefts out the ':'. You can use it like this to get the ':' into your string:
struct tm tmNow;
time_t now = time(NULL); // Get the current time
_localtime64_s(&tmNow, &now);
char bufferTime[26];
char bufferTimezoneOffset[6];
size_t tsizTime = strftime(bufferTime, 26, "%Y-%m-%dT%H:%M:%S", &tmNow); // The current time formatted "2017-02-22T10:00:00"
size_t tsizOffset = strftime(bufferTimezoneOffset, 6, "%z", &tmNow); // The timezone offset -0500
strncpy_s(&bufferTime[tsizTime], 26, bufferTimezoneOffset, 3); // Adds the hour part of the timezone offset
bufferTime[tsizTime + 3] = ':'; // insert ':'
strncpy_s(&bufferTime[tsizTime + 4], 26, &bufferTimezoneOffset[3], 3); // Adds the minutes part of the timezone offset
puts(bufferTime); // Your output: "2017-02-22T10:00:00-05:00"
I left out the milliseconds, as they are not part of the localtime as far as I know.
Maybe something like this. We call GetLocalTime and GetTimeZoneInformation then pass it to the function which returns formatted string.
This is written quickly, not tested besides observing the fact it returns correct result on my machine now. It operates on the fact that SYSTEMTIME has a member Bias where UTC = Localtime + Bias and Bias is set in minutes. So get hours by dividing by 60 and taking absolute value of that. Then we get the minutes in similar way and set the sign depending on if Bias > 0
#include <Windows.h>
#include <string>
#include <sstream>
#include <iomanip>
#include <cmath>
std::string format_system_time(const SYSTEMTIME& sys_time, const TIME_ZONE_INFORMATION& time_zone)
{
std::ostringstream formatted_date_time;
formatted_date_time << std::setfill('0');
formatted_date_time << sys_time.wYear << "-" << std::setw(2) << sys_time.wMonth << "-" <<
std::setw(2) << sys_time.wDay << "T" << std::setw(2) << sys_time.wHour << ":" <<
std::setw(2) << sys_time.wMinute << ":" << std::setw(2) << sys_time.wSecond << "." <<
std::setw(3) << sys_time.wMilliseconds;
//UTC = localtime + bias; bias is in minutes
int utc_offset_hours = time_zone.Bias / 60;
int utc_offset_minutes = std::abs(time_zone.Bias - (utc_offset_hours * 60));
char offset_sign = time_zone.Bias > 0 ? '-' : '+';
formatted_date_time << offset_sign << std::setw(2) << std::abs(utc_offset_hours) << ":" << utc_offset_minutes;
return formatted_date_time.str();
}
int main(int argc, char* argv[])
{
SYSTEMTIME date_and_time;
GetLocalTime(&date_and_time);
TIME_ZONE_INFORMATION time_zone;
GetTimeZoneInformation(&time_zone);
auto& formatted_date_time = format_system_time(date_and_time, time_zone);
return 0;
}
I don't think there is a drop-in solution for c++ on Windows. The closest you can get is InternetTimeFromSystemTime but it is only documented to support RFC1123.
You probably have to code it yourself with GetLocalTime + GetTimeZoneInformation + wsprintf (or GetTimeZoneInformationForYear if you are not dealing with the current time).
Using Howard Hinnant's free, open-source timezone library, which works on VS-2013 and later, but does require some installation:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace std;
using namespace std::chrono;
using namespace date;
auto zt = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
cout << format("%FT%T%Ez\n", zt);
}
This just output for me:
2017-02-22T17:29:03.859-05:00

C++: Converting date-time between timezones keeping precision

Consider the input: 2014-04-14T16:28:07.023 (no time-zone, milliseconds precision)
I parsed it and I have the parts as numbers.
The input is always considered to be in UTC
I want to display it as local time
I want to keep the milliseconds precision when displaying
I have C++98 and boost 1.51.
I inspected high_resolution_clock and system_clock, but was unable to produce the final plot for the problem yet.
As requested in the comments to post as an answer, here is how it can be done without Boost:
#include <iostream>
#include <stdlib.h>
#include <time.h>
int main() {
int year, month, day, hour, minute, second, millisecond;
if (std::cin >> year >> month >> day >> hour >> minute >> second >> millisecond) {
struct tm utc;
utc.tm_year = year;
utc.tm_mon = month;
utc.tm_mday = day;
utc.tm_hour = hour;
utc.tm_min = minute;
utc.tm_sec = second;
utc.tm_isdst = 0;
time_t time = timegm(&utc);
if (time == (time_t) -1)
abort();
struct tm *local = localtime(&time);
if (localtime == NULL)
abort();
year = local->tm_year;
month = local->tm_mon;
day = local->tm_mday;
hour = local->tm_hour;
minute = local->tm_min;
second = local->tm_sec;
std::cout << year << ' ' << month << ' ' << day << ' ' << hour << ' ' << minute << ' ' << second << ' ' << millisecond << std::endl;
}
}
Note that the millisecond variable is read from input, and written to output, without any modification.
This uses the non-standard timegm function, but the documentation for that function contains a more portable implementation that you could include, if you want.
I have a solution which will be sufficient for me, but I don't know if it is the best approach in general or not. I'm about to use boost::posix_time::ptime and boost::date_time's c_local_adjustor:
#include <iostream>
#include <boost/date_time.hpp>
#include <boost/date_time/c_local_time_adjustor.hpp>
int main()
{
typedef boost::posix_time::ptime TP;
typedef boost::date_time::c_local_adjustor<TP> local_adj;
TP tUTC(boost::gregorian::date(2014,4,13),boost::posix_time::millisec(23));
TP tLocal(local_adj::utc_to_local(tUTC));
std::cout << boost::posix_time::to_simple_string(tUTC) << std::endl;
std::cout << boost::posix_time::to_simple_string(tLocal) << std::endl;
return 0;
}
Will print:
2014-Apr-13 00:00:00.023000
2014-Apr-13 02:00:00.023000
I did'nt use using namespace to show where is what. The ptime class has accessors to every detail I need. The c_local_adjustor does not have local_to_utc method, but it can be worked around.
(I got nowhere with chrono, I was able to do only circles in the documentation.)

Capturing a time in milliseconds

The following piece of code is used to print the time in the logs:
#define PRINTTIME() struct tm * tmptime;
time_t tmpGetTime;
time(&tmpGetTime);
tmptime = localtime(&tmpGetTime);
cout << tmptime->tm_mday << "/" <<tmptime->tm_mon+1 << "/" << 1900+tmptime->tm_year << " " << tmptime->tm_hour << ":" << tmptime->tm_min << ":" << tmptime->tm_sec<<">>";
Is there any way to add milliseconds to this?
To have millisecond precision you have to use system calls specific to your OS.
In Linux you can use
#include <sys/time.h>
timeval tv;
gettimeofday(&tv, 0);
// then convert struct tv to your needed ms precision
timeval has microsecond precision.
In Windows you can use:
#include <Windows.h>
SYSTEMTIME st;
GetSystemTime(&st);
// then convert st to your precision needs
Of course you can use Boost to do that for you :)
//C++11 Style:
cout << "Time in Milliseconds =" <<
chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now().time_since_epoch()).count()
<< std::endl;
cout << "Time in MicroSeconds=" <<
chrono::duration_cast<chrono::microseconds>(chrono::steady_clock::now().time_since_epoch()).count()
<< std::endl;
You need a timer with a higher resolution in order to capture milliseconds. Try this:
int cloc = clock();
//do something that takes a few milliseconds
cout << (clock() - cloc) << endl;
This is of course dependent on your OS.
The high resolution timers are usually gettimeofday on Linux style platforms and QueryPerformanceCounter on Windows.
You should be aware that timing the duration of a single operation (even with a high resolution timer) will not yield accurate results. There are too many random factors at play. To get reliable timing information, you should run the task to be timed in a loop and compute the average task time. For this type of timing, the clock() function should be sufficient.
If you don't want to use any OS-specific code, you can use the ACE package which supplies the ACE_OS::gettimeofday function for most standard operating systems.
For example:
ACE_Time_Value startTime = ACE_OS::gettimeofday();
do_something();
ACE_Time_Value endTime = ACE_OS::gettimeofday();
cout << "Elapsed time: " << (endTime.sec() - startTime.sec()) << " seconds and " << double(endTime.usec() - startTime.usec()) / 1000 << " milliseconds." << endl;
This code will work regardless of your OS (as long as ACE supports this OS).
In Ubuntu 16.04 this worked for me...
const std::string currentDateTime() {
char fmt[64], buf[64];
struct timeval tv;
struct tm *tm;
gettimeofday(&tv, NULL);
tm = localtime(&tv.tv_sec);
strftime(fmt, sizeof fmt, "%Y-%m-%d %H:%M:%S.%%06u", tm);
snprintf(buf, sizeof buf, fmt, tv.tv_usec);
return buf;
}
Then, with...
std::cout << currentDateTime();
I get...
2016-12-29 11:09:55.331008
New answer for old question using C++11 or C++14 and this free, open-source library:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto now = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
cout << format("%e/%m/%Y %T", now) << '\n';
}
This just output for me:
16/01/2017 15:34:32.167
which is my current local date and time to millisecond precision. By eliminating the floor<milliseconds>() you will automatically get whatever precision your system_clock has.
If you wanted the result as a UTC timestamp instead of a local timestamp, it is even easier:
auto now = floor<milliseconds>(system_clock::now());
cout << format("%e/%m/%Y %T", now) << '\n';
And if you want a UTC timestamp and you aren't picky about the precision or the format, you can just:
cout << system_clock::now() << '\n';
which just output for me:
2017-01-16 20:42:11.267245