C++ Convert a string timestamp to std::chrono::system_clock::time_point - c++

I am trying to convert a string timestamp expressed in the following format: "28.08.2017 03:59:55.0007" to a std::chrono::system_clock::time_point by preserving the microseconds precision.
Is there any way to achieve this by using the standard library or boost?
Thanks.

I'd make use of Howard Hinnant's date library https://howardhinnant.github.io/date/date.html
e.g.:
std::stringstream str( "28.08.2017 03:59:55.0007" );
str.imbue( std::locale() );
std::chrono::time_point< std::chrono::system_clock, std::chrono::microseconds > result;
date::from_stream( str, "%d.%m.%Y %H:%M:%S", result );
std::cout << result.time_since_epoch().count();

Thought I'd add an answer since there isn't one available that exclusively uses the standard.
Given the input: istringstream timestamp("28.08.2017 03:59:55.0007"), this could be converted to a tm via get_time, but for the fractional seconds. The fractional seconds would need to be converted manually (this could be done by constructing chrono::microseconds from the rounded remainder divided by the micro ratio.) All this could be combined into something like this:
tm tmb;
double r;
timestamp >> get_time(&tmb, "%d.%m.%Y %T") >> r;
const auto output = chrono::time_point_cast<chrono::microseconds>(chrono::system_clock::from_time_t(mktime(&tmb))) + chrono::microseconds(lround(r * micro::den));
Live Example

One implementation can be:
#include <ctime>
#include <cmath>
#include <chrono>
#include <string>
#include <cstdint>
#include <stdexcept>
std::chrono::system_clock::time_point parse_my_timestamp(std::string const& timestamp) {
auto error = [&timestamp]() { throw std::invalid_argument("Invalid timestamp: " + timestamp); };
std::tm tm;
auto fraction = ::strptime(timestamp.c_str(), "%d.%m.%Y %H:%M:%S", &tm);
if(!fraction)
error();
std::chrono::nanoseconds ns(0);
if('.' == *fraction) {
++fraction;
char* fraction_end = 0;
std::chrono::nanoseconds fraction_value(std::strtoul(fraction, &fraction_end, 10));
if(fraction_end != timestamp.data() + timestamp.size())
error();
auto fraction_len = fraction_end - fraction;
if(fraction_len > 9)
error();
ns = fraction_value * static_cast<std::int32_t>(std::pow(10, 9 - fraction_len));
}
else if(fraction != timestamp.data() + timestamp.size())
error();
auto seconds_since_epoch = std::mktime(&tm); // Assumes timestamp is in localtime. For UTC use timegm.
auto timepoint_ns = std::chrono::system_clock::from_time_t(seconds_since_epoch) + ns;
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(timepoint_ns);
}

Related

how to use localtime() correctly?

In one of my utility programs, localtime() is used to covert unix timestamps to human readable date time.
The following code used to work in VS2010 while it fails to work in VS2019:
std::string sec = "1234123456";
int nsec = atoi(sec.c_str());
tm* t = localtime((time_t*)&nsec); // return null pointer
If I change the code in the following way, it will work also in VS2019:
std::string sec = "1234123456";
int nsec = atoi(sec.c_str());
time_t tt = nsec;
tm* t = localtime(&tt); // works
I have no idea why the additional int to time_t conversion is needed, any suggestion would be appreciated.
On most (if not all) modern compilers time_t is now a 64-bit integer. (time_t*)&nsec is therefore undefined behaviour as you are casting from one pointer type to a different one.
You fixed version is well defined but you will run into the reason that time_t is now 64-bit as 32-bit numbers will only work for times up to 2038 (assuming time_t is using the Unix epoch).
Unfortunately c++ doesn't provide a simple method for converting a string to time_t, to do it properly you'd need something like this:
#include <iostream>
#include <charconv>
time_t str_to_time_t(const std::string& str)
{
auto begin = str.c_str();
auto end = begin + str.size();
time_t time;
auto result = std::from_chars(begin, end, time);
if (result.ec != std::errc())
{
throw std::system_error(std::make_error_code(result.ec));
}
if (result.ptr != end)
{
throw std::invalid_argument("invalid time_t string");
}
return time;
}
int main()
{
std::string sec = "1234123456";
auto nsec = str_to_time_t(sec);
tm* t = localtime((time_t*)&nsec);
if (t)
{
std::cout << "parsed OK\n";
}
}

How to convert chrono::seconds to string in HH:MM:SS format in C++?

