Why is there no C++11 threadsafe alternative to std::localtime and std::gmtime?
Solution 1:
According to N2661, the paper that added <chrono>
:
This paper does not offer calendrical services except for a minimal mapping to and from C's
time_t
.As this paper does not propose a date/time library, nor specify epochs, it also does not address leap seconds. However, a date/time library will find this to be an excellent foundation on which to build.
This paper does not propose a general purpose physical quantities library.
This paper proposes a solid foundation that, in the future, could provide a compatible starting point for a general physical units library. While such a future library might take any of several forms, the present proposal stops well short of actually being a physical units library. This proposal is time-specific, and continues to be motivated by the time-related needs of the threading library.
The major goal of this proposal is to satisfy the needs of the standard library threading API in a manner which is easy to use, safe to use, efficient, and flexible enough to not be obsolete 10 or even 100 years from now. Every feature contained in this proposal is here for a specific reason with practical use cases as motivation. Things that fell into the category of "cool", or "that sounds like it might be useful", or "very useful but not needed by this interface" have not been included. Such items might appear in other proposals, and possibly target a TR.
Note that the major goal of <chrono>
is "to satisfy the needs of the standard library threading API", which does not require calendar services.
Solution 2:
localtime
and gmtime
have internal storage that is static, which means they are not threadsafe (we have to return a pointer to a data structure, so it either has to be allocated dynamically, a static value or a global value - since allocating dynamically would leak memory, that is not a reasonable solution, meaning that it has to be a global or static variable [theoretically, one could allocate and store in TLS, and make it threadsafe that way]).
Most systems do have threadsafe alternatives, but they are not part of the standard library. For example, Linux/Posix has localtime_r
and gmtime_r
, which takes an extra parameter for the result. See for example
http://pubs.opengroup.org/onlinepubs/7908799/xsh/gmtime.html
Similarly, Microsoft libraries have gmtime_s
, which is also re-entrant and works in a similar way (passing in the output parameter as an input). See http://msdn.microsoft.com/en-us/library/3stkd9be.aspx
As to why the standard C++11 library doesn't use these functions? That you'd have to ask the people who wrote that specification - I expect it's portability and convenience, but I'm not entirely sure.
Solution 3:
There is no threadsafe alternative to std::localtime
and std::gmtime
because you didn't propose one and marshal it through the entire standardization process. And neither did anyone else.
chrono
s only calendar code is code that wraps existing time_t
functions. Standardizing or writing new ones was outside of the domain of the chrono
project. Doing such standardization would require more time, more effort, and add more dependencies. Simply wrapping each time_t
function was simple, had few dependencies, and quick.
They focused their effort narrowly. And they succeeded at what they focused on.
I encourage you to start working on <calendar>
or joining such an effort to create a robust calendaring API for std
. Good luck and godspeed!
Solution 4:
If you are willing to use a free, open-source 3rd party library, here is a way to print std::chrono::system_clock::time_point
in UTC:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << system_clock::now() << " UTC\n";
}
This is a thread-safe alternative to std::gmtime
using modern C++ syntax.
For a modern, thread-safe std::localtime
replacement, you need this closely related higher level timezone library and the syntax looks like this:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << make_zoned(current_zone(), system_clock::now()) << "\n";
}
Both of these will output with whatever precision your system_clock
supports, for example:
2016-07-05 10:03:01.608080 EDT
(microseconds on macOS)
These libraries go far beyond a gmtime
and localtime
replacement. For example, do you want to see the current date in the Julian calendar?
#include "julian.h"
#include <iostream>
int
main()
{
using namespace std::chrono;
std::cout << julian::year_month_day(date::floor<date::days>(system_clock::now())) << "\n";
}
2016-06-22
How about the current GPS time?
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
std::cout << std::chrono::system_clock::now() << " UTC\n";
std::cout << gps_clock::now() << " GPS\n";
}
2016-07-05 14:13:02.138091 UTC
2016-07-05 14:13:19.138524 GPS
https://github.com/HowardHinnant/date
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0355r0.html
Update
The "date.h" and "tz.h" libraries are now in the draft C++2a specification, with very minor changes, and where we hope 'a' is '0'. They will live in the header <chrono>
and under namespace std::chrono
(and there will not be a date namespace
).
Solution 5:
As others had mentioned, there is really no threadsafe convenience and portable time formatting approach in any available C++ standard, but there is some archaic preprocessor technique I found usable (thanks to Andrei Alexandrescu at CppCon 2015 slide 17 & 18):
std::mutex gmtime_call_mutex;
template< size_t For_Separating_Instantiations >
std::tm const * utc_impl( std::chrono::system_clock::time_point const & tp )
{
thread_local static std::tm tm = {};
std::time_t const time = std::chrono::system_clock::to_time_t( tp );
{
std::unique_lock< std::mutex > ul( gmtime_call_mutex );
tm = *std::gmtime( &time );
}
return &tm;
}
#ifdef __COUNTER__
#define utc( arg ) utc_impl<__COUNTER__>( (arg) )
#else
#define utc( arg ) utc_impl<__LINE__>( (arg) )
#endif
Here we declare function with size_t
template argument and returning pointer to static member std::tm
. Now each call of this function with different template argument create a new function with brand new static std::tm
variable. If __COUNTER__
macro is defined, it should be replaced by incremented integer value each time it is used, otherwise we use __LINE__
macro and in this case better to be sure that we do not call macro utc
twice in one line.
Global gmtime_call_mutex
protect non-threadsafe std::gmtime
call in each instantiation, and at least in Linux shouldn't be a performance problem as lock acquiring is firstly performed as running around spinlock, and in our case should never end up with real thread lock.
thread_local
ensures that different threads running same code with utc
calls will still working with different std::tm
variables.
Example of usage:
void work_with_range(
std::chrono::system_clock::time_point from = {}
, std::chrono::system_clock::time_point to = {}
)
{
std::cout << "Will work with range from "
<< ( from == decltype(from)()
? std::put_time( nullptr, "beginning" )
: std::put_time( utc( from ), "%Y-%m-%d %H:%M:%S" )
)
<< " to "
<< ( to == decltype(to)()
? std::put_time( nullptr, "end" )
: std::put_time( utc( to ), "%Y-%m-%d %H:%M:%S" )
)
<< "."
<< std::endl;
// ...
}