xref: /llvm-project/libcxx/test/std/time/time.syn/formatter.year.pass.cpp (revision 6a54dfbfe534276d644d7f9c027f0deeb748dd53)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 // UNSUPPORTED: no-localization
11 // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
12 
13 // TODO FMT This test should not require std::to_chars(floating-point)
14 // XFAIL: availability-fp_to_chars-missing
15 
16 // REQUIRES: locale.fr_FR.UTF-8
17 // REQUIRES: locale.ja_JP.UTF-8
18 
19 // <chrono>
20 
21 // template<class charT> struct formatter<chrono::year, charT>;
22 
23 #include <chrono>
24 #include <format>
25 
26 #include <cassert>
27 #include <concepts>
28 #include <locale>
29 #include <iostream>
30 #include <type_traits>
31 
32 #include "formatter_tests.h"
33 #include "make_string.h"
34 #include "platform_support.h" // locale name macros
35 #include "string_literal.h"
36 #include "test_macros.h"
37 
38 template <class CharT>
39 static void test_no_chrono_specs() {
40   check(SV("-32767"), SV("{}"), std::chrono::year{-32'767});
41   check(SV("-1000"), SV("{}"), std::chrono::year{-1000});
42   check(SV("-0100"), SV("{}"), std::chrono::year{-100});
43   check(SV("-0010"), SV("{}"), std::chrono::year{-10});
44   check(SV("-0001"), SV("{}"), std::chrono::year{-1});
45   check(SV("0000"), SV("{}"), std::chrono::year{0});
46   check(SV("0001"), SV("{}"), std::chrono::year{1});
47   check(SV("0010"), SV("{}"), std::chrono::year{10});
48   check(SV("0100"), SV("{}"), std::chrono::year{100});
49   check(SV("1000"), SV("{}"), std::chrono::year{1000});
50   check(SV("32727"), SV("{}"), std::chrono::year{32'727});
51 
52   // Invalid year
53   check(SV("-32768 is not a valid year"), SV("{}"), std::chrono::year{-32'768});
54   check(SV("-32768 is not a valid year"), SV("{}"), std::chrono::year{32'768});
55 }
56 
57 template <class CharT>
58 static void test_valid_values() {
59   constexpr std::basic_string_view<CharT> fmt = SV(
60       "{:"
61       "%%C='%C'%t"
62       "%%EC='%EC'%t"
63       "%%y='%y'%t"
64       "%%Ey='%Ey'%t"
65       "%%Oy='%Oy'%t"
66       "%%Y='%Y'%t"
67       "%%EY='%EY'%t"
68       "%n}");
69   constexpr std::basic_string_view<CharT> lfmt = SV(
70       "{:L"
71       "%%C='%C'%t"
72       "%%EC='%EC'%t"
73       "%%y='%y'%t"
74       "%%Ey='%Ey'%t"
75       "%%Oy='%Oy'%t"
76       "%%Y='%Y'%t"
77       "%%EY='%EY'%t"
78       "%n}");
79 
80   const std::locale loc(LOCALE_ja_JP_UTF_8);
81   std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
82 
83   // Non localized output using C-locale
84   check(SV("%C='00'\t"
85 #if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__)
86            "%EC='00'\t"
87 #else
88            "%EC='0'\t"
89 #endif
90            "%y='00'\t"
91            "%Ey='00'\t"
92            "%Oy='00'\t"
93            "%Y='0000'\t"
94 #if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__)
95            "%EY='0000'\t"
96 #elif defined(_AIX)
97            "%EY=''\t"
98 #else
99            "%EY='0'\t"
100 #endif
101            "\n"),
102         fmt,
103         std::chrono::year{0});
104 
105   check(SV("%C='19'\t"
106            "%EC='19'\t"
107            "%y='70'\t"
108            "%Ey='70'\t"
109            "%Oy='70'\t"
110            "%Y='1970'\t"
111            "%EY='1970'\t"
112            "\n"),
113         fmt,
114         std::chrono::year{1970});
115 
116   check(SV("%C='20'\t"
117            "%EC='20'\t"
118            "%y='38'\t"
119            "%Ey='38'\t"
120            "%Oy='38'\t"
121            "%Y='2038'\t"
122            "%EY='2038'\t"
123            "\n"),
124         fmt,
125         std::chrono::year{2038});
126 
127   // Use the global locale (fr_FR)
128   check(SV("%C='00'\t"
129 #if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__)
130            "%EC='00'\t"
131 #else
132            "%EC='0'\t"
133 #endif
134            "%y='00'\t"
135            "%Ey='00'\t"
136            "%Oy='00'\t"
137            "%Y='0000'\t"
138 #if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__)
139            "%EY='0000'\t"
140 #elif defined(_AIX)
141            "%EY=''\t"
142 #else
143            "%EY='0'\t"
144 #endif
145            "\n"),
146         lfmt,
147         std::chrono::year{0});
148 
149   check(SV("%C='19'\t"
150            "%EC='19'\t"
151            "%y='70'\t"
152            "%Ey='70'\t"
153            "%Oy='70'\t"
154            "%Y='1970'\t"
155            "%EY='1970'\t"
156            "\n"),
157         lfmt,
158         std::chrono::year{1970});
159 
160   check(SV("%C='20'\t"
161            "%EC='20'\t"
162            "%y='38'\t"
163            "%Ey='38'\t"
164            "%Oy='38'\t"
165            "%Y='2038'\t"
166            "%EY='2038'\t"
167            "\n"),
168         lfmt,
169         std::chrono::year{2038});
170 
171   // Use supplied locale (ja_JP). This locale has a different alternate.
172 #if defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
173 
174   check(SV("%C='00'\t"
175 #  if defined(__APPLE__) || defined(_WIN32) || defined(__FreeBSD__)
176            "%EC='00'\t"
177 #  else
178            "%EC='0'\t"
179 #  endif
180            "%y='00'\t"
181            "%Ey='00'\t"
182            "%Oy='00'\t"
183            "%Y='0000'\t"
184 #  if defined(_AIX)
185            "%EY=''\t"
186 #  else
187            "%EY='0000'\t"
188 #  endif
189            "\n"),
190         lfmt,
191         std::chrono::year{0});
192 
193   check(SV("%C='19'\t"
194            "%EC='19'\t"
195            "%y='70'\t"
196            "%Ey='70'\t"
197            "%Oy='70'\t"
198            "%Y='1970'\t"
199            "%EY='1970'\t"
200            "\n"),
201         lfmt,
202         std::chrono::year{1970});
203 
204   check(SV("%C='20'\t"
205            "%EC='20'\t"
206            "%y='38'\t"
207            "%Ey='38'\t"
208            "%Oy='38'\t"
209            "%Y='2038'\t"
210            "%EY='2038'\t"
211            "\n"),
212         lfmt,
213         std::chrono::year{2038});
214 
215 #else // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
216   check(loc,
217         SV("%C='00'\t"
218            "%EC='紀元前'\t"
219            "%y='00'\t"
220   // https://sourceware.org/bugzilla/show_bug.cgi?id=23758
221 #  if defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29
222            "%Ey='1'\t"
223 #  else
224            "%Ey='01'\t"
225 #  endif
226            "%Oy='〇'\t"
227            "%Y='0000'\t"
228   // https://sourceware.org/bugzilla/show_bug.cgi?id=23758
229 #  if defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29
230            "%EY='紀元前1年'\t"
231 #  else
232            "%EY='紀元前01年'\t"
233 #  endif
234            "\n"),
235         lfmt,
236         std::chrono::year{0});
237 
238   check(loc,
239         SV("%C='19'\t"
240            "%EC='昭和'\t"
241            "%y='70'\t"
242            "%Ey='45'\t"
243            "%Oy='七十'\t"
244            "%Y='1970'\t"
245            "%EY='昭和45年'\t"
246            "\n"),
247         lfmt,
248         std::chrono::year{1970});
249 
250   // Note this test will fail if the Reiwa era ends before 2038.
251   check(loc,
252         SV("%C='20'\t"
253            "%EC='令和'\t"
254            "%y='38'\t"
255            "%Ey='20'\t"
256            "%Oy='三十八'\t"
257            "%Y='2038'\t"
258            "%EY='令和20年'\t"
259            "\n"),
260         lfmt,
261         std::chrono::year{2038});
262 #endif // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
263 
264   std::locale::global(std::locale::classic());
265 }
266 
267 template <class CharT>
268 static void test_padding() {
269   constexpr std::basic_string_view<CharT> fmt = SV("{:%%C='%C'%t%%y='%y'%t%%Y='%Y'%t%n}");
270   check(SV("%C='-100'\t%y='99'\t%Y='-9999'\t\n"), fmt, std::chrono::year{-9'999});
271   check(SV("%C='-10'\t%y='99'\t%Y='-0999'\t\n"), fmt, std::chrono::year{-999});
272   check(SV("%C='-1'\t%y='99'\t%Y='-0099'\t\n"), fmt, std::chrono::year{-99});
273   check(SV("%C='-1'\t%y='09'\t%Y='-0009'\t\n"), fmt, std::chrono::year{-9});
274   check(SV("%C='00'\t%y='00'\t%Y='0000'\t\n"), fmt, std::chrono::year{0});
275   check(SV("%C='00'\t%y='09'\t%Y='0009'\t\n"), fmt, std::chrono::year{9});
276   check(SV("%C='00'\t%y='99'\t%Y='0099'\t\n"), fmt, std::chrono::year{99});
277   check(SV("%C='09'\t%y='99'\t%Y='0999'\t\n"), fmt, std::chrono::year{999});
278   check(SV("%C='99'\t%y='99'\t%Y='9999'\t\n"), fmt, std::chrono::year{9'999});
279   check(SV("%C='100'\t%y='00'\t%Y='10000'\t\n"), fmt, std::chrono::year{10'000});
280 }
281 
282 template <class CharT>
283 static void test() {
284   test_no_chrono_specs<CharT>();
285   test_valid_values<CharT>();
286   test_padding<CharT>();
287   check_invalid_types<CharT>(
288       {SV("C"), SV("y"), SV("Y"), SV("EC"), SV("Ey"), SV("EY"), SV("Oy")}, std::chrono::year{1970});
289 
290   check_exception("The format specifier expects a '%' or a '}'", SV("{:A"), std::chrono::year{1970});
291   check_exception("The chrono specifiers contain a '{'", SV("{:%%{"), std::chrono::year{1970});
292   check_exception("End of input while parsing a conversion specifier", SV("{:%"), std::chrono::year{1970});
293   check_exception("End of input while parsing the modifier E", SV("{:%E"), std::chrono::year{1970});
294   check_exception("End of input while parsing the modifier O", SV("{:%O"), std::chrono::year{1970});
295 
296   // Precision not allowed
297   check_exception("The format specifier expects a '%' or a '}'", SV("{:.3}"), std::chrono::year{1970});
298 }
299 
300 int main(int, char**) {
301   test<char>();
302 
303 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
304   test<wchar_t>();
305 #endif
306 
307   return 0;
308 }
309