Most simple way to get string containing time interval - c++

I'm new to std::chrono and I'm looking for a simple way to construct a string containing a time interval formatted hhh:mm:ss (yes, 3 hour figures), indicating the difference between a start time point and now.
How would I go about this using a steady_clock? The examples on Cppreference don't quite fit this problem.

Any time you find yourself manually applying conversion factors among units with the <chrono> library, you should be asking yourself:
Why am I converting units manually? Isn't this what <chrono> is
supposed to do for me?!
A "conversion factor" is 60, or 1000, or 100, or whatever. If you see it in your code, you're opening yourself up to conversion factor errors.
Here is sasha.sochka's code rewritten without these conversion factors. And just to throw in how general this technique is, milliseconds are added for flare:
#include <chrono>
#include <string>
#include <sstream>
#include <iomanip>
#include <iostream>
int main() {
using namespace std::chrono;
steady_clock::time_point start;
steady_clock::time_point now = steady_clock::now();
auto d = now -start;
auto hhh = duration_cast<hours>(d);
d -= hhh;
auto mm = duration_cast<minutes>(d);
d -= mm;
auto ss = duration_cast<seconds>(d);
d -= ss;
auto ms = duration_cast<milliseconds>(d);
std::ostringstream stream;
stream << std::setfill('0') << std::setw(3) << hhh.count() << ':' <<
std::setfill('0') << std::setw(2) << mm.count() << ':' <<
std::setfill('0') << std::setw(2) << ss.count() << '.' <<
std::setfill('0') << std::setw(3) << ms.count();
std::string result = stream.str();
std::cout << result << '\n';
}
There are other ways to do this without exposed conversion factors, this way is only an example. My main point is: avoid hardcoding unit conversion factors in your code. They are error prone. Even if you get it right when you first code it, conversion factors are vulnerable to future code maintenance. You can future-proof your code by demanding that all unit conversions happen within the <chrono> library.

As Joachim Pileborg noted higher in the comments there is no function for format a string from a duration object. But you can do it using duration_cast to convert time difference first to hours and then minutes and seconds.
After that using C++11 to_string function you can concatenate them to get the resulting string.
#include <chrono>
#include <string>
#include <sstream>
#include <iomanip>
int main() {
using namespace std::chrono;
steady_clock::time_point start = /* Some point in time */;
steady_clock::time_point now = steady_clock::now();
int hhh = duration_cast<hours>(now - start).count();
int mm = duration_cast<minutes>(now - start).count() % 60;
int ss = duration_cast<seconds>(now - start).count() % 60;
std::ostringstream stream;
stream << std::setfill('0') << std::setw(3) << hhh << ':' <<
std::setfill('0') << std::setw(2) << mm << ':' <<
std::setfill('0') << std::setw(2) << ss;
std::string result = stream.str();
}

Related

Timestamp conversion using cplusplus [duplicate]

