]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // Formatting library for C++ - time formatting |
2 | // | |
eafe8130 | 3 | // Copyright (c) 2012 - present, Victor Zverovich |
11fdf7f2 TL |
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> | |
eafe8130 | 13 | #include <locale> |
11fdf7f2 TL |
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<>(); } | |
eafe8130 | 26 | } // namespace internal |
11fdf7f2 TL |
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 | ||
eafe8130 | 50 | #if !FMT_MSC_VER |
11fdf7f2 TL |
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 | } | |
eafe8130 | 57 | #endif |
11fdf7f2 TL |
58 | }; |
59 | dispatcher lt(time); | |
11fdf7f2 | 60 | // Too big time values may be unsupported. |
eafe8130 TL |
61 | if (!lt.run()) |
62 | FMT_THROW(format_error("time_t value out of range")); | |
63 | return lt.tm_; | |
11fdf7f2 TL |
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 | ||
eafe8130 | 88 | #if !FMT_MSC_VER |
11fdf7f2 TL |
89 | bool fallback(internal::null<>) { |
90 | std::tm *tm = std::gmtime(&time_); | |
91 | if (tm) tm_ = *tm; | |
92 | return tm != FMT_NULL; | |
93 | } | |
eafe8130 | 94 | #endif |
11fdf7f2 TL |
95 | }; |
96 | dispatcher gt(time); | |
11fdf7f2 | 97 | // Too big time values may be unsupported. |
eafe8130 TL |
98 | if (!gt.run()) |
99 | FMT_THROW(format_error("time_t value out of range")); | |
100 | return gt.tm_; | |
11fdf7f2 TL |
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 = internal::null_terminating_iterator<Char>(ctx); | |
120 | if (*it == ':') | |
121 | ++it; | |
122 | auto end = it; | |
123 | while (*end && *end != '}') | |
124 | ++end; | |
125 | tm_format.reserve(end - it + 1); | |
126 | using internal::pointer_from; | |
127 | tm_format.append(pointer_from(it), pointer_from(end)); | |
128 | tm_format.push_back('\0'); | |
129 | return pointer_from(end); | |
130 | } | |
131 | ||
132 | template <typename FormatContext> | |
133 | auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) { | |
eafe8130 | 134 | basic_memory_buffer<Char> buf; |
11fdf7f2 TL |
135 | std::size_t start = buf.size(); |
136 | for (;;) { | |
137 | std::size_t size = buf.capacity() - start; | |
138 | std::size_t count = | |
139 | internal::strftime(&buf[start], size, &tm_format[0], &tm); | |
140 | if (count != 0) { | |
141 | buf.resize(start + count); | |
142 | break; | |
143 | } | |
144 | if (size >= tm_format.size() * 256) { | |
145 | // If the buffer is 256 times larger than the format string, assume | |
146 | // that `strftime` gives an empty result. There doesn't seem to be a | |
147 | // better way to distinguish the two cases: | |
148 | // https://github.com/fmtlib/fmt/issues/367 | |
149 | break; | |
150 | } | |
151 | const std::size_t MIN_GROWTH = 10; | |
152 | buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); | |
153 | } | |
eafe8130 | 154 | return std::copy(buf.begin(), buf.end(), ctx.out()); |
11fdf7f2 TL |
155 | } |
156 | ||
157 | basic_memory_buffer<Char> tm_format; | |
158 | }; | |
159 | FMT_END_NAMESPACE | |
160 | ||
161 | #endif // FMT_TIME_H_ |