Skip to content

Commit

Permalink
Thread-safe time formatting (#396)
Browse files Browse the repository at this point in the history
  • Loading branch information
codicodi authored and vitaut committed Jan 20, 2018
1 parent f853d94 commit 3f24a38
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
80 changes: 80 additions & 0 deletions include/fmt/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,86 @@

namespace fmt {

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(format_error("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(format_error("time_t value out of range"));
return std::tm();
}

template <>
struct formatter<std::tm> {
template <typename ParseContext>
Expand Down
24 changes: 24 additions & 0 deletions test/time-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,27 @@ TEST(TimeTest, GrowBuffer) {
TEST(TimeTest, EmptyResult) {
EXPECT_EQ("", fmt::format("{}", std::tm()));
}

bool EqualTime(const std::tm &lhs, const std::tm &rhs) {
return lhs.tm_sec == rhs.tm_sec &&
lhs.tm_min == rhs.tm_min &&
lhs.tm_hour == rhs.tm_hour &&
lhs.tm_mday == rhs.tm_mday &&
lhs.tm_mon == rhs.tm_mon &&
lhs.tm_year == rhs.tm_year &&
lhs.tm_wday == rhs.tm_wday &&
lhs.tm_yday == rhs.tm_yday &&
lhs.tm_isdst == rhs.tm_isdst;
}

TEST(TimeTest, LocalTime) {
std::time_t t = std::time(0);
std::tm tm = *std::localtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
}

TEST(TimeTest, GMTime) {
std::time_t t = std::time(0);
std::tm tm = *std::gmtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
}

0 comments on commit 3f24a38

Please sign in to comment.