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_ |
---|