Thread-safe time formatting

This adds thread-safe (at least on platforms that provide necessary extensions) replacement functions for std::localtime and std::gmtime. Alternatively they could be placed in a new source file time.cc, but time.h seems so empty right now...
This commit is contained in:
codicodi 2016-10-09 23:57:52 +02:00 committed by GitHub
parent 4809e2956a
commit 3acdbc03d9

View File

@ -13,6 +13,12 @@
#include "fmt/format.h" #include "fmt/format.h"
#include <ctime> #include <ctime>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4702) // unreachable code
#pragma warning(disable: 4996) // "deprecated" functions
#endif
namespace fmt { namespace fmt {
template <typename ArgFormatter> template <typename ArgFormatter>
void format_arg(BasicFormatter<char, ArgFormatter> &f, void format_arg(BasicFormatter<char, ArgFormatter> &f,
@ -48,6 +54,99 @@ void format_arg(BasicFormatter<char, ArgFormatter> &f,
} }
format_str = end + 1; format_str = end + 1;
} }
namespace internal{
inline Null<> localtime_r(...){
return Null<>();
}
inline Null<> localtime_s(...){
return Null<>();
}
inline Null<> gmtime_r(...){
return Null<>();
}
inline Null<> gmtime_s(...){
return Null<>();
}
} }
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time){
struct LocalTime {
std::time_t time_;
std::tm tm_;
LocalTime(std::time_t t)
:time_(t) {}
bool run(){
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm* tm){
return tm != 0;
}
bool handle(internal::Null<>){
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res){
return res == 0;
}
bool fallback(internal::Null<>){
using namespace fmt::internal;
std::tm* tm = std::localtime(&time_);
if (tm != 0) tm_ = *tm;
return tm != 0;
}
};
LocalTime lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time)
{
struct GmTime {
std::time_t time_;
std::tm tm_;
GmTime(std::time_t t)
:time_(t) {}
bool run(){
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm* tm){
return tm != 0;
}
bool handle(internal::Null<>){
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res){
return res == 0;
}
bool fallback(internal::Null<>){
std::tm* tm = std::gmtime(&time_);
if (tm != 0) tm_ = *tm;
return tm != 0;
}
};
GmTime gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
} //namespace fmt
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // FMT_TIME_H_ #endif // FMT_TIME_H_