I have a function which accepts second as argument and returns a string in HH:MM:SS format. Without std::chrono, I can implement it like this:
string myclass::ElapsedTime(long secs) {
uint32_t hh = secs / 3600;
uint32_t mm = (secs % 3600) / 60;
uint32_t ss = (secs % 3600) % 60;
char timestring[9];
sprintf(timestring, "%02d:%02d:%02d", hh,mm,ss);
return string(timestring);
}
Using std::chrono, I can convert the argument to std::chrono::seconds sec {seconds};.
But the how can I convert it to string with the format?
I saw the good video tutorial from Howard Hinnant in https://youtu.be/P32hvk8b13M. Unfortunately, there is no example for this case.
Using Howard Hinnant's header-only date.h library it looks like ths:
#include "date/date.h"
#include <string>
std::string
ElapsedTime(std::chrono::seconds secs)
{
return date::format("%T", secs);
}
If you want to write it yourself, then it looks more like:
#include <chrono>
#include <string>
std::string
ElapsedTime(std::chrono::seconds secs)
{
using namespace std;
using namespace std::chrono;
bool neg = secs < 0s;
if (neg)
secs = -secs;
auto h = duration_cast<hours>(secs);
secs -= h;
auto m = duration_cast<minutes>(secs);
secs -= m;
std::string result;
if (neg)
result.push_back('-');
if (h < 10h)
result.push_back('0');
result += to_string(h/1h);
result += ':';
if (m < 10min)
result.push_back('0');
result += to_string(m/1min);
result += ':';
if (secs < 10s)
result.push_back('0');
result += to_string(secs/1s);
return result;
}
In C++20, you'll be able to say:
std::string
ElapsedTime(std::chrono::seconds secs)
{
return std::format("{:%T}", secs);
}
Once C++20 implementations land, you'll be able to do the following (untested code):
std::chrono::hh_mm_ss<std::chrono::seconds> tod{std::chrono::seconds(secs)};
std::cout << tod;
See time.hms.overview for more info.

C++ - compare two dates

I am writing a application which needs the possibility to compare two dates. This is what I have so far:
struct entry {
string text;
string date; // format: dd.mm.yyyy
bool finished;
};
string addNulls(int number, int cols) {
string num = to_string(number);
if (num.size() < cols) {
int count = cols - num.size();
for (int i = 0; i < count; i++) {
num = "0" + num;
}
}
return num;
}
// [...]
entry e = {"here is some text", "21.03.2019", false};
int day2 = atoi(e.date.substr(0, 2).c_str());
int month2 = atoi(e.date.substr(3, 2).c_str());
int year2 = atoi(e.date.substr(6, 4).c_str());
time_t t = time(0);
struct tm * now = localtime(&t);
string date1 = e.date.substr(6, 4) + "-" + e.date.substr(3, 2) + "-" + e.date.substr(0, 2) + " 00:00:00";
string date2 = addNulls(now->tm_year, 4) + "-" + addNulls(now->tm_mon, 2) + "-" + addNulls(now->tm_mday, 2) + " 00:00:00";
if(date2 > date1) {
// do something
}
the code gets an "entry" struct which contains a date. Than the code compares the date with the actual time. The problem is, it does not work! I run some tests with some example content, but the result (date2 > date1) returns false.
Why?
I read this: C++ compare to string dates
I'm not actually answering your question. However I am offering you a solution. Have you considered a date/time library? Boost datetime is very popular.
If you are compiling in C++11 or later, I recommend this date time library, as it is header-only (eliminating the need to link to a library such as boost), and in my opinion, it has cleaner syntax (that is a very subjective and biased viewpoint).
This latter library builds on the C++11 <chrono> library. Here is your example code using this library:
#include "date.h"
#include <iostream>
#include <string>
struct entry {
std::string text;
date::year_month_day date;
bool finished;
};
int
main()
{
entry e = {"here is some text", date::day(21)/3/2019, false};
auto day2 = e.date.day();
auto month2 = e.date.month();
auto year2 = e.date.year();
auto t = std::chrono::system_clock::now();
auto date1 = date::sys_days{e.date};
auto date2 = t;
if (date2 > date1)
std::cout << "It is past " << e.date << '\n';
else
std::cout << "It is not past " << e.date << '\n';
}
Which currently outputs:
It is not past 2019-03-21
In C++14, the chrono literals make specifying literal times very compact:
using namespace std::literals;
auto date1 = date::sys_days{e.date} + 0h + 0min + 0s;
Also on the subject of literals, you can make the construction of entry slightly more compact if you drop in a using namespace date;:
entry e = {"here is some text", 21_d/3/2019, false};
Reusing a date or datetime class, or even creating your own, is easier than trying to use a string to hold a date. Additionally you get the type-safety of not accidentally adding a string to a date, when you meant to add a time duration to a time point.
Why don't you use strptime to parse your date strings, convert them to epoch times and then compare?
#include <time.h>
char *
strptime(const char *restrict buf, const char *restrict format,
struct tm *restrict tm);

Milliseconds since epoch to dateformat C++