How do I get a uint unix timestamp in C++? I've googled a bit and it seems that most methods are looking for more convoluted ways to represent time. Can't I just get it as a uint?
C++20 introduced a guarantee that time_since_epoch is relative to the UNIX epoch, and cppreference.com gives an example that I've distilled to the relevant code, and changed to units of seconds rather than hours:
#include <iostream>
#include <chrono>
int main()
{
const auto p1 = std::chrono::system_clock::now();
std::cout << "seconds since epoch: "
<< std::chrono::duration_cast<std::chrono::seconds>(
p1.time_since_epoch()).count() << '\n';
}
Using C++17 or earlier, time() is the simplest function - seconds since Epoch, which for Linux and UNIX at least would be the UNIX epoch. Linux manpage here.
The cppreference page linked above gives this example:
#include <ctime>
#include <iostream>
int main()
{
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result))
<< result << " seconds since the Epoch\n";
}
#include<iostream>
#include<ctime>
int main()
{
std::time_t t = std::time(0); // t is an integer type
std::cout << t << " seconds since 01-Jan-1970\n";
return 0;
}
The most common advice is wrong, you can't just rely on time(). That's used for relative timing: ISO C++ doesn't specify that 1970-01-01T00:00Z is time_t(0)
What's worse is that you can't easily figure it out, either. Sure, you can find the calendar date of time_t(0) with gmtime, but what are you going to do if that's 2000-01-01T00:00Z ? How many seconds were there between 1970-01-01T00:00Z and 2000-01-01T00:00Z? It's certainly no multiple of 60, due to leap seconds.
As this is the first result on google and there's no C++20 answer yet, here's how to use std::chrono to do this:
#include <chrono>
//...
using namespace std::chrono;
int64_t timestamp = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
In versions of C++ before 20, system_clock's epoch being Unix epoch is a de-facto convention, but it's not standardized. If you're not on C++20, use at your own risk.
#include <iostream>
#include <sys/time.h>
using namespace std;
int main ()
{
unsigned long int sec= time(NULL);
cout<<sec<<endl;
}
I created a global define with more information:
#include <iostream>
#include <ctime>
#include <iomanip>
#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) // only show filename and not it's path (less clutter)
#define INFO std::cout << std::put_time(std::localtime(&time_now), "%y-%m-%d %OH:%OM:%OS") << " [INFO] " << __FILENAME__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") >> "
#define ERROR std::cout << std::put_time(std::localtime(&time_now), "%y-%m-%d %OH:%OM:%OS") << " [ERROR] " << __FILENAME__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") >> "
static std::time_t time_now = std::time(nullptr);
Use it like this:
INFO << "Hello world" << std::endl;
ERROR << "Goodbye world" << std::endl;
Sample output:
16-06-23 21:33:19 [INFO] main.cpp(main:6) >> Hello world
16-06-23 21:33:19 [ERROR] main.cpp(main:7) >> Goodbye world
Put these lines in your header file. I find this very useful for debugging, etc.
Windows uses a different epoch and time units: see
Convert Windows Filetime to second in Unix/Linux
What std::time() returns on Windows is (as yet) unknown to me (;-))

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

Create a time_point at seconds::max results in negative value

I'm trying to use a time_point to effectively represent forever by setting it to seconds::max which, I believe, should represent that much time since epoch. When doing this, though, I get -1 as the time since epoch in the resulting time_point. What am I not understanding?
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
int main() {
auto tp1 = system_clock::time_point( seconds::zero() );
auto tp2 = system_clock::time_point( seconds::max() );
cout << "tp1: " << duration_cast<seconds>(tp1.time_since_epoch()).count() << endl;
cout << "tp2: " << duration_cast<seconds>(tp2.time_since_epoch()).count() << endl;
return 0;
}
The output running that is:
tp1: 0
tp2: -1
Here's a little quick&dirty program to explore the limits of system_clock time_points at different precisions:
#include <chrono>
#include <iostream>
using days = std::chrono::duration
<int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
using years = std::chrono::duration
<double, std::ratio_multiply<std::ratio<146097, 400>, days::period>>;
template <class Rep, class Period>
void
max_limit(std::chrono::duration<Rep, Period> d)
{
std::cout << "[" << Period::num << '/' << Period::den << "] ";
std::cout << years{d.max()}.count() + 1970 << '\n';
}
int
main()
{
using namespace std;
using namespace std::chrono;
max_limit(nanoseconds{});
max_limit(microseconds{});
max_limit(milliseconds{});
max_limit(seconds{});
}
This will output the year (in floating point) that time_point<system_clock, D> will max out at for any duration D. This program outputs:
[1/1000000000] 2262.28
[1/1000000] 294247
[1/1000] 2.92279e+08
[1/1] 2.92277e+11
Meaning system_clock based on nanoseconds overflows in the year 2262. If you coarsen that to microseconds, you overflow in the year 294,247. And so on.
Once you coarsen to seconds, the max goes out to a ridiculous range. But when you convert that back to system_clock::time_point, which is at least as fine as microseconds, and perhaps as fine as nanoseconds (depending on your platform), you just blow it out of the water.
To solve your problem I recommend:
auto M = system_clock::time_point::max();
Adding a few more diagnostics shows the issue (on my system):
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
int main() {
auto tp1 = system_clock::time_point( seconds::zero() );
auto tp2 = system_clock::time_point( seconds::max() );
using type = decltype(system_clock::time_point(seconds::zero()));
cout << type::duration::max().count() << endl;
cout << type::duration::period::den << endl;
cout << type::duration::period::num << endl;
cout << seconds::max().count() << endl;
cout << milliseconds::max().count() << endl;
cout << "tp1: " << duration_cast<seconds>(tp1.time_since_epoch()).count() << endl;
cout << "tp2: " << duration_cast<seconds>(tp2.time_since_epoch()).count() << endl;
return 0;
}
For me, the denominator value is 1,000,000 for the system_clock's time_point. Thus max seconds is going to overflow it when converted up.

