]>
Commit | Line | Data |
---|---|---|
eafe8130 TL |
1 | // Formatting library for C++ - time formatting tests |
2 | // | |
3 | // Copyright (c) 2012 - present, Victor Zverovich | |
4 | // All rights reserved. | |
5 | // | |
6 | // For the license information refer to format.h. | |
7 | ||
8 | #include "fmt/chrono.h" | |
eafe8130 | 9 | |
1e59de90 TL |
10 | #include <ctime> |
11 | #include <vector> | |
12 | ||
20effc67 TL |
13 | #include "gtest-extra.h" // EXPECT_THROW_MSG |
14 | #include "util.h" // get_locale | |
15 | ||
16 | using fmt::runtime; | |
eafe8130 | 17 | |
20effc67 | 18 | using testing::Contains; |
f67539c2 | 19 | |
20effc67 | 20 | auto make_tm() -> std::tm { |
eafe8130 TL |
21 | auto time = std::tm(); |
22 | time.tm_mday = 1; | |
23 | return time; | |
24 | } | |
25 | ||
20effc67 | 26 | auto make_hour(int h) -> std::tm { |
eafe8130 TL |
27 | auto time = make_tm(); |
28 | time.tm_hour = h; | |
29 | return time; | |
30 | } | |
31 | ||
20effc67 | 32 | auto make_minute(int m) -> std::tm { |
eafe8130 TL |
33 | auto time = make_tm(); |
34 | time.tm_min = m; | |
35 | return time; | |
36 | } | |
37 | ||
20effc67 | 38 | auto make_second(int s) -> std::tm { |
eafe8130 TL |
39 | auto time = make_tm(); |
40 | time.tm_sec = s; | |
41 | return time; | |
42 | } | |
43 | ||
1e59de90 TL |
44 | std::string system_strftime(const std::string& format, const std::tm* timeptr, |
45 | std::locale* locptr = nullptr) { | |
46 | auto loc = locptr ? *locptr : std::locale::classic(); | |
47 | auto& facet = std::use_facet<std::time_put<char>>(loc); | |
48 | std::ostringstream os; | |
49 | os.imbue(loc); | |
50 | facet.put(os, os, ' ', timeptr, format.c_str(), | |
51 | format.c_str() + format.size()); | |
52 | #ifdef _WIN32 | |
53 | // Workaround a bug in older versions of Universal CRT. | |
54 | auto str = os.str(); | |
55 | if (str == "-0000") str = "+0000"; | |
56 | return str; | |
57 | #else | |
58 | return os.str(); | |
59 | #endif | |
60 | } | |
61 | ||
62 | FMT_CONSTEXPR std::tm make_tm(int year, int mon, int mday, int hour, int min, | |
63 | int sec) { | |
64 | auto tm = std::tm(); | |
65 | tm.tm_sec = sec; | |
66 | tm.tm_min = min; | |
67 | tm.tm_hour = hour; | |
68 | tm.tm_mday = mday; | |
69 | tm.tm_mon = mon - 1; | |
70 | tm.tm_year = year - 1900; | |
71 | return tm; | |
72 | } | |
73 | ||
20effc67 TL |
74 | TEST(chrono_test, format_tm) { |
75 | auto tm = std::tm(); | |
9f95a23c TL |
76 | tm.tm_year = 116; |
77 | tm.tm_mon = 3; | |
78 | tm.tm_mday = 25; | |
20effc67 TL |
79 | tm.tm_hour = 11; |
80 | tm.tm_min = 22; | |
81 | tm.tm_sec = 33; | |
82 | EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), | |
83 | "The date is 2016-04-25 11:22:33."); | |
1e59de90 TL |
84 | EXPECT_EQ(fmt::format("{:%Y}", tm), "2016"); |
85 | EXPECT_EQ(fmt::format("{:%C}", tm), "20"); | |
86 | EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); | |
87 | EXPECT_EQ(fmt::format("{:%e}", tm), "25"); | |
88 | EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/16"); | |
89 | EXPECT_EQ(fmt::format("{:%F}", tm), "2016-04-25"); | |
90 | EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33"); | |
91 | ||
92 | // Short year | |
93 | tm.tm_year = 999 - 1900; | |
94 | tm.tm_mon = 0; // for %G | |
95 | tm.tm_mday = 2; // for %G | |
96 | tm.tm_wday = 3; // for %G | |
97 | tm.tm_yday = 1; // for %G | |
98 | EXPECT_EQ(fmt::format("{:%Y}", tm), "0999"); | |
99 | EXPECT_EQ(fmt::format("{:%C%y}", tm), "0999"); | |
100 | EXPECT_EQ(fmt::format("{:%G}", tm), "0999"); | |
101 | ||
102 | tm.tm_year = 27 - 1900; | |
103 | EXPECT_EQ(fmt::format("{:%Y}", tm), "0027"); | |
104 | EXPECT_EQ(fmt::format("{:%C%y}", tm), "0027"); | |
105 | ||
106 | // Overflow year | |
107 | tm.tm_year = 2147483647; | |
108 | EXPECT_EQ(fmt::format("{:%Y}", tm), "2147485547"); | |
109 | ||
110 | tm.tm_year = -2147483648; | |
111 | EXPECT_EQ(fmt::format("{:%Y}", tm), "-2147481748"); | |
112 | ||
113 | // for week on the year | |
114 | // https://www.cl.cam.ac.uk/~mgk25/iso-time.html | |
115 | std::vector<std::tm> tm_list = { | |
116 | make_tm(1975, 12, 29, 12, 14, 16), // W01 | |
117 | make_tm(1977, 1, 2, 12, 14, 16), // W53 | |
118 | make_tm(1999, 12, 27, 12, 14, 16), // W52 | |
119 | make_tm(1999, 12, 31, 12, 14, 16), // W52 | |
120 | make_tm(2000, 1, 1, 12, 14, 16), // W52 | |
121 | make_tm(2000, 1, 2, 12, 14, 16), // W52 | |
122 | make_tm(2000, 1, 3, 12, 14, 16) // W1 | |
123 | }; | |
124 | ||
125 | #if defined(__MINGW32__) && !defined(_UCRT) | |
126 | GTEST_SKIP() << "Skip the rest of this test because it relies on strftime() " | |
127 | "conforming to C99, but on this platform, MINGW + MSVCRT, " | |
128 | "the function conforms only to C89."; | |
129 | #endif | |
130 | ||
131 | const std::string iso_week_spec = "%Y-%m-%d: %G %g %V"; | |
132 | for (auto ctm : tm_list) { | |
133 | // Calculate tm_yday, tm_wday, etc. | |
134 | std::time_t t = std::mktime(&ctm); | |
135 | tm = *std::localtime(&t); | |
136 | ||
137 | auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec); | |
138 | EXPECT_EQ(system_strftime(iso_week_spec, &tm), | |
139 | fmt::format(fmt::runtime(fmt_spec), tm)); | |
140 | } | |
141 | ||
142 | // Every day from 1970-01-01 | |
143 | std::time_t time_now = std::time(nullptr); | |
144 | for (std::time_t t = 6 * 3600; t < time_now; t += 86400) { | |
145 | tm = *std::localtime(&t); | |
146 | ||
147 | auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec); | |
148 | EXPECT_EQ(system_strftime(iso_week_spec, &tm), | |
149 | fmt::format(fmt::runtime(fmt_spec), tm)); | |
150 | } | |
151 | } | |
152 | ||
153 | // MSVC: | |
154 | // minkernel\crts\ucrt\src\appcrt\time\wcsftime.cpp(971) : Assertion failed: | |
155 | // timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099 | |
156 | #ifndef _WIN32 | |
157 | TEST(chrono_test, format_tm_future) { | |
158 | auto tm = std::tm(); | |
159 | tm.tm_year = 10445; // 10000+ years | |
160 | tm.tm_mon = 3; | |
161 | tm.tm_mday = 25; | |
162 | tm.tm_hour = 11; | |
163 | tm.tm_min = 22; | |
164 | tm.tm_sec = 33; | |
165 | EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), | |
166 | "The date is 12345-04-25 11:22:33."); | |
167 | EXPECT_EQ(fmt::format("{:%Y}", tm), "12345"); | |
168 | EXPECT_EQ(fmt::format("{:%C}", tm), "123"); | |
169 | EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); | |
170 | EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/45"); | |
171 | EXPECT_EQ(fmt::format("{:%F}", tm), "12345-04-25"); | |
172 | EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33"); | |
9f95a23c TL |
173 | } |
174 | ||
1e59de90 TL |
175 | TEST(chrono_test, format_tm_past) { |
176 | auto tm = std::tm(); | |
177 | tm.tm_year = -2001; | |
178 | tm.tm_mon = 3; | |
179 | tm.tm_mday = 25; | |
180 | tm.tm_hour = 11; | |
181 | tm.tm_min = 22; | |
182 | tm.tm_sec = 33; | |
183 | EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), | |
184 | "The date is -101-04-25 11:22:33."); | |
185 | EXPECT_EQ(fmt::format("{:%Y}", tm), "-101"); | |
186 | ||
187 | // macOS %C - "-1" | |
188 | // Linux %C - "-2" | |
189 | // fmt %C - "-1" | |
190 | EXPECT_EQ(fmt::format("{:%C}", tm), "-1"); | |
191 | EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); | |
192 | ||
193 | // macOS %D - "04/25/01" (%y) | |
194 | // Linux %D - "04/25/99" (%y) | |
195 | // fmt %D - "04/25/01" (%y) | |
196 | EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/01"); | |
197 | ||
198 | EXPECT_EQ(fmt::format("{:%F}", tm), "-101-04-25"); | |
199 | EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33"); | |
200 | ||
201 | tm.tm_year = -1901; // -1 | |
202 | EXPECT_EQ(fmt::format("{:%Y}", tm), "-001"); | |
203 | EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); | |
204 | ||
205 | tm.tm_year = -1911; // -11 | |
206 | EXPECT_EQ(fmt::format("{:%Y}", tm), "-011"); | |
207 | EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm)); | |
208 | } | |
209 | #endif | |
210 | ||
20effc67 TL |
211 | TEST(chrono_test, grow_buffer) { |
212 | auto s = std::string("{:"); | |
9f95a23c TL |
213 | for (int i = 0; i < 30; ++i) s += "%c"; |
214 | s += "}\n"; | |
20effc67 | 215 | auto t = std::time(nullptr); |
1e59de90 | 216 | (void)fmt::format(fmt::runtime(s), *std::localtime(&t)); |
9f95a23c TL |
217 | } |
218 | ||
20effc67 | 219 | TEST(chrono_test, format_to_empty_container) { |
9f95a23c TL |
220 | auto time = std::tm(); |
221 | time.tm_sec = 42; | |
20effc67 | 222 | auto s = std::string(); |
9f95a23c TL |
223 | fmt::format_to(std::back_inserter(s), "{:%S}", time); |
224 | EXPECT_EQ(s, "42"); | |
225 | } | |
226 | ||
20effc67 | 227 | TEST(chrono_test, empty_result) { EXPECT_EQ(fmt::format("{}", std::tm()), ""); } |
9f95a23c | 228 | |
20effc67 | 229 | auto equal(const std::tm& lhs, const std::tm& rhs) -> bool { |
9f95a23c TL |
230 | return lhs.tm_sec == rhs.tm_sec && lhs.tm_min == rhs.tm_min && |
231 | lhs.tm_hour == rhs.tm_hour && lhs.tm_mday == rhs.tm_mday && | |
232 | lhs.tm_mon == rhs.tm_mon && lhs.tm_year == rhs.tm_year && | |
233 | lhs.tm_wday == rhs.tm_wday && lhs.tm_yday == rhs.tm_yday && | |
234 | lhs.tm_isdst == rhs.tm_isdst; | |
235 | } | |
236 | ||
20effc67 TL |
237 | TEST(chrono_test, localtime) { |
238 | auto t = std::time(nullptr); | |
239 | auto tm = *std::localtime(&t); | |
240 | EXPECT_TRUE(equal(tm, fmt::localtime(t))); | |
9f95a23c TL |
241 | } |
242 | ||
20effc67 TL |
243 | TEST(chrono_test, gmtime) { |
244 | auto t = std::time(nullptr); | |
245 | auto tm = *std::gmtime(&t); | |
246 | EXPECT_TRUE(equal(tm, fmt::gmtime(t))); | |
9f95a23c TL |
247 | } |
248 | ||
1e59de90 | 249 | template <typename TimePoint> auto strftime_full(TimePoint tp) -> std::string { |
20effc67 TL |
250 | auto t = std::chrono::system_clock::to_time_t(tp); |
251 | auto tm = *std::localtime(&t); | |
1e59de90 | 252 | return system_strftime("%Y-%m-%d %H:%M:%S", &tm); |
20effc67 TL |
253 | } |
254 | ||
255 | TEST(chrono_test, time_point) { | |
256 | auto t1 = std::chrono::system_clock::now(); | |
1e59de90 TL |
257 | EXPECT_EQ(strftime_full(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1)); |
258 | EXPECT_EQ(strftime_full(t1), fmt::format("{}", t1)); | |
20effc67 TL |
259 | using time_point = |
260 | std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>; | |
261 | auto t2 = time_point(std::chrono::seconds(42)); | |
1e59de90 TL |
262 | EXPECT_EQ(strftime_full(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2)); |
263 | ||
264 | std::vector<std::string> spec_list = { | |
265 | "%%", "%n", "%t", "%Y", "%EY", "%y", "%Oy", "%Ey", "%C", | |
266 | "%EC", "%G", "%g", "%b", "%h", "%B", "%m", "%Om", "%U", | |
267 | "%OU", "%W", "%OW", "%V", "%OV", "%j", "%d", "%Od", "%e", | |
268 | "%Oe", "%a", "%A", "%w", "%Ow", "%u", "%Ou", "%H", "%OH", | |
269 | "%I", "%OI", "%M", "%OM", "%S", "%OS", "%x", "%Ex", "%X", | |
270 | "%EX", "%D", "%F", "%R", "%T", "%p", "%z", "%Z"}; | |
271 | #ifndef _WIN32 | |
272 | // Disabled on Windows because these formats are not consistent among | |
273 | // platforms. | |
274 | spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"}); | |
275 | #elif defined(__MINGW32__) && !defined(_UCRT) | |
276 | // Only C89 conversion specifiers when using MSVCRT instead of UCRT | |
277 | spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d", "%a", | |
278 | "%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p", "%Z"}; | |
279 | #endif | |
280 | spec_list.push_back("%Y-%m-%d %H:%M:%S"); | |
281 | ||
282 | for (const auto& spec : spec_list) { | |
283 | auto t = std::chrono::system_clock::to_time_t(t1); | |
284 | auto tm = *std::localtime(&t); | |
285 | ||
286 | auto sys_output = system_strftime(spec, &tm); | |
287 | ||
288 | auto fmt_spec = fmt::format("{{:{}}}", spec); | |
289 | EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1)); | |
290 | EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); | |
291 | } | |
20effc67 | 292 | } |
eafe8130 | 293 | |
9f95a23c TL |
294 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR |
295 | ||
20effc67 | 296 | TEST(chrono_test, format_default) { |
eafe8130 TL |
297 | EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42))); |
298 | EXPECT_EQ("42as", | |
299 | fmt::format("{}", std::chrono::duration<int, std::atto>(42))); | |
300 | EXPECT_EQ("42fs", | |
301 | fmt::format("{}", std::chrono::duration<int, std::femto>(42))); | |
302 | EXPECT_EQ("42ps", | |
303 | fmt::format("{}", std::chrono::duration<int, std::pico>(42))); | |
304 | EXPECT_EQ("42ns", fmt::format("{}", std::chrono::nanoseconds(42))); | |
305 | EXPECT_EQ("42µs", fmt::format("{}", std::chrono::microseconds(42))); | |
306 | EXPECT_EQ("42ms", fmt::format("{}", std::chrono::milliseconds(42))); | |
307 | EXPECT_EQ("42cs", | |
308 | fmt::format("{}", std::chrono::duration<int, std::centi>(42))); | |
309 | EXPECT_EQ("42ds", | |
310 | fmt::format("{}", std::chrono::duration<int, std::deci>(42))); | |
311 | EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42))); | |
312 | EXPECT_EQ("42das", | |
313 | fmt::format("{}", std::chrono::duration<int, std::deca>(42))); | |
314 | EXPECT_EQ("42hs", | |
315 | fmt::format("{}", std::chrono::duration<int, std::hecto>(42))); | |
316 | EXPECT_EQ("42ks", | |
317 | fmt::format("{}", std::chrono::duration<int, std::kilo>(42))); | |
318 | EXPECT_EQ("42Ms", | |
319 | fmt::format("{}", std::chrono::duration<int, std::mega>(42))); | |
320 | EXPECT_EQ("42Gs", | |
321 | fmt::format("{}", std::chrono::duration<int, std::giga>(42))); | |
322 | EXPECT_EQ("42Ts", | |
323 | fmt::format("{}", std::chrono::duration<int, std::tera>(42))); | |
324 | EXPECT_EQ("42Ps", | |
325 | fmt::format("{}", std::chrono::duration<int, std::peta>(42))); | |
326 | EXPECT_EQ("42Es", | |
327 | fmt::format("{}", std::chrono::duration<int, std::exa>(42))); | |
328 | EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42))); | |
329 | EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42))); | |
9f95a23c TL |
330 | EXPECT_EQ( |
331 | "42[15]s", | |
332 | fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42))); | |
333 | EXPECT_EQ( | |
334 | "42[15/4]s", | |
335 | fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42))); | |
336 | } | |
337 | ||
20effc67 | 338 | TEST(chrono_test, align) { |
9f95a23c TL |
339 | auto s = std::chrono::seconds(42); |
340 | EXPECT_EQ("42s ", fmt::format("{:5}", s)); | |
341 | EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5)); | |
342 | EXPECT_EQ(" 42s", fmt::format("{:>5}", s)); | |
343 | EXPECT_EQ("**42s**", fmt::format("{:*^7}", s)); | |
344 | EXPECT_EQ("03:25:45 ", | |
345 | fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345))); | |
346 | EXPECT_EQ(" 03:25:45", | |
347 | fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345))); | |
348 | EXPECT_EQ("~~03:25:45~~", | |
349 | fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345))); | |
350 | EXPECT_EQ("03:25:45 ", | |
351 | fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12)); | |
eafe8130 TL |
352 | } |
353 | ||
20effc67 | 354 | TEST(chrono_test, format_specs) { |
eafe8130 TL |
355 | EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0))); |
356 | EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0))); | |
357 | EXPECT_EQ("\t", fmt::format("{:%t}", std::chrono::seconds(0))); | |
358 | EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(0))); | |
359 | EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(60))); | |
360 | EXPECT_EQ("42", fmt::format("{:%S}", std::chrono::seconds(42))); | |
361 | EXPECT_EQ("01.234", fmt::format("{:%S}", std::chrono::milliseconds(1234))); | |
362 | EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(0))); | |
363 | EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(60))); | |
364 | EXPECT_EQ("42", fmt::format("{:%M}", std::chrono::minutes(42))); | |
365 | EXPECT_EQ("01", fmt::format("{:%M}", std::chrono::seconds(61))); | |
366 | EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(0))); | |
367 | EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(24))); | |
368 | EXPECT_EQ("14", fmt::format("{:%H}", std::chrono::hours(14))); | |
369 | EXPECT_EQ("01", fmt::format("{:%H}", std::chrono::minutes(61))); | |
370 | EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(0))); | |
371 | EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(12))); | |
372 | EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24))); | |
373 | EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4))); | |
374 | EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14))); | |
375 | EXPECT_EQ("03:25:45", | |
376 | fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345))); | |
377 | EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345))); | |
378 | EXPECT_EQ("03:25:45", fmt::format("{:%T}", std::chrono::seconds(12345))); | |
9f95a23c TL |
379 | EXPECT_EQ("12345", fmt::format("{:%Q}", std::chrono::seconds(12345))); |
380 | EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345))); | |
eafe8130 TL |
381 | } |
382 | ||
20effc67 | 383 | TEST(chrono_test, invalid_specs) { |
eafe8130 | 384 | auto sec = std::chrono::seconds(0); |
1e59de90 | 385 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%a}"), sec), fmt::format_error, |
20effc67 | 386 | "no date"); |
1e59de90 | 387 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%A}"), sec), fmt::format_error, |
20effc67 | 388 | "no date"); |
1e59de90 | 389 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%c}"), sec), fmt::format_error, |
20effc67 | 390 | "no date"); |
1e59de90 | 391 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%x}"), sec), fmt::format_error, |
20effc67 | 392 | "no date"); |
1e59de90 | 393 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ex}"), sec), fmt::format_error, |
20effc67 | 394 | "no date"); |
1e59de90 | 395 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%X}"), sec), fmt::format_error, |
20effc67 | 396 | "no date"); |
1e59de90 | 397 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%EX}"), sec), fmt::format_error, |
20effc67 | 398 | "no date"); |
1e59de90 | 399 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%D}"), sec), fmt::format_error, |
20effc67 | 400 | "no date"); |
1e59de90 | 401 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%F}"), sec), fmt::format_error, |
20effc67 | 402 | "no date"); |
1e59de90 | 403 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ec}"), sec), fmt::format_error, |
20effc67 | 404 | "no date"); |
1e59de90 | 405 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%w}"), sec), fmt::format_error, |
20effc67 | 406 | "no date"); |
1e59de90 | 407 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%u}"), sec), fmt::format_error, |
20effc67 | 408 | "no date"); |
1e59de90 | 409 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%b}"), sec), fmt::format_error, |
20effc67 | 410 | "no date"); |
1e59de90 | 411 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%B}"), sec), fmt::format_error, |
20effc67 | 412 | "no date"); |
1e59de90 | 413 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%z}"), sec), fmt::format_error, |
20effc67 | 414 | "no date"); |
1e59de90 | 415 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Z}"), sec), fmt::format_error, |
20effc67 | 416 | "no date"); |
1e59de90 | 417 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Eq}"), sec), fmt::format_error, |
eafe8130 | 418 | "invalid format"); |
1e59de90 | 419 | EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Oq}"), sec), fmt::format_error, |
eafe8130 TL |
420 | "invalid format"); |
421 | } | |
422 | ||
20effc67 TL |
423 | auto format_tm(const std::tm& time, fmt::string_view spec, |
424 | const std::locale& loc) -> std::string { | |
425 | auto& facet = std::use_facet<std::time_put<char>>(loc); | |
426 | std::ostringstream os; | |
427 | os.imbue(loc); | |
428 | facet.put(os, os, ' ', &time, spec.begin(), spec.end()); | |
429 | return os.str(); | |
430 | } | |
431 | ||
432 | TEST(chrono_test, locale) { | |
433 | auto loc = get_locale("ja_JP.utf8"); | |
434 | if (loc == std::locale::classic()) return; | |
435 | # define EXPECT_TIME(spec, time, duration) \ | |
436 | { \ | |
437 | auto jp_loc = std::locale("ja_JP.utf8"); \ | |
438 | EXPECT_EQ(format_tm(time, spec, jp_loc), \ | |
439 | fmt::format(jp_loc, "{:L" spec "}", duration)); \ | |
440 | } | |
eafe8130 TL |
441 | EXPECT_TIME("%OH", make_hour(14), std::chrono::hours(14)); |
442 | EXPECT_TIME("%OI", make_hour(14), std::chrono::hours(14)); | |
443 | EXPECT_TIME("%OM", make_minute(42), std::chrono::minutes(42)); | |
444 | EXPECT_TIME("%OS", make_second(42), std::chrono::seconds(42)); | |
445 | auto time = make_tm(); | |
446 | time.tm_hour = 3; | |
447 | time.tm_min = 25; | |
448 | time.tm_sec = 45; | |
449 | auto sec = std::chrono::seconds(12345); | |
450 | EXPECT_TIME("%r", time, sec); | |
451 | EXPECT_TIME("%p", time, sec); | |
452 | } | |
9f95a23c | 453 | |
20effc67 | 454 | using dms = std::chrono::duration<double, std::milli>; |
9f95a23c | 455 | |
20effc67 | 456 | TEST(chrono_test, format_default_fp) { |
9f95a23c TL |
457 | typedef std::chrono::duration<float> fs; |
458 | EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234))); | |
459 | typedef std::chrono::duration<float, std::milli> fms; | |
460 | EXPECT_EQ("1.234ms", fmt::format("{}", fms(1.234))); | |
461 | typedef std::chrono::duration<double> ds; | |
462 | EXPECT_EQ("1.234s", fmt::format("{}", ds(1.234))); | |
463 | EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234))); | |
464 | } | |
465 | ||
20effc67 | 466 | TEST(chrono_test, format_precision) { |
1e59de90 TL |
467 | EXPECT_THROW_MSG( |
468 | (void)fmt::format(runtime("{:.2}"), std::chrono::seconds(42)), | |
469 | fmt::format_error, "precision not allowed for this argument type"); | |
470 | EXPECT_EQ("1ms", fmt::format("{:.0}", dms(1.234))); | |
9f95a23c TL |
471 | EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234))); |
472 | EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2)); | |
1e59de90 TL |
473 | |
474 | EXPECT_EQ("13ms", fmt::format("{:.0}", dms(12.56))); | |
475 | EXPECT_EQ("12.6ms", fmt::format("{:.1}", dms(12.56))); | |
476 | EXPECT_EQ("12.56ms", fmt::format("{:.2}", dms(12.56))); | |
9f95a23c TL |
477 | } |
478 | ||
20effc67 | 479 | TEST(chrono_test, format_full_specs) { |
1e59de90 | 480 | EXPECT_EQ("1ms ", fmt::format("{:6.0}", dms(1.234))); |
9f95a23c TL |
481 | EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234))); |
482 | EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2)); | |
483 | EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1)); | |
484 | EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8)); | |
485 | EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3)); | |
486 | EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234))); | |
1e59de90 TL |
487 | |
488 | EXPECT_EQ("13ms ", fmt::format("{:6.0}", dms(12.56))); | |
489 | EXPECT_EQ(" 13ms", fmt::format("{:>8.{}}", dms(12.56), 0)); | |
490 | EXPECT_EQ(" 13ms ", fmt::format("{:^{}.{}}", dms(12.56), 6, 0)); | |
491 | EXPECT_EQ(" 13ms ", fmt::format("{0:^{2}.{1}}", dms(12.56), 0, 8)); | |
492 | EXPECT_EQ("==13ms===", fmt::format("{:=^{}.{}}", dms(12.56), 9, 0)); | |
493 | EXPECT_EQ("***13ms***", fmt::format("{:*^10.0}", dms(12.56))); | |
9f95a23c TL |
494 | } |
495 | ||
20effc67 | 496 | TEST(chrono_test, format_simple_q) { |
9f95a23c TL |
497 | typedef std::chrono::duration<float> fs; |
498 | EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234))); | |
499 | typedef std::chrono::duration<float, std::milli> fms; | |
500 | EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", fms(1.234))); | |
501 | typedef std::chrono::duration<double> ds; | |
502 | EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", ds(1.234))); | |
503 | EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234))); | |
504 | } | |
505 | ||
20effc67 | 506 | TEST(chrono_test, format_precision_q) { |
1e59de90 TL |
507 | EXPECT_THROW_MSG( |
508 | (void)fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)), | |
509 | fmt::format_error, "precision not allowed for this argument type"); | |
9f95a23c TL |
510 | EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234))); |
511 | EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2)); | |
512 | } | |
513 | ||
20effc67 | 514 | TEST(chrono_test, format_full_specs_q) { |
1e59de90 | 515 | EXPECT_EQ("1 ms ", fmt::format("{:7.0%Q %q}", dms(1.234))); |
9f95a23c TL |
516 | EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234))); |
517 | EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2)); | |
518 | EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1)); | |
519 | EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9)); | |
520 | EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3)); | |
521 | EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234))); | |
1e59de90 TL |
522 | |
523 | EXPECT_EQ("13 ms ", fmt::format("{:7.0%Q %q}", dms(12.56))); | |
524 | EXPECT_EQ(" 13 ms", fmt::format("{:>8.{}%Q %q}", dms(12.56), 0)); | |
525 | EXPECT_EQ(" 13 ms ", fmt::format("{:^{}.{}%Q %q}", dms(12.56), 8, 0)); | |
526 | EXPECT_EQ(" 13 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(12.56), 0, 9)); | |
527 | EXPECT_EQ("==13 ms==", fmt::format("{:=^{}.{}%Q %q}", dms(12.56), 9, 0)); | |
528 | EXPECT_EQ("***13 ms***", fmt::format("{:*^11.0%Q %q}", dms(12.56))); | |
9f95a23c TL |
529 | } |
530 | ||
20effc67 | 531 | TEST(chrono_test, invalid_width_id) { |
1e59de90 | 532 | EXPECT_THROW((void)fmt::format(runtime("{:{o}"), std::chrono::seconds(0)), |
f67539c2 TL |
533 | fmt::format_error); |
534 | } | |
535 | ||
20effc67 | 536 | TEST(chrono_test, invalid_colons) { |
1e59de90 | 537 | EXPECT_THROW((void)fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)), |
f67539c2 TL |
538 | fmt::format_error); |
539 | } | |
540 | ||
20effc67 | 541 | TEST(chrono_test, negative_durations) { |
f67539c2 TL |
542 | EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345))); |
543 | EXPECT_EQ("-03:25:45", | |
544 | fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345))); | |
545 | EXPECT_EQ("-00:01", | |
546 | fmt::format("{:%M:%S}", std::chrono::duration<double>(-1))); | |
547 | EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345))); | |
548 | EXPECT_EQ("-00.127", | |
549 | fmt::format("{:%S}", | |
550 | std::chrono::duration<signed char, std::milli>{-127})); | |
551 | auto min = std::numeric_limits<int>::min(); | |
552 | EXPECT_EQ(fmt::format("{}", min), | |
553 | fmt::format("{:%Q}", std::chrono::duration<int>(min))); | |
554 | } | |
555 | ||
20effc67 | 556 | TEST(chrono_test, special_durations) { |
1e59de90 TL |
557 | auto value = fmt::format("{:%S}", std::chrono::duration<double>(1e20)); |
558 | EXPECT_EQ(value, "40"); | |
f67539c2 TL |
559 | auto nan = std::numeric_limits<double>::quiet_NaN(); |
560 | EXPECT_EQ( | |
561 | "nan nan nan nan nan:nan nan", | |
562 | fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan))); | |
f67539c2 TL |
563 | EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::exa>(1)), |
564 | "1Es"); | |
565 | EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)), | |
566 | "1as"); | |
567 | EXPECT_EQ(fmt::format("{:%R}", std::chrono::duration<char, std::mega>{2}), | |
568 | "03:33"); | |
569 | EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}), | |
570 | "03:33:20"); | |
1e59de90 TL |
571 | EXPECT_EQ("44.000000000000", |
572 | fmt::format("{:%S}", std::chrono::duration<float, std::pico>( | |
573 | 1.54213895E+26))); | |
f67539c2 TL |
574 | } |
575 | ||
20effc67 TL |
576 | TEST(chrono_test, unsigned_duration) { |
577 | EXPECT_EQ("42s", fmt::format("{}", std::chrono::duration<unsigned>(42))); | |
578 | } | |
579 | ||
580 | TEST(chrono_test, weekday) { | |
581 | auto loc = get_locale("ru_RU.UTF-8"); | |
582 | std::locale::global(loc); | |
583 | auto mon = fmt::weekday(1); | |
1e59de90 TL |
584 | |
585 | auto tm = std::tm(); | |
586 | tm.tm_wday = static_cast<int>(mon.c_encoding()); | |
587 | ||
20effc67 | 588 | EXPECT_EQ(fmt::format("{}", mon), "Mon"); |
1e59de90 TL |
589 | EXPECT_EQ(fmt::format("{:%a}", tm), "Mon"); |
590 | ||
20effc67 TL |
591 | if (loc != std::locale::classic()) { |
592 | EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}), | |
593 | Contains(fmt::format(loc, "{:L}", mon))); | |
1e59de90 TL |
594 | EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}), |
595 | Contains(fmt::format(loc, "{:%a}", tm))); | |
596 | } | |
597 | } | |
598 | ||
599 | TEST(chrono_test, cpp20_duration_subsecond_support) { | |
600 | using attoseconds = std::chrono::duration<long long, std::atto>; | |
601 | // Check that 18 digits of subsecond precision are supported. | |
602 | EXPECT_EQ(fmt::format("{:%S}", attoseconds{999999999999999999}), | |
603 | "00.999999999999999999"); | |
604 | EXPECT_EQ(fmt::format("{:%S}", attoseconds{673231113420148734}), | |
605 | "00.673231113420148734"); | |
606 | EXPECT_EQ(fmt::format("{:%S}", attoseconds{-673231113420148734}), | |
607 | "-00.673231113420148734"); | |
608 | EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{13420148734}), | |
609 | "13.420148734"); | |
610 | EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{-13420148734}), | |
611 | "-13.420148734"); | |
612 | EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234"); | |
613 | { | |
614 | // Check that {:%H:%M:%S} is equivalent to {:%T}. | |
615 | auto dur = std::chrono::milliseconds{3601234}; | |
616 | auto formatted_dur = fmt::format("{:%T}", dur); | |
617 | EXPECT_EQ(formatted_dur, "01:00:01.234"); | |
618 | EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur); | |
619 | } | |
620 | using nanoseconds_dbl = std::chrono::duration<double, std::nano>; | |
621 | EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{-123456789}), "-00.123456789"); | |
622 | EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{9123456789}), "09.123456789"); | |
623 | // Verify that only the seconds part is extracted and printed. | |
624 | EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123456789}), "39.123456789"); | |
625 | EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123000000}), "39.123000000"); | |
626 | { | |
627 | // Now the hour is printed, and we also test if negative doubles work. | |
628 | auto dur = nanoseconds_dbl{-99123456789}; | |
629 | auto formatted_dur = fmt::format("{:%T}", dur); | |
630 | EXPECT_EQ(formatted_dur, "-00:01:39.123456789"); | |
631 | EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur); | |
20effc67 | 632 | } |
1e59de90 TL |
633 | // Check that durations with precision greater than std::chrono::seconds have |
634 | // fixed precision, and print zeros even if there is no fractional part. | |
635 | EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}), | |
636 | "07.000000"); | |
637 | EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 3>>(1)), | |
638 | "00.333333"); | |
639 | EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 7>>(1)), | |
640 | "00.142857"); | |
20effc67 TL |
641 | } |
642 | ||
9f95a23c | 643 | #endif // FMT_STATIC_THOUSANDS_SEPARATOR |