I have the milliseconds since epoch (windows/gregorian) for a specific time in long long int and would like to convert it to human time such as yy-mm-dd-hh-mm-ss-milli. (My platform: Windows 7, 64bit)
Unfortunately, all solutions I have found so far can't deal with the milli second (long long int) part.
C++11 API is incomplete, so I had to invent a bicycle:
static long getTs() {
struct timeval tp;
gettimeofday(&tp, NULL);
long int ms = tp.tv_sec * 1000 + tp.tv_usec / 1000;
return ms;
}
static string format(long ts,string fmt,int cutBack=0){
time_t tt = ts/1000;
int microsec = ts%1000;
struct std::tm * ptm = std::localtime(&tt);
string fmtms=std::regex_replace(fmt, std::regex("%ms"), to_string(microsec));
std::stringstream ss;
ss << std::put_time(ptm, fmtms.c_str());
string ret = ss.str();
return ret.substr(0,ret.size()-cutBack);
}
std::cout << CommonUtils::format(CommonUtils::getTs(), "%Y-%m-%dT%H:%M:%S.%ms%Z")<<endl;
gives me: 2020-01-24T11:55:14.375+07, cutBack parameter is optional, it specifies how many characters to remove from the output string. It is useful when timezone format like +0700 is to long, and you just need +07.
Basically, you should be able to take whatever it is that you have that writes the formatted time without milliseconds, and add the remainder of the division of the number of millisconds by 1000. This should work because leap time is always an integer number of seconds.
Assuming C++11, you can try this:
#include <chrono>
#include <iomanip>
using namespace std;
using namespace chrono;
long long int milliSecondsSinceEpoch = ... // this is your starting point
const auto durationSinceEpoch = std::chrono::milliseconds(milliSecondsSinceEpoch);
const time_point<system_clock> tp_after_duration(durationSinceEpoch);
time_t time_after_duration = system_clock::to_time_t(tp_after_duration);
std::tm* formattedTime = std::localtime(&time_after_duration);
long long int milliseconds_remainder = milliSecondsSinceEpoch % 1000;
cout <<put_time(std::localtime(&time_after_duration), "%y-%m-%d-%H-%M-%S-") << milliseconds_remainder << endl;

How to get a list of days from an interval of timestamps

I'm trying to write a function that would give me an array of days that exists between two timestamps.
For example
getDays(int startTimestamp,int stopTimestamp);
the output would be
2011-11-05
2011-11-06
2011-11-07
Is there anyway of doing this in a clean way without having to do some heavy algo ?
I'm not familiar with C++ so I just want to make sure there is no function that could do this for me before I start writting a big function.
Cheers
Have a while loop
std::vector<std::string> dateList;
while ( startTimestamp < stopTimestamp )
{
//Use strftime to convert startTimestamp to your format
// append to dateList
//increment startTimestamp by 1 day depending on what unit it is
}
strftime documenation. If your units were time_t this is a specific example
std::vector<std::string> getDays(time_t startTimestamp,time_t stopTimestamp)
{
std::vector<std::string> dateList;
char buffer[256];
while ( startTimestamp < stopTimestamp )
{
struct tm * timeinfo;
timeinfo = localtime ( &startTimestamp );
strftime (buffer,256,"%Y-%m-%d",timeinfo);
dateList.push_back( buffer );
startTimestamp += 24 * 60 * 60;
}
return dateList;
}
From reading the docs, this should work on C++0x (but I haven't got a compliant compiler handy to test it myself)1:
#include <iostream>
#include <iomanip>
#include <ctime>
#include <chrono>
using namespace std::chrono;
template <typename Clock>
void dayList(Clock from, Clock till)
{
for (Clock day=from; day<=till; day += hours(24))
{
std::time_t day_c = system_clock::to_time_t(day);
std::cout << std::put_time(std::localtime(&day_c), "%Y-%M-%D") << '\n';
}
}
int main()
{
time_point<system_clock>
from = system_clock::now(),
till = from + hours(190000);
dayList(from, till);
}
1 Edit This suggests that you'd need to have VS2010 (or maybe gcc4.7?)
If you want to print both days when the start is "2011/11/18 23:59:59" and the end is "2011/11/19 00:00:01", you can start by aligning startTimestamp to midnight of its day.
A day has 24*60*60 seconds in it, so you would do:
int const seconds_per_day = 24 * 60 * 60;
int alignedStart = startTimestamp - startTimestamp % seconds_per_day;
Then you loop from that alignedStart until you reach or go past the stopTimestamp, advancing seconds_per_day each time.
A different implementation, transforming counting iterators using a lambda function:
#include <boost/iterator/counting_iterator.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <string>
#include <algorithm>
#include <vector>
#include <iterator>
#include <time.h>
#include <iostream>
std::vector<std::string> getDays(time_t start, time_t end) {
std::vector<std::string> ret;
std::transform(boost::make_counting_iterator<unsigned>(start/(24*3600)),
boost::make_counting_iterator<unsigned>(end/(24*3600)),
std::back_inserter(ret),
[](unsigned day) {
return to_iso_extended_string(boost::posix_time::from_time_t(day*3600*24)).substr(0,10);
});
return ret;
}
Tested with:
int main() {
const std::vector<std::string> days = getDays(0, time(NULL));
std::copy(days.begin(), days.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}