Boost log file names in a different timezone

I currently have a log process in boost
that is initialized with
keywords::file_name = (my_file_str + %Y-%m-%d_%H.%5N.log).c_str();
Is there any way to change the timezone of the datetime object? I would like to use a timezone that rolls at 5PM US/New_York namely EST-2EDT.
Yes, it is possible, here is a function generating a timestamp in US/NY:
#include <boost/date_time/local_time/local_time.hpp>
#include <sstream>
std::string getNYCTimestamp() {
boost::posix_time::ptime pt =
boost::posix_time::microsec_clock::universal_time();
boost::local_time::time_zone_ptr utc_zone(
new boost::local_time::posix_time_zone("UTC"));
boost::local_time::local_date_time utc_time(pt, utc_zone);
boost::local_time::time_zone_ptr nyc_zone(
new boost::local_time::posix_time_zone("EST-05:00:00EDT+01:00:00,M4.1.0/02:00:00,M10.5.0/02:00:00"));
boost::local_time::local_date_time nyc_time = utc_time.local_time_in(nyc_zone);
auto our_facet = new boost::gregorian::date_facet("%Y-%m-%d");
our_facet->format("%Y-%m-%d");
auto os = std::ostringstream();
os.imbue(std::locale(std::locale::classic(), our_facet));
boost::posix_time::time_duration t = nyc_time.time_of_day();
os << nyc_time.date() << "_"
<< std::setw(2) << std::setfill('0') << t.hours() << '-'
<< std::setw(2) << std::setfill('0') << t.minutes() << '-'
<< std::setw(2) << std::setfill('0') << t.seconds() << '.'
<< std::setw(6) << std::setfill('0') << t.fractional_seconds();
return os.str();
}
Note, that "%Y-%m-%d_%H.%5N" is most probably wrong, because:
it omits minutes and seconds
it tries to fit nanoseconds (9 digits) into 5 symbols.
I am not 100% sure this will suffice (since I don't know what your requirements are) but here : http://boost-log.sourceforge.net/libs/log/doc/html/log/tutorial/attributes.html is a discussion of log attributes. It seems to me that you may be able to add it a secondary timestamp that is adjusted fo timezone.

Time stamp for saving file or folder?

Is there a simpler way to do a time stamp for saving a file/creating a directory as a date time stamp ?
only using standard library (not boost). Is there a faster way to do it ?
This is my current code
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t tt = std::chrono::system_clock::to_time_t(now);
tm utc_tm = *gmtime(&tt);
oname.str("");
oname << (utc_tm.tm_year + 1900) << '-' << std::setfill('0') << std::setw(2) << (utc_tm.tm_mon + 1) << '-' << std::setfill('0') << std::setw(2) << utc_tm.tm_mday << " " << std::setfill('0') << std::setw(2)<< utc_tm.tm_hour <<':' << std::setfill('0') << std::setw(2) << utc_tm.tm_min <<':' << std::setfill('0') << std::setw(2) << utc_tm.tm_sec;
ts = oname.str();
There is a less tortuous way:
#include <string>
#include <ctime>
std::string get_timestamp()
{
auto now = std::time(nullptr);
char buf[sizeof("YYYY-MM-DD HH:MM:SS")];
return std::string(buf,buf +
std::strftime(buf,sizeof(buf),"%F %T",std::gmtime(&now)));
}
It is very probably also faster, because it is less tortuous, but that is
also very probably immaterial in a setting where disc I/O is in play.
This gives you the same timestamps as your own code, e.g.
2015-03-28 10:48:45
See std::time and
std::strftime to
understand how the desired formatting is achieved and note that std::strftime
returns the length of the string it has composed, excluding its nul-terminator.
This code is standard, but if you are working with MS VC++ 2013 or later then
you could also consider the use of std::put_time,
as in:
#include <iomanip>
#include <sstream>
#include <string>
#include <ctime>
std::string get_timestamp()
{
auto now = std::time(nullptr);
std::ostringstream os;
os << std::put_time(std::gmtime(&now),"%F %T");
return os.str();
}
which is simpler still. (I have not tested that.) std::put_time however
is unsupported by gcc as of 4.9.
Seemingly you want your timestamps formatted as YYYY-MM-DD HH:MM:SS. If they
are to be used in filenames, it would be more prudent to keep them free of spaces:
perhaps YYYY-MM-DD_HH:MM:SS.