| 1 | // Formatting library for C++ - time formatting |
|---|
| 2 | // |
|---|
| 3 | // Copyright (c) 2012 - present, Victor Zverovich |
|---|
| 4 | // All rights reserved. |
|---|
| 5 | // |
|---|
| 6 | // For the license information refer to format.h. |
|---|
| 7 | |
|---|
| 8 | #ifndef FMT_TIME_H_ |
|---|
| 9 | #define FMT_TIME_H_ |
|---|
| 10 | |
|---|
| 11 | #include "format.h" |
|---|
| 12 | #include <ctime> |
|---|
| 13 | #include <locale> |
|---|
| 14 | |
|---|
| 15 | FMT_BEGIN_NAMESPACE |
|---|
| 16 | |
|---|
| 17 | // Prevents expansion of a preceding token as a function-style macro. |
|---|
| 18 | // Usage: f FMT_NOMACRO() |
|---|
| 19 | #define FMT_NOMACRO |
|---|
| 20 | |
|---|
| 21 | namespace internal{ |
|---|
| 22 | inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } |
|---|
| 23 | inline null<> localtime_s(...) { return null<>(); } |
|---|
| 24 | inline null<> gmtime_r(...) { return null<>(); } |
|---|
| 25 | inline null<> gmtime_s(...) { return null<>(); } |
|---|
| 26 | } // namespace internal |
|---|
| 27 | |
|---|
| 28 | // Thread-safe replacement for std::localtime |
|---|
| 29 | inline std::tm localtime(std::time_t time) { |
|---|
| 30 | struct dispatcher { |
|---|
| 31 | std::time_t time_; |
|---|
| 32 | std::tm tm_; |
|---|
| 33 | |
|---|
| 34 | dispatcher(std::time_t t): time_(t) {} |
|---|
| 35 | |
|---|
| 36 | bool run() { |
|---|
| 37 | using namespace fmt::internal; |
|---|
| 38 | return handle(localtime_r(&time_, &tm_)); |
|---|
| 39 | } |
|---|
| 40 | |
|---|
| 41 | bool handle(std::tm *tm) { return tm != FMT_NULL; } |
|---|
| 42 | |
|---|
| 43 | bool handle(internal::null<>) { |
|---|
| 44 | using namespace fmt::internal; |
|---|
| 45 | return fallback(localtime_s(&tm_, &time_)); |
|---|
| 46 | } |
|---|
| 47 | |
|---|
| 48 | bool fallback(int res) { return res == 0; } |
|---|
| 49 | |
|---|
| 50 | #if !FMT_MSC_VER |
|---|
| 51 | bool fallback(internal::null<>) { |
|---|
| 52 | using namespace fmt::internal; |
|---|
| 53 | std::tm *tm = std::localtime(&time_); |
|---|
| 54 | if (tm) tm_ = *tm; |
|---|
| 55 | return tm != FMT_NULL; |
|---|
| 56 | } |
|---|
| 57 | #endif |
|---|
| 58 | }; |
|---|
| 59 | dispatcher lt(time); |
|---|
| 60 | // Too big time values may be unsupported. |
|---|
| 61 | if (!lt.run()) |
|---|
| 62 | FMT_THROW(format_error("time_t value out of range")); |
|---|
| 63 | return lt.tm_; |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | // Thread-safe replacement for std::gmtime |
|---|
| 67 | inline std::tm gmtime(std::time_t time) { |
|---|
| 68 | struct dispatcher { |
|---|
| 69 | std::time_t time_; |
|---|
| 70 | std::tm tm_; |
|---|
| 71 | |
|---|
| 72 | dispatcher(std::time_t t): time_(t) {} |
|---|
| 73 | |
|---|
| 74 | bool run() { |
|---|
| 75 | using namespace fmt::internal; |
|---|
| 76 | return handle(gmtime_r(&time_, &tm_)); |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | bool handle(std::tm *tm) { return tm != FMT_NULL; } |
|---|
| 80 | |
|---|
| 81 | bool handle(internal::null<>) { |
|---|
| 82 | using namespace fmt::internal; |
|---|
| 83 | return fallback(gmtime_s(&tm_, &time_)); |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | bool fallback(int res) { return res == 0; } |
|---|
| 87 | |
|---|
| 88 | #if !FMT_MSC_VER |
|---|
| 89 | bool fallback(internal::null<>) { |
|---|
| 90 | std::tm *tm = std::gmtime(&time_); |
|---|
| 91 | if (tm) tm_ = *tm; |
|---|
| 92 | return tm != FMT_NULL; |
|---|
| 93 | } |
|---|
| 94 | #endif |
|---|
| 95 | }; |
|---|
| 96 | dispatcher gt(time); |
|---|
| 97 | // Too big time values may be unsupported. |
|---|
| 98 | if (!gt.run()) |
|---|
| 99 | FMT_THROW(format_error("time_t value out of range")); |
|---|
| 100 | return gt.tm_; |
|---|
| 101 | } |
|---|
| 102 | |
|---|
| 103 | namespace internal { |
|---|
| 104 | inline std::size_t strftime(char *str, std::size_t count, const char *format, |
|---|
| 105 | const std::tm *time) { |
|---|
| 106 | return std::strftime(str, count, format, time); |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | inline std::size_t strftime(wchar_t *str, std::size_t count, |
|---|
| 110 | const wchar_t *format, const std::tm *time) { |
|---|
| 111 | return std::wcsftime(str, count, format, time); |
|---|
| 112 | } |
|---|
| 113 | } |
|---|
| 114 | |
|---|
| 115 | template <typename Char> |
|---|
| 116 | struct formatter<std::tm, Char> { |
|---|
| 117 | template <typename ParseContext> |
|---|
| 118 | auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { |
|---|
| 119 | auto it = ctx.begin(); |
|---|
| 120 | if (it != ctx.end() && *it == ':') |
|---|
| 121 | ++it; |
|---|
| 122 | auto end = it; |
|---|
| 123 | while (end != ctx.end() && *end != '}') |
|---|
| 124 | ++end; |
|---|
| 125 | tm_format.reserve(internal::to_unsigned(end - it + 1)); |
|---|
| 126 | tm_format.append(it, end); |
|---|
| 127 | tm_format.push_back('\0'); |
|---|
| 128 | return end; |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | template <typename FormatContext> |
|---|
| 132 | auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) { |
|---|
| 133 | basic_memory_buffer<Char> buf; |
|---|
| 134 | std::size_t start = buf.size(); |
|---|
| 135 | for (;;) { |
|---|
| 136 | std::size_t size = buf.capacity() - start; |
|---|
| 137 | std::size_t count = |
|---|
| 138 | internal::strftime(&buf[start], size, &tm_format[0], &tm); |
|---|
| 139 | if (count != 0) { |
|---|
| 140 | buf.resize(start + count); |
|---|
| 141 | break; |
|---|
| 142 | } |
|---|
| 143 | if (size >= tm_format.size() * 256) { |
|---|
| 144 | // If the buffer is 256 times larger than the format string, assume |
|---|
| 145 | // that `strftime` gives an empty result. There doesn't seem to be a |
|---|
| 146 | // better way to distinguish the two cases: |
|---|
| 147 | // https://github.com/fmtlib/fmt/issues/367 |
|---|
| 148 | break; |
|---|
| 149 | } |
|---|
| 150 | const std::size_t MIN_GROWTH = 10; |
|---|
| 151 | buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); |
|---|
| 152 | } |
|---|
| 153 | return std::copy(buf.begin(), buf.end(), ctx.out()); |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | basic_memory_buffer<Char> tm_format; |
|---|
| 157 | }; |
|---|
| 158 | FMT_END_NAMESPACE |
|---|
| 159 | |
|---|
| 160 | #endif // FMT_TIME_H_ |
|---|