Introduction
I tried to be a fancy guy who uses modern C++ standard library features, but I failed. This tip is written for me and for you. You're welcome to ask for changes and I will provide the details you asked for if I think that you will have a proper relation to this tip. This wheel is re-invented to have a possibility to print date and time in logger class. The main design approach was to have no dependencies except STD C++.
Background
You may often need to measure a period of time and/or print a time stamp into logs. Before C++11, you have to wrap a C-code into C++ or use it directly. It works, but your code became a mess of conditional compiling statements (in the case of system portable code). And you, definitely need a portable C++ library which covers all your requirements. And you've got it.
Using the Code
Copy, paste and adopt the code below. It is free.
To make the code below compilable, you have to include the files below:
#include <ctime>
#include <chrono>
At first, I'll give all my regards to Microsoft who make me write such annoying things like this, to make my core fully portable:
#if defined(_MSC_VER) || defined(__BORLANDC__)
#include <winerror.h>
struct tm *localtime_r(const time_t *timep, struct tm *result)
{
if (timep != NULL && result != NULL)
{
errno_t err = localtime_s(result, timep);
if (err != NOERROR)
{
return result;
}
}
return NULL;
}
#endif
Let's begin with the time. You think you need this to print high resolution logs:
std::chrono::time_point<std::chrono::steady_clock>
logClock = std::chrono::high_resolution_clock::now();
And you're wrong. The row below does not provide anything except itself. You cannot extract parts that you need except stupid constexpr
(tested in G++/MSVC). Instead of that useless row, you need this:
std::chrono::system_clock::duration
zero = std::chrono::system_clock::now().time_since_epoch();
I didn't find a proper conversion from the fancy data types into human readable strings which contain date and time, so I have to call for undead C-style conversion. At first, we have to convert our fancy time into seconds:
time_t ll = std::chrono::duration_cast<std::chrono::seconds>(zero).count();
At the second step, I use localtime_r()
to split monolite into parts (the code below is not perfect, protect yourself from the invalid data and corruption):
struct tm lt;
localtime_r(&ll, <);
Call strftime()
to get the string
representation:
char buf[100];
std::strftime(buf, sizeof(buf), "%d/%m/%y %H:%M:%S", <);
or ctime( )
if you need just a string
and don't care about the format.
And you may also need a fraction. As you might have seen, you've got them already. But you need a way to be sure that you extract them properly in a system independent way.
And you think - you can use a row like this:
std::chrono::duration<std::chrono::milliseconds> mils =
std::chrono::duration_cast<std::chrono::milliseconds>(zero).count();
And you're wrong again. Because you don't get the logic, neither do I. All you need in this case is:
long long mils = std::chrono::duration_cast<std::chrono::microseconds>(zero).count();
After that row, you will have a time in milliseconds. If you need milliseconds inside the second, just get them with %
operator:
mils %= 1000;
Now you are ready to be a fancy guy and you can create your own datetime
class as it was in old and good times. :-)
Points of Interest
Before writing this tip, I tried to find a proper solution of this issue at one page, but I failed. So I tried to combine all that I found out in my place.
History
- 12th August, 2019: First edition. I found a new tip and am full of positive emotions about it.