1//===-- Unittests for strftime --------------------------------------------===//
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#include "hdr/types/struct_tm.h"
10#include "src/__support/CPP/array.h"
11#include "src/__support/integer_to_string.h"
12#include "src/time/strftime.h"
13#include "src/time/time_constants.h"
14#include "test/UnitTest/Test.h"
15
16// Copied from sprintf_test.cpp.
17// TODO: put this somewhere more reusable, it's handy.
18// Subtract 1 from sizeof(expected_str) to account for the null byte.
19#define EXPECT_STREQ_LEN(actual_written, actual_str, expected_str) \
20 EXPECT_EQ(actual_written, sizeof(expected_str) - 1); \
21 EXPECT_STREQ(actual_str, expected_str);
22
23constexpr int get_adjusted_year(int year) {
24 // tm_year counts years since 1900, so subtract 1900 to get the tm_year for a
25 // given raw year.
26 return year - LIBC_NAMESPACE::time_constants::TIME_YEAR_BASE;
27}
28
29// TODO: Move this somewhere it can be reused. It seems like a useful tool to
30// have.
31// A helper class to generate simple padded numbers. It places the result in its
32// internal buffer, which is cleared on every call.
33class SimplePaddedNum {
34 static constexpr int BUFF_LEN = 16;
35 char buff[BUFF_LEN];
36 size_t cur_len; // length of string currently in buff
37
38 void clear_buff() {
39 // TODO: builtin_memset?
40 for (int i = 0; i < BUFF_LEN; ++i)
41 buff[i] = '\0';
42 }
43
44public:
45 SimplePaddedNum() = default;
46
47 // PRECONDITIONS: 0 < num < 2**31, min_width < 16
48 // Returns: Pointer to the start of the padded number as a string, stored in
49 // the internal buffer.
50 char *get_padded_num(int num, size_t min_width, char padding_char = '0') {
51 clear_buff();
52
53 // we're not handling the negative sign here, so padding on negative numbers
54 // will be incorrect. For this use case I consider that to be a reasonable
55 // tradeoff for simplicity. This is more meant for the cases where we can
56 // loop through all the possibilities, and for time those are all positive.
57 LIBC_NAMESPACE::IntegerToString<int> raw(num);
58 auto str = raw.view();
59 int leading_zeroes = static_cast<int>(min_width - raw.size());
60
61 int i = 0;
62 for (; static_cast<int>(i) < leading_zeroes; ++i)
63 buff[i] = padding_char;
64 for (size_t str_cur = 0, e = str.size(); str_cur < e; ++i, ++str_cur)
65 buff[i] = str[str_cur];
66 cur_len = i;
67 return buff;
68 }
69
70 size_t get_str_len() { return cur_len; }
71};
72
73TEST(LlvmLibcStrftimeTest, ConstantConversions) {
74 // this tests %n, %t, and %%, which read nothing.
75 struct tm time;
76 char buffer[100];
77 size_t written = 0;
78
79 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%n", &time);
80 EXPECT_STREQ_LEN(written, buffer, "\n");
81
82 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%t", &time);
83 EXPECT_STREQ_LEN(written, buffer, "\t");
84
85 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%%", &time);
86 EXPECT_STREQ_LEN(written, buffer, "%");
87}
88
89TEST(LlvmLibcStrftimeTest, CenturyTests) {
90 // this tests %C, which reads: [tm_year]
91 struct tm time;
92 char buffer[100];
93 size_t written = 0;
94
95 // basic tests
96 time.tm_year = get_adjusted_year(2022);
97 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
98 EXPECT_STREQ_LEN(written, buffer, "20");
99
100 time.tm_year = get_adjusted_year(11900);
101 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
102 EXPECT_STREQ_LEN(written, buffer, "119");
103
104 time.tm_year = get_adjusted_year(1900);
105 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
106 EXPECT_STREQ_LEN(written, buffer, "19");
107
108 time.tm_year = get_adjusted_year(900);
109 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
110 EXPECT_STREQ_LEN(written, buffer, "09");
111
112 time.tm_year = get_adjusted_year(0);
113 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
114 EXPECT_STREQ_LEN(written, buffer, "00");
115
116 // This case does not match what glibc does.
117 // Both the C standard and Posix say %C is "Replaced by the year divided by
118 // 100 and truncated to an integer, as a decimal number."
119 // What glibc does is it returns the century for the provided year.
120 // The difference is that glibc returns "-1" as the century for year -1, and
121 // "-2" for year -101.
122 // This case demonstrates that LLVM-libc instead just divides by 100, and
123 // returns the result. "00" for year -1, and "-1" for year -101.
124 // Personally, neither of these really feels right. Posix has a table of
125 // examples where it treats "%C%y" as identical to "%Y". Neither of these
126 // behaviors would handle that properly, you'd either get "-199" or "0099"
127 // (since %y always returns a number in the range [00-99]).
128 time.tm_year = get_adjusted_year(-1);
129 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
130 EXPECT_STREQ_LEN(written, buffer, "00");
131
132 time.tm_year = get_adjusted_year(-101);
133 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
134 EXPECT_STREQ_LEN(written, buffer, "-1");
135
136 time.tm_year = get_adjusted_year(-9001);
137 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
138 EXPECT_STREQ_LEN(written, buffer, "-90");
139
140 time.tm_year = get_adjusted_year(-10001);
141 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
142 EXPECT_STREQ_LEN(written, buffer, "-100");
143
144 // width tests (with the 0 flag, since the default padding is undefined).
145 time.tm_year = get_adjusted_year(2023);
146 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01C", &time);
147 EXPECT_STREQ_LEN(written, buffer, "20");
148
149 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02C", &time);
150 EXPECT_STREQ_LEN(written, buffer, "20");
151
152 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05C", &time);
153 EXPECT_STREQ_LEN(written, buffer, "00020");
154
155 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010C", &time);
156 EXPECT_STREQ_LEN(written, buffer, "0000000020");
157
158 time.tm_year = get_adjusted_year(900);
159 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01C", &time);
160 EXPECT_STREQ_LEN(written, buffer, "9");
161
162 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02C", &time);
163 EXPECT_STREQ_LEN(written, buffer, "09");
164
165 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05C", &time);
166 EXPECT_STREQ_LEN(written, buffer, "00009");
167
168 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010C", &time);
169 EXPECT_STREQ_LEN(written, buffer, "0000000009");
170
171 time.tm_year = get_adjusted_year(12345);
172 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01C", &time);
173 EXPECT_STREQ_LEN(written, buffer, "123");
174
175 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02C", &time);
176 EXPECT_STREQ_LEN(written, buffer, "123");
177
178 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05C", &time);
179 EXPECT_STREQ_LEN(written, buffer, "00123");
180
181 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010C", &time);
182 EXPECT_STREQ_LEN(written, buffer, "0000000123");
183
184 time.tm_year = get_adjusted_year(-123);
185 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01C", &time);
186 EXPECT_STREQ_LEN(written, buffer, "-1");
187
188 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02C", &time);
189 EXPECT_STREQ_LEN(written, buffer, "-1");
190
191 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05C", &time);
192 EXPECT_STREQ_LEN(written, buffer, "-0001");
193
194 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010C", &time);
195 EXPECT_STREQ_LEN(written, buffer, "-000000001");
196
197 // '+' flag tests
198 time.tm_year = get_adjusted_year(2023);
199 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1C", &time);
200 EXPECT_STREQ_LEN(written, buffer, "20");
201
202 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+2C", &time);
203 EXPECT_STREQ_LEN(written, buffer, "20");
204
205 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5C", &time);
206 EXPECT_STREQ_LEN(written, buffer, "+0020");
207
208 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10C", &time);
209 EXPECT_STREQ_LEN(written, buffer, "+000000020");
210
211 time.tm_year = get_adjusted_year(900);
212 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1C", &time);
213 EXPECT_STREQ_LEN(written, buffer, "9");
214
215 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+2C", &time);
216 EXPECT_STREQ_LEN(written, buffer, "09");
217
218 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5C", &time);
219 EXPECT_STREQ_LEN(written, buffer, "+0009");
220
221 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10C", &time);
222 EXPECT_STREQ_LEN(written, buffer, "+000000009");
223
224 time.tm_year = get_adjusted_year(12345);
225 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1C", &time);
226 EXPECT_STREQ_LEN(written, buffer, "+123");
227
228 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+2C", &time);
229 EXPECT_STREQ_LEN(written, buffer, "+123");
230
231 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5C", &time);
232 EXPECT_STREQ_LEN(written, buffer, "+0123");
233
234 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10C", &time);
235 EXPECT_STREQ_LEN(written, buffer, "+000000123");
236
237 time.tm_year = get_adjusted_year(-123);
238 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1C", &time);
239 EXPECT_STREQ_LEN(written, buffer, "-1");
240
241 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+2C", &time);
242 EXPECT_STREQ_LEN(written, buffer, "-1");
243
244 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5C", &time);
245 EXPECT_STREQ_LEN(written, buffer, "-0001");
246
247 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10C", &time);
248 EXPECT_STREQ_LEN(written, buffer, "-000000001");
249
250 // Posix specified tests:
251 time.tm_year = get_adjusted_year(17);
252 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
253 EXPECT_STREQ_LEN(written, buffer, "00");
254
255 time.tm_year = get_adjusted_year(270);
256 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%C", &time);
257 EXPECT_STREQ_LEN(written, buffer, "02");
258
259 time.tm_year = get_adjusted_year(270);
260 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+3C", &time);
261 EXPECT_STREQ_LEN(written, buffer, "+02");
262
263 time.tm_year = get_adjusted_year(12345);
264 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+3C", &time);
265 EXPECT_STREQ_LEN(written, buffer, "+123");
266
267 time.tm_year = get_adjusted_year(12345);
268 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%04C", &time);
269 EXPECT_STREQ_LEN(written, buffer, "0123");
270
271 time.tm_year = get_adjusted_year(12345);
272 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+4C", &time);
273 EXPECT_STREQ_LEN(written, buffer, "+123");
274
275 time.tm_year = get_adjusted_year(123456);
276 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%06C", &time);
277 EXPECT_STREQ_LEN(written, buffer, "001234");
278
279 time.tm_year = get_adjusted_year(123456);
280 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+6C", &time);
281 EXPECT_STREQ_LEN(written, buffer, "+01234");
282}
283
284TEST(LlvmLibcStrftimeTest, TwoDigitDayOfMonth) {
285 using LIBC_NAMESPACE::time_constants::MAX_DAYS_PER_MONTH;
286 // this tests %d, which reads: [tm_mday]
287 struct tm time;
288 char buffer[100];
289 size_t written = 0;
290 SimplePaddedNum spn;
291
292 // Tests on all the well defined values
293 for (int i = 1; i <= MAX_DAYS_PER_MONTH; ++i) {
294 time.tm_mday = i;
295 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%d", &time);
296 char *result = spn.get_padded_num(i, 2);
297
298 ASSERT_STREQ(buffer, result);
299 ASSERT_EQ(written, size_t(2));
300 }
301
302 // padding is technically undefined for this conversion, but we support it, so
303 // we need to test it.
304 time.tm_mday = 5;
305 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01d", &time);
306 EXPECT_STREQ_LEN(written, buffer, "5");
307
308 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02d", &time);
309 EXPECT_STREQ_LEN(written, buffer, "05");
310
311 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05d", &time);
312 EXPECT_STREQ_LEN(written, buffer, "00005");
313
314 time.tm_mday = 31;
315 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01d", &time);
316 EXPECT_STREQ_LEN(written, buffer, "31");
317
318 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02d", &time);
319 EXPECT_STREQ_LEN(written, buffer, "31");
320
321 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05d", &time);
322 EXPECT_STREQ_LEN(written, buffer, "00031");
323}
324
325TEST(LlvmLibcStrftimeTest, MinDigitDayOfMonth) {
326 // this tests %e, which reads: [tm_mday]
327 struct tm time;
328 char buffer[100];
329 size_t written = 0;
330 SimplePaddedNum spn;
331
332 // Tests on all the well defined values
333 for (int i = 1; i < 32; ++i) {
334 time.tm_mday = i;
335 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%e", &time);
336 char *result = spn.get_padded_num(i, 2, ' ');
337
338 ASSERT_STREQ(buffer, result);
339 ASSERT_EQ(written, spn.get_str_len());
340 }
341
342 // padding is technically undefined for this conversion, but we support it, so
343 // we need to test it.
344 time.tm_mday = 5;
345 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01e", &time);
346 EXPECT_STREQ_LEN(written, buffer, "5");
347
348 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02e", &time);
349 EXPECT_STREQ_LEN(written, buffer, "05");
350
351 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05e", &time);
352 EXPECT_STREQ_LEN(written, buffer, "00005");
353
354 time.tm_mday = 31;
355 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01e", &time);
356 EXPECT_STREQ_LEN(written, buffer, "31");
357
358 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02e", &time);
359 EXPECT_STREQ_LEN(written, buffer, "31");
360
361 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05e", &time);
362 EXPECT_STREQ_LEN(written, buffer, "00031");
363}
364
365TEST(LlvmLibcStrftimeTest, ISOYearOfCentury) {
366 // this tests %g, which reads: [tm_year, tm_wday, tm_yday]
367
368 // A brief primer on ISO dates:
369 // 1) ISO weeks start on Monday and end on Sunday
370 // 2) ISO years start on the Monday of the 1st ISO week of the year
371 // 3) The 1st ISO week of the ISO year has the 4th day of the Gregorian year.
372
373 struct tm time;
374 char buffer[100];
375 size_t written = 0;
376 SimplePaddedNum spn;
377
378 // a sunday in the middle of the year. No need to worry about rounding
379 time.tm_wday = 0;
380 time.tm_yday = 100;
381
382 // Test the easy cases
383 for (int i = 0; i < 102; ++i) {
384 time.tm_year = i;
385 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%g", &time);
386 char *result = spn.get_padded_num(i % 100, 2);
387
388 ASSERT_STREQ(buffer, result);
389 ASSERT_EQ(written, spn.get_str_len());
390 }
391
392 // Test the harder to round cases
393
394 // not a leap year. Not relevant for the start-of-year tests, but it does
395 // matter for the end-of-year tests.
396 time.tm_year = 99;
397
398 /*
399This table has an X for each day that should be in the previous year,
400everywhere else should be in the current year.
401
402 yday
403 0123456
404 i 1 Monday
405 s 2 Tuesday
406 o 3 Wednesday
407 w 4 Thursday
408 d 5 X Friday
409 a 6 XX Saturday
410 y 7 XXX Sunday
411*/
412
413 // check the first days of the year
414 for (int yday = 0; yday < 5; ++yday) {
415 for (int iso_wday = LIBC_NAMESPACE::time_constants::MONDAY; iso_wday < 8;
416 ++iso_wday) {
417 // start with monday, to match the ISO week.
418 time.tm_wday = iso_wday % LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK;
419 time.tm_yday = yday;
420
421 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%g", &time);
422
423 if (iso_wday <= LIBC_NAMESPACE::time_constants::THURSDAY || yday >= 3) {
424 // monday - thursday are never in the previous year, nor are the 4th and
425 // after.
426 EXPECT_STREQ_LEN(written, buffer, "99");
427 } else {
428 // iso_wday is 5, 6, or 7 and yday is 0, 1, or 2.
429 // days_since_thursday is therefor 1, 2, or 3.
430 const int days_since_thursday =
431 iso_wday - LIBC_NAMESPACE::time_constants::THURSDAY;
432
433 if (days_since_thursday > yday) {
434 EXPECT_STREQ_LEN(written, buffer, "98");
435 } else {
436 EXPECT_STREQ_LEN(written, buffer, "99");
437 }
438 }
439 }
440 }
441
442 /*
443 Similar to above, but the Xs represent being in the NEXT year. Also the
444 top counts down until the end of the year.
445
446 year end - yday
447 6543210
448 i 1 XXX Monday
449 s 2 XX Tuesday
450 o 3 X Wednesday
451 w 4 Thursday
452 d 5 Friday
453 a 6 Saturday
454 y 7 Sunday
455
456
457 If we place the charts next to each other, you can more easily see the
458 pattern:
459
460year end - yday yday
461 6543210 0123456
462 i 1 XXX Monday
463 s 2 XX Tuesday
464 o 3 X Wednesday
465 w 4 Thursday
466 d 5 X Friday
467 a 6 XX Saturday
468 y 7 XXX Sunday
469
470 From this we can see that thursday is always in the same ISO and regular
471 year, because the ISO year starts on the week with the 4th. Since Thursday
472 is at least 3 days from either edge of the ISO week, the first thursday of
473 the year is always in the first ISO week of the year.
474 */
475
476 // set up all the extra stuff to cover leap years.
477 struct tm time_leap_year;
478 char buffer_leap_year[100];
479 size_t written_leap_year = 0;
480 time_leap_year = time;
481 time_leap_year.tm_year = 100; // 2000 is a leap year.
482
483 // check the last days of the year. Checking 5 to make sure all the leap year
484 // cases are covered as well.
485 for (int days_left = 0; days_left < 5; ++days_left) {
486 for (int iso_wday = LIBC_NAMESPACE::time_constants::MONDAY; iso_wday < 8;
487 ++iso_wday) {
488 // start with monday, to match the ISO week.
489 time.tm_wday = iso_wday % LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK;
490 // subtract 1 from the max yday to handle yday being 0-indexed.
491 time.tm_yday = LIBC_NAMESPACE::time_constants::DAYS_PER_NON_LEAP_YEAR -
492 1 - days_left;
493
494 time_leap_year.tm_wday =
495 iso_wday % LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK;
496 time_leap_year.tm_yday =
497 LIBC_NAMESPACE::time_constants::LAST_DAY_OF_LEAP_YEAR - days_left;
498
499 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%g", &time);
500 written_leap_year = LIBC_NAMESPACE::strftime(
501 buffer_leap_year, sizeof(buffer_leap_year), "%g", &time_leap_year);
502
503 if (iso_wday >= LIBC_NAMESPACE::time_constants::THURSDAY ||
504 days_left >= 3) {
505 // thursday - sunday are never in the next year, nor are days more than
506 // 3 days before the end.
507 EXPECT_STREQ_LEN(written, buffer, "99");
508 EXPECT_STREQ_LEN(written_leap_year, buffer_leap_year, "00");
509 } else {
510 // iso_wday is 1, 2 or 3 and days_left is 0, 1, or 2
511 if (iso_wday + days_left <= 3) {
512 EXPECT_STREQ_LEN(written, buffer, "00");
513 EXPECT_STREQ_LEN(written_leap_year, buffer_leap_year, "01");
514 } else {
515 EXPECT_STREQ_LEN(written, buffer, "99");
516 EXPECT_STREQ_LEN(written_leap_year, buffer_leap_year, "00");
517 }
518 }
519 }
520 }
521
522 // padding is technically undefined for this conversion, but we support it, so
523 // we need to test it.
524 time.tm_year = 5;
525 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01g", &time);
526 EXPECT_STREQ_LEN(written, buffer, "5");
527
528 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02g", &time);
529 EXPECT_STREQ_LEN(written, buffer, "05");
530
531 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05g", &time);
532 EXPECT_STREQ_LEN(written, buffer, "00005");
533
534 time.tm_year = 31;
535 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01g", &time);
536 EXPECT_STREQ_LEN(written, buffer, "31");
537
538 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02g", &time);
539 EXPECT_STREQ_LEN(written, buffer, "31");
540
541 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05g", &time);
542 EXPECT_STREQ_LEN(written, buffer, "00031");
543}
544
545TEST(LlvmLibcStrftimeTest, ISOYear) {
546 // this tests %G, which reads: [tm_year, tm_wday, tm_yday]
547
548 // This stuff is all the same as above, but for brevity I'm not going to
549 // duplicate all the comments explaining exactly how ISO years work. The
550 // general comments are still here though.
551
552 struct tm time;
553 char buffer[100];
554 size_t written = 0;
555 SimplePaddedNum spn;
556
557 // a sunday in the middle of the year. No need to worry about rounding
558 time.tm_wday = 0;
559 time.tm_yday = 100;
560
561 // Test the easy cases
562 for (int i = 1; i < 10000; ++i) {
563 time.tm_year = get_adjusted_year(i);
564 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%G", &time);
565 char *result = spn.get_padded_num(i, 4);
566
567 ASSERT_STREQ(buffer, result);
568 ASSERT_EQ(written, spn.get_str_len());
569 }
570
571 // also check it handles years with extra digits properly
572 time.tm_year = get_adjusted_year(12345);
573 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%G", &time);
574 EXPECT_STREQ_LEN(written, buffer, "12345");
575
576 // Test the harder to round cases
577
578 // not a leap year. Not relevant for the start-of-year tests, but it does
579 // matter for the end-of-year tests.
580 time.tm_year = get_adjusted_year(1999);
581
582 // check the first days of the year
583 for (int yday = 0; yday < 5; ++yday) {
584 for (int iso_wday = 1; iso_wday < 8; ++iso_wday) {
585 // start with monday, to match the ISO week.
586 time.tm_wday = iso_wday % LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK;
587 time.tm_yday = yday;
588
589 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%G", &time);
590
591 if (iso_wday <= LIBC_NAMESPACE::time_constants::THURSDAY || yday >= 4) {
592 // monday - thursday are never in the previous year, nor are the 4th and
593 // after.
594 EXPECT_STREQ_LEN(written, buffer, "1999");
595 } else {
596 // iso_wday is 5, 6, or 7 and yday is 0, 1, or 2.
597 // days_since_thursday is therefor 1, 2, or 3.
598 const int days_since_thursday =
599 iso_wday - LIBC_NAMESPACE::time_constants::THURSDAY;
600
601 if (days_since_thursday > yday) {
602 EXPECT_STREQ_LEN(written, buffer, "1998");
603 } else {
604 EXPECT_STREQ_LEN(written, buffer, "1999");
605 }
606 }
607 }
608 }
609
610 // set up all the extra stuff to cover leap years.
611 struct tm time_leap_year;
612 char buffer_leap_year[100];
613 size_t written_leap_year = 0;
614 time_leap_year = time;
615 time_leap_year.tm_year = 100; // 2000 is a leap year.
616
617 // check the last days of the year. Checking 5 to make sure all the leap year
618 // cases are covered as well.
619 for (int days_left = 0; days_left < 5; ++days_left) {
620 for (int iso_wday = 1; iso_wday < 8; ++iso_wday) {
621 // start with monday, to match the ISO week.
622 time.tm_wday = iso_wday % LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK;
623 // subtract 1 from the max yday to handle yday being 0-indexed.
624 time.tm_yday =
625 LIBC_NAMESPACE::time_constants::LAST_DAY_OF_NON_LEAP_YEAR - days_left;
626
627 time_leap_year.tm_wday =
628 iso_wday % LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK;
629 time_leap_year.tm_yday =
630 LIBC_NAMESPACE::time_constants::LAST_DAY_OF_LEAP_YEAR - days_left;
631
632 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%G", &time);
633 written_leap_year = LIBC_NAMESPACE::strftime(
634 buffer_leap_year, sizeof(buffer_leap_year), "%G", &time_leap_year);
635
636 if (iso_wday >= 4 || days_left >= 3) {
637 // thursday - sunday are never in the next year, nor are days more than
638 // 3 days before the end.
639 EXPECT_STREQ_LEN(written, buffer, "1999");
640 EXPECT_STREQ_LEN(written_leap_year, buffer_leap_year, "2000");
641 } else {
642 // iso_wday is 1, 2 or 3 and days_left is 0, 1, or 2
643 if (iso_wday + days_left <= 3) {
644 EXPECT_STREQ_LEN(written, buffer, "2000");
645 EXPECT_STREQ_LEN(written_leap_year, buffer_leap_year, "2001");
646 } else {
647 EXPECT_STREQ_LEN(written, buffer, "1999");
648 EXPECT_STREQ_LEN(written_leap_year, buffer_leap_year, "2000");
649 }
650 }
651 }
652 }
653
654 // padding is technically undefined for this conversion, but we support it, so
655 // we need to test it.
656 time.tm_year = get_adjusted_year(5);
657 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01G", &time);
658 EXPECT_STREQ_LEN(written, buffer, "5");
659
660 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02G", &time);
661 EXPECT_STREQ_LEN(written, buffer, "05");
662
663 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05G", &time);
664 EXPECT_STREQ_LEN(written, buffer, "00005");
665
666 time.tm_year = get_adjusted_year(31);
667 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01G", &time);
668 EXPECT_STREQ_LEN(written, buffer, "31");
669
670 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02G", &time);
671 EXPECT_STREQ_LEN(written, buffer, "31");
672
673 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05G", &time);
674 EXPECT_STREQ_LEN(written, buffer, "00031");
675
676 time.tm_year = get_adjusted_year(2001);
677 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01G", &time);
678 EXPECT_STREQ_LEN(written, buffer, "2001");
679
680 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02G", &time);
681 EXPECT_STREQ_LEN(written, buffer, "2001");
682
683 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05G", &time);
684 EXPECT_STREQ_LEN(written, buffer, "02001");
685}
686
687TEST(LlvmLibcStrftimeTest, TwentyFourHour) {
688 // this tests %H, which reads: [tm_hour]
689 struct tm time;
690 char buffer[100];
691 size_t written = 0;
692 SimplePaddedNum spn;
693
694 // Tests on all the well defined values
695 for (int i = 0; i < 24; ++i) {
696 time.tm_hour = i;
697 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%H", &time);
698 char *result = spn.get_padded_num(i, 2);
699
700 ASSERT_STREQ(buffer, result);
701 ASSERT_EQ(written, spn.get_str_len());
702 }
703
704 // padding is technically undefined for this conversion, but we support it, so
705 // we need to test it.
706 time.tm_hour = 5;
707 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01H", &time);
708 EXPECT_STREQ_LEN(written, buffer, "5");
709
710 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02H", &time);
711 EXPECT_STREQ_LEN(written, buffer, "05");
712
713 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05H", &time);
714 EXPECT_STREQ_LEN(written, buffer, "00005");
715
716 time.tm_hour = 23;
717 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01H", &time);
718 EXPECT_STREQ_LEN(written, buffer, "23");
719
720 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02H", &time);
721 EXPECT_STREQ_LEN(written, buffer, "23");
722
723 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05H", &time);
724 EXPECT_STREQ_LEN(written, buffer, "00023");
725}
726
727TEST(LlvmLibcStrftimeTest, TwelveHour) {
728 // this tests %I, which reads: [tm_hour]
729 struct tm time;
730 char buffer[100];
731 size_t written = 0;
732 SimplePaddedNum spn;
733
734 time.tm_hour = 0;
735 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%I", &time);
736 EXPECT_STREQ_LEN(written, buffer, "12");
737
738 // Tests on all the well defined values, except 0 since it was easier to
739 // special case it.
740 for (int i = 1; i <= 12; ++i) {
741 char *result = spn.get_padded_num(i, 2);
742
743 time.tm_hour = i;
744 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%I", &time);
745 ASSERT_STREQ(buffer, result);
746 ASSERT_EQ(written, spn.get_str_len());
747
748 // hour + 12 should give the same result
749 time.tm_hour = i + 12;
750 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%I", &time);
751 ASSERT_STREQ(buffer, result);
752 ASSERT_EQ(written, spn.get_str_len());
753 }
754
755 // padding is technically undefined for this conversion, but we support it, so
756 // we need to test it.
757 time.tm_hour = 5;
758 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01I", &time);
759 EXPECT_STREQ_LEN(written, buffer, "5");
760
761 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02I", &time);
762 EXPECT_STREQ_LEN(written, buffer, "05");
763
764 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05I", &time);
765 EXPECT_STREQ_LEN(written, buffer, "00005");
766
767 time.tm_hour = 23;
768 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01I", &time);
769 EXPECT_STREQ_LEN(written, buffer, "11");
770
771 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02I", &time);
772 EXPECT_STREQ_LEN(written, buffer, "11");
773
774 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05I", &time);
775 EXPECT_STREQ_LEN(written, buffer, "00011");
776}
777
778TEST(LlvmLibcStrftimeTest, DayOfYear) {
779 // this tests %j, which reads: [tm_yday]
780 struct tm time;
781 char buffer[100];
782 size_t written = 0;
783 SimplePaddedNum spn;
784
785 // Tests on all the well defined values
786 for (int i = 0; i < LIBC_NAMESPACE::time_constants::DAYS_PER_LEAP_YEAR; ++i) {
787 time.tm_yday = i;
788 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%j", &time);
789 char *result = spn.get_padded_num(i + 1, 3);
790
791 ASSERT_STREQ(buffer, result);
792 ASSERT_EQ(written, spn.get_str_len());
793 }
794
795 // padding is technically undefined for this conversion, but we support it, so
796 // we need to test it.
797 time.tm_yday = 5 - 1;
798 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01j", &time);
799 EXPECT_STREQ_LEN(written, buffer, "5");
800
801 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02j", &time);
802 EXPECT_STREQ_LEN(written, buffer, "05");
803
804 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05j", &time);
805 EXPECT_STREQ_LEN(written, buffer, "00005");
806
807 time.tm_yday = 123 - 1;
808 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01j", &time);
809 EXPECT_STREQ_LEN(written, buffer, "123");
810
811 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02j", &time);
812 EXPECT_STREQ_LEN(written, buffer, "123");
813
814 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05j", &time);
815 EXPECT_STREQ_LEN(written, buffer, "00123");
816}
817
818TEST(LlvmLibcStrftimeTest, MonthOfYear) {
819 // this tests %m, which reads: [tm_mon]
820 struct tm time;
821 char buffer[100];
822 size_t written = 0;
823 SimplePaddedNum spn;
824
825 // Tests on all the well defined values
826 for (int i = 0; i < LIBC_NAMESPACE::time_constants::MONTHS_PER_YEAR; ++i) {
827 time.tm_mon = i;
828 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%m", &time);
829 // %m is 1 indexed, so add 1 to the number we're comparing to.
830 char *result = spn.get_padded_num(i + 1, 2);
831
832 ASSERT_STREQ(buffer, result);
833 ASSERT_EQ(written, spn.get_str_len());
834 }
835
836 // padding is technically undefined for this conversion, but we support it, so
837 // we need to test it.
838 time.tm_mon = 5 - 1;
839 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01m", &time);
840 EXPECT_STREQ_LEN(written, buffer, "5");
841
842 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02m", &time);
843 EXPECT_STREQ_LEN(written, buffer, "05");
844
845 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05m", &time);
846 EXPECT_STREQ_LEN(written, buffer, "00005");
847
848 time.tm_mon = 11 - 1;
849 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01m", &time);
850 EXPECT_STREQ_LEN(written, buffer, "11");
851
852 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02m", &time);
853 EXPECT_STREQ_LEN(written, buffer, "11");
854
855 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05m", &time);
856 EXPECT_STREQ_LEN(written, buffer, "00011");
857}
858
859TEST(LlvmLibcStrftimeTest, MinuteOfHour) {
860 // this tests %M, which reads: [tm_min]
861 struct tm time;
862 char buffer[100];
863 size_t written = 0;
864 SimplePaddedNum spn;
865
866 // Tests on all the well defined values
867 for (int i = 0; i < LIBC_NAMESPACE::time_constants::MINUTES_PER_HOUR; ++i) {
868 time.tm_min = i;
869 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%M", &time);
870 char *result = spn.get_padded_num(i, 2);
871
872 ASSERT_STREQ(buffer, result);
873 ASSERT_EQ(written, spn.get_str_len());
874 }
875
876 // padding is technically undefined for this conversion, but we support it, so
877 // we need to test it.
878 time.tm_min = 5;
879 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01M", &time);
880 EXPECT_STREQ_LEN(written, buffer, "5");
881
882 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02M", &time);
883 EXPECT_STREQ_LEN(written, buffer, "05");
884
885 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05M", &time);
886 EXPECT_STREQ_LEN(written, buffer, "00005");
887
888 time.tm_min = 11;
889 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01M", &time);
890 EXPECT_STREQ_LEN(written, buffer, "11");
891
892 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02M", &time);
893 EXPECT_STREQ_LEN(written, buffer, "11");
894
895 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05M", &time);
896 EXPECT_STREQ_LEN(written, buffer, "00011");
897}
898
899TEST(LlvmLibcStrftimeTest, SecondsSinceEpoch) {
900 // this tests %s, which reads: [tm_year, tm_mon, tm_mday, tm_hour, tm_min,
901 // tm_sec, tm_isdst]
902 struct tm time;
903 char buffer[100];
904 size_t written = 0;
905
906 time.tm_year = get_adjusted_year(1970);
907 // yday is not used, the day of the year is calculated from the month and mday
908 time.tm_mon = 0;
909 time.tm_mday = 1; // the only 1-indexed member
910 time.tm_hour = 0;
911 time.tm_min = 0;
912 time.tm_sec = 1;
913 time.tm_isdst = 0;
914
915 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%s", &time);
916 EXPECT_STREQ_LEN(written, buffer, "1");
917
918 // The time as of writing this test
919 time.tm_year = get_adjusted_year(2025);
920 time.tm_mon = 1;
921 time.tm_mday = 4;
922 time.tm_hour = 11;
923 time.tm_min = 8;
924 time.tm_sec = 41;
925 time.tm_isdst = 0;
926
927 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%s", &time);
928 // if you run your system's strftime to compare you will likely get a slightly
929 // different result because it's supposed to respect timezones.
930 EXPECT_STREQ_LEN(written, buffer, "1738667321");
931
932 // Thorough testing of the mktime mechanism is done in the mktime tests, so
933 // they aren't duplicated here.
934
935 // padding is technically undefined for this conversion, but we support it, so
936 // we need to test it.
937 time.tm_year = get_adjusted_year(1970);
938 time.tm_mon = 0;
939 time.tm_mday = 1;
940 time.tm_hour = 0;
941 time.tm_min = 0;
942 time.tm_sec = 5;
943 time.tm_isdst = 0;
944 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01s", &time);
945 EXPECT_STREQ_LEN(written, buffer, "5");
946
947 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02s", &time);
948 EXPECT_STREQ_LEN(written, buffer, "05");
949
950 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05s", &time);
951 EXPECT_STREQ_LEN(written, buffer, "00005");
952
953 time.tm_min = 11;
954 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01s", &time);
955 EXPECT_STREQ_LEN(written, buffer, "665");
956
957 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02s", &time);
958 EXPECT_STREQ_LEN(written, buffer, "665");
959
960 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05s", &time);
961 EXPECT_STREQ_LEN(written, buffer, "00665");
962}
963
964TEST(LlvmLibcStrftimeTest, SecondOfMinute) {
965 // this tests %S, which reads: [tm_sec]
966 struct tm time;
967 char buffer[100];
968 size_t written = 0;
969 SimplePaddedNum spn;
970
971 // Tests on all the well defined values
972 for (int i = 0; i < LIBC_NAMESPACE::time_constants::SECONDS_PER_MIN; ++i) {
973 time.tm_sec = i;
974 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%S", &time);
975 char *result = spn.get_padded_num(i, 2);
976
977 ASSERT_STREQ(buffer, result);
978 ASSERT_EQ(written, spn.get_str_len());
979 }
980
981 // padding is technically undefined for this conversion, but we support it, so
982 // we need to test it.
983 time.tm_sec = 5;
984 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01S", &time);
985 EXPECT_STREQ_LEN(written, buffer, "5");
986
987 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02S", &time);
988 EXPECT_STREQ_LEN(written, buffer, "05");
989
990 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05S", &time);
991 EXPECT_STREQ_LEN(written, buffer, "00005");
992
993 time.tm_sec = 11;
994 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01S", &time);
995 EXPECT_STREQ_LEN(written, buffer, "11");
996
997 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02S", &time);
998 EXPECT_STREQ_LEN(written, buffer, "11");
999
1000 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05S", &time);
1001 EXPECT_STREQ_LEN(written, buffer, "00011");
1002}
1003
1004TEST(LlvmLibcStrftimeTest, ISODayOfWeek) {
1005 // this tests %u, which reads: [tm_wday]
1006 struct tm time;
1007 char buffer[100];
1008 size_t written = 0;
1009 SimplePaddedNum spn;
1010
1011 time.tm_wday = LIBC_NAMESPACE::time_constants::SUNDAY;
1012 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%u", &time);
1013 EXPECT_STREQ_LEN(written, buffer, "7");
1014
1015 // Tests on all the well defined values except for sunday, which is 0 in
1016 // normal weekdays but 7 here.
1017 for (int i = LIBC_NAMESPACE::time_constants::MONDAY;
1018 i <= LIBC_NAMESPACE::time_constants::SATURDAY; ++i) {
1019 time.tm_wday = i;
1020 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%u", &time);
1021 char *result = spn.get_padded_num(i, 1);
1022
1023 ASSERT_STREQ(buffer, result);
1024 ASSERT_EQ(written, spn.get_str_len());
1025 }
1026
1027 // padding is technically undefined for this conversion, but we support it, so
1028 // we need to test it.
1029 time.tm_wday = 5;
1030 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01u", &time);
1031 EXPECT_STREQ_LEN(written, buffer, "5");
1032
1033 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02u", &time);
1034 EXPECT_STREQ_LEN(written, buffer, "05");
1035
1036 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05u", &time);
1037 EXPECT_STREQ_LEN(written, buffer, "00005");
1038}
1039
1040TEST(LlvmLibcStrftimeTest, WeekOfYearStartingSunday) {
1041 // this tests %U, which reads: [tm_year, tm_wday, tm_yday]
1042 struct tm time;
1043 char buffer[100];
1044 size_t written = 0;
1045 SimplePaddedNum spn;
1046
1047 // setting the year to a leap year, but it doesn't actually matter. This
1048 // conversion doesn't end up checking the year at all.
1049 time.tm_year = get_adjusted_year(2000);
1050
1051 const int WEEK_START = LIBC_NAMESPACE::time_constants::SUNDAY;
1052
1053 for (int first_weekday = LIBC_NAMESPACE::time_constants::SUNDAY;
1054 first_weekday <= LIBC_NAMESPACE::time_constants::SATURDAY;
1055 ++first_weekday) {
1056 time.tm_wday = first_weekday;
1057 int cur_week = 0;
1058
1059 // iterate through the year, starting on first_weekday.
1060 for (int yday = 0;
1061 yday < LIBC_NAMESPACE::time_constants::DAYS_PER_LEAP_YEAR; ++yday) {
1062 time.tm_yday = yday;
1063 // If the week just ended, move to the next week.
1064 if (time.tm_wday == WEEK_START)
1065 ++cur_week;
1066
1067 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%U", &time);
1068 char *result = spn.get_padded_num(cur_week, 2);
1069
1070 ASSERT_STREQ(buffer, result);
1071 ASSERT_EQ(written, spn.get_str_len());
1072
1073 // a day has passed, move to the next weekday, looping as necessary.
1074 time.tm_wday =
1075 (time.tm_wday + 1) % LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK;
1076 }
1077 }
1078
1079 // padding is technically undefined for this conversion, but we support it, so
1080 // we need to test it.
1081 time.tm_wday = LIBC_NAMESPACE::time_constants::SUNDAY;
1082 time.tm_yday = 22;
1083 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01U", &time);
1084 EXPECT_STREQ_LEN(written, buffer, "4");
1085
1086 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02U", &time);
1087 EXPECT_STREQ_LEN(written, buffer, "04");
1088
1089 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05U", &time);
1090 EXPECT_STREQ_LEN(written, buffer, "00004");
1091
1092 time.tm_wday = LIBC_NAMESPACE::time_constants::SUNDAY;
1093 time.tm_yday = 78;
1094 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01U", &time);
1095 EXPECT_STREQ_LEN(written, buffer, "12");
1096
1097 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02U", &time);
1098 EXPECT_STREQ_LEN(written, buffer, "12");
1099
1100 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05U", &time);
1101 EXPECT_STREQ_LEN(written, buffer, "00012");
1102}
1103
1104TEST(LlvmLibcStrftimeTest, ISOWeekOfYear) {
1105 // this tests %V, which reads: [tm_year, tm_wday, tm_yday]
1106 struct tm time;
1107 char buffer[100];
1108 size_t written = 0;
1109 SimplePaddedNum spn;
1110
1111 const int starting_year = get_adjusted_year(1999);
1112
1113 // we're going to check the days from 1999 to 2001 to cover all the
1114 // transitions to and from leap years and non-leap years (the start of 1999
1115 // and end of 2001 cover the non-leap years).
1116 const int days_to_check = // 1096
1117 LIBC_NAMESPACE::time_constants::DAYS_PER_NON_LEAP_YEAR +
1118 LIBC_NAMESPACE::time_constants::DAYS_PER_LEAP_YEAR +
1119 LIBC_NAMESPACE::time_constants::DAYS_PER_NON_LEAP_YEAR;
1120
1121 const int WEEK_START = LIBC_NAMESPACE::time_constants::MONDAY;
1122
1123 for (int first_weekday = LIBC_NAMESPACE::time_constants::SUNDAY;
1124 first_weekday <= LIBC_NAMESPACE::time_constants::SATURDAY;
1125 ++first_weekday) {
1126 time.tm_year = starting_year;
1127 time.tm_wday = first_weekday;
1128 time.tm_yday = 0;
1129 int cur_week = 1;
1130 if (first_weekday == LIBC_NAMESPACE::time_constants::SUNDAY ||
1131 first_weekday == LIBC_NAMESPACE::time_constants::SATURDAY)
1132 cur_week = 52;
1133 else if (first_weekday == LIBC_NAMESPACE::time_constants::FRIDAY)
1134 cur_week = 53;
1135
1136 // iterate through the year, starting on first_weekday.
1137 for (size_t cur_day = 0; cur_day < days_to_check; ++cur_day) {
1138 // If the week just ended, move to the next week.
1139
1140 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%V", &time);
1141 char *result = spn.get_padded_num(cur_week, 2);
1142
1143 ASSERT_STREQ(buffer, result);
1144 ASSERT_EQ(written, spn.get_str_len());
1145
1146 // a day has passed, increment the counters.
1147 ++time.tm_yday;
1148 if (time.tm_yday ==
1149 (time.tm_year == get_adjusted_year(2000)
1150 ? LIBC_NAMESPACE::time_constants::DAYS_PER_LEAP_YEAR
1151 : LIBC_NAMESPACE::time_constants::DAYS_PER_NON_LEAP_YEAR)) {
1152 time.tm_yday = 0;
1153 ++time.tm_year;
1154 }
1155
1156 time.tm_wday =
1157 (time.tm_wday + 1) % LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK;
1158 if (time.tm_wday == WEEK_START) {
1159 ++cur_week;
1160 const int days_left_in_year =
1161 (time.tm_year == get_adjusted_year(2000)
1162 ? LIBC_NAMESPACE::time_constants::LAST_DAY_OF_LEAP_YEAR
1163 : LIBC_NAMESPACE::time_constants::LAST_DAY_OF_NON_LEAP_YEAR) -
1164 time.tm_yday;
1165
1166 // if the week we're currently in is in the next year, or if the year
1167 // has turned over, reset the week.
1168 if (days_left_in_year < 3 || (cur_week > 51 && time.tm_yday < 10))
1169 cur_week = 1;
1170 }
1171 }
1172 }
1173
1174 // padding is technically undefined for this conversion, but we support it, so
1175 // we need to test it.
1176 time.tm_wday = LIBC_NAMESPACE::time_constants::SUNDAY;
1177 time.tm_yday = 22;
1178 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01U", &time);
1179 EXPECT_STREQ_LEN(written, buffer, "4");
1180
1181 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02U", &time);
1182 EXPECT_STREQ_LEN(written, buffer, "04");
1183
1184 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05U", &time);
1185 EXPECT_STREQ_LEN(written, buffer, "00004");
1186
1187 time.tm_wday = LIBC_NAMESPACE::time_constants::SUNDAY;
1188 time.tm_yday = 78;
1189 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01U", &time);
1190 EXPECT_STREQ_LEN(written, buffer, "12");
1191
1192 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02U", &time);
1193 EXPECT_STREQ_LEN(written, buffer, "12");
1194
1195 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05U", &time);
1196 EXPECT_STREQ_LEN(written, buffer, "00012");
1197}
1198
1199TEST(LlvmLibcStrftimeTest, DayOfWeek) {
1200 // this tests %w, which reads: [tm_wday]
1201 struct tm time;
1202 char buffer[100];
1203 size_t written = 0;
1204 SimplePaddedNum spn;
1205
1206 // Tests on all the well defined values.
1207 for (int i = LIBC_NAMESPACE::time_constants::SUNDAY;
1208 i <= LIBC_NAMESPACE::time_constants::SATURDAY; ++i) {
1209 time.tm_wday = i;
1210 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%w", &time);
1211 char *result = spn.get_padded_num(i, 1);
1212
1213 ASSERT_STREQ(buffer, result);
1214 ASSERT_EQ(written, spn.get_str_len());
1215 }
1216
1217 // padding is technically undefined for this conversion, but we support it, so
1218 // we need to test it.
1219 time.tm_wday = 5;
1220 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01w", &time);
1221 EXPECT_STREQ_LEN(written, buffer, "5");
1222
1223 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02w", &time);
1224 EXPECT_STREQ_LEN(written, buffer, "05");
1225
1226 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05w", &time);
1227 EXPECT_STREQ_LEN(written, buffer, "00005");
1228}
1229
1230TEST(LlvmLibcStrftimeTest, WeekOfYearStartingMonday) {
1231 // this tests %W, which reads: [tm_year, tm_wday, tm_yday]
1232 struct tm time;
1233 char buffer[100];
1234 size_t written = 0;
1235 SimplePaddedNum spn;
1236
1237 // setting the year to a leap year, but it doesn't actually matter. This
1238 // conversion doesn't end up checking the year at all.
1239 time.tm_year = get_adjusted_year(2000);
1240
1241 const int WEEK_START = LIBC_NAMESPACE::time_constants::MONDAY;
1242
1243 for (int first_weekday = LIBC_NAMESPACE::time_constants::SUNDAY;
1244 first_weekday <= LIBC_NAMESPACE::time_constants::SATURDAY;
1245 ++first_weekday) {
1246 time.tm_wday = first_weekday;
1247 int cur_week = 0;
1248
1249 // iterate through the year, starting on first_weekday.
1250 for (int yday = 0;
1251 yday < LIBC_NAMESPACE::time_constants::DAYS_PER_LEAP_YEAR; ++yday) {
1252 time.tm_yday = yday;
1253 // If the week just ended, move to the next week.
1254 if (time.tm_wday == WEEK_START)
1255 ++cur_week;
1256
1257 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%W", &time);
1258 char *result = spn.get_padded_num(cur_week, 2);
1259
1260 ASSERT_STREQ(buffer, result);
1261 ASSERT_EQ(written, spn.get_str_len());
1262
1263 // a day has passed, move to the next weekday, looping as necessary.
1264 time.tm_wday =
1265 (time.tm_wday + 1) % LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK;
1266 }
1267 }
1268
1269 // padding is technically undefined for this conversion, but we support it, so
1270 // we need to test it.
1271 time.tm_wday = LIBC_NAMESPACE::time_constants::MONDAY;
1272 time.tm_yday = 22;
1273 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01W", &time);
1274 EXPECT_STREQ_LEN(written, buffer, "4");
1275
1276 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02W", &time);
1277 EXPECT_STREQ_LEN(written, buffer, "04");
1278
1279 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05W", &time);
1280 EXPECT_STREQ_LEN(written, buffer, "00004");
1281
1282 time.tm_wday = LIBC_NAMESPACE::time_constants::MONDAY;
1283 time.tm_yday = 78;
1284 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01W", &time);
1285 EXPECT_STREQ_LEN(written, buffer, "12");
1286
1287 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02W", &time);
1288 EXPECT_STREQ_LEN(written, buffer, "12");
1289
1290 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05W", &time);
1291 EXPECT_STREQ_LEN(written, buffer, "00012");
1292}
1293
1294TEST(LlvmLibcStrftimeTest, YearOfCentury) {
1295 // this tests %y, which reads: [tm_year]
1296 struct tm time;
1297 char buffer[100];
1298 size_t written = 0;
1299 SimplePaddedNum spn;
1300
1301 time.tm_year = get_adjusted_year(2000);
1302
1303 // iterate through the year, starting on first_weekday.
1304 for (int year = 1900; year < 2001; ++year) {
1305 time.tm_year = get_adjusted_year(year);
1306
1307 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%y", &time);
1308 char *result = spn.get_padded_num(year % 100, 2);
1309
1310 ASSERT_STREQ(buffer, result);
1311 ASSERT_EQ(written, spn.get_str_len());
1312 }
1313
1314 // padding is technically undefined for this conversion, but we support it, so
1315 // we need to test it.
1316 time.tm_year = get_adjusted_year(2004);
1317 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01y", &time);
1318 EXPECT_STREQ_LEN(written, buffer, "4");
1319
1320 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02y", &time);
1321 EXPECT_STREQ_LEN(written, buffer, "04");
1322
1323 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05y", &time);
1324 EXPECT_STREQ_LEN(written, buffer, "00004");
1325
1326 time.tm_year = get_adjusted_year(12345);
1327 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01y", &time);
1328 EXPECT_STREQ_LEN(written, buffer, "45");
1329
1330 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%02y", &time);
1331 EXPECT_STREQ_LEN(written, buffer, "45");
1332
1333 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05y", &time);
1334 EXPECT_STREQ_LEN(written, buffer, "00045");
1335}
1336
1337TEST(LlvmLibcStrftimeTest, FullYearTests) {
1338 // this tests %Y, which reads: [tm_year]
1339 struct tm time;
1340 char buffer[100];
1341 size_t written = 0;
1342 SimplePaddedNum spn;
1343
1344 // Test the easy cases
1345 for (int i = 1; i < 10000; ++i) {
1346 time.tm_year = get_adjusted_year(i);
1347 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1348 char *result = spn.get_padded_num(i, 4);
1349
1350 ASSERT_STREQ(buffer, result);
1351 ASSERT_EQ(written, spn.get_str_len());
1352 }
1353
1354 time.tm_year = get_adjusted_year(11900);
1355 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1356 EXPECT_STREQ_LEN(written, buffer, "11900");
1357
1358 time.tm_year = get_adjusted_year(0);
1359 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1360 EXPECT_STREQ_LEN(written, buffer, "0000");
1361
1362 time.tm_year = get_adjusted_year(-1);
1363 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1364 // TODO: should this be what we standardize? Posix doesn't specify what to do
1365 // about negative numbers
1366 EXPECT_STREQ_LEN(written, buffer, "-001");
1367
1368 time.tm_year = get_adjusted_year(-9001);
1369 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1370 EXPECT_STREQ_LEN(written, buffer, "-9001");
1371
1372 time.tm_year = get_adjusted_year(-10001);
1373 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1374 EXPECT_STREQ_LEN(written, buffer, "-10001");
1375
1376 // width tests (with the 0 flag, since the default padding is undefined).
1377 time.tm_year = get_adjusted_year(2023);
1378 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01Y", &time);
1379 EXPECT_STREQ_LEN(written, buffer, "2023");
1380
1381 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%04Y", &time);
1382 EXPECT_STREQ_LEN(written, buffer, "2023");
1383
1384 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05Y", &time);
1385 EXPECT_STREQ_LEN(written, buffer, "02023");
1386
1387 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010Y", &time);
1388 EXPECT_STREQ_LEN(written, buffer, "0000002023");
1389
1390 time.tm_year = get_adjusted_year(900);
1391 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01Y", &time);
1392 EXPECT_STREQ_LEN(written, buffer, "900");
1393
1394 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%04Y", &time);
1395 EXPECT_STREQ_LEN(written, buffer, "0900");
1396
1397 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05Y", &time);
1398 EXPECT_STREQ_LEN(written, buffer, "00900");
1399
1400 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010Y", &time);
1401 EXPECT_STREQ_LEN(written, buffer, "0000000900");
1402
1403 time.tm_year = get_adjusted_year(12345);
1404 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01Y", &time);
1405 EXPECT_STREQ_LEN(written, buffer, "12345");
1406
1407 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%04Y", &time);
1408 EXPECT_STREQ_LEN(written, buffer, "12345");
1409
1410 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05Y", &time);
1411 EXPECT_STREQ_LEN(written, buffer, "12345");
1412
1413 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010Y", &time);
1414 EXPECT_STREQ_LEN(written, buffer, "0000012345");
1415
1416 time.tm_year = get_adjusted_year(-123);
1417 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01Y", &time);
1418 EXPECT_STREQ_LEN(written, buffer, "-123");
1419
1420 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%04Y", &time);
1421 EXPECT_STREQ_LEN(written, buffer, "-123");
1422
1423 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05Y", &time);
1424 EXPECT_STREQ_LEN(written, buffer, "-0123");
1425
1426 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010Y", &time);
1427 EXPECT_STREQ_LEN(written, buffer, "-000000123");
1428
1429 // '+' flag tests
1430 time.tm_year = get_adjusted_year(2023);
1431 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1Y", &time);
1432 EXPECT_STREQ_LEN(written, buffer, "2023");
1433
1434 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+4Y", &time);
1435 EXPECT_STREQ_LEN(written, buffer, "2023");
1436
1437 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5Y", &time);
1438 EXPECT_STREQ_LEN(written, buffer, "+2023");
1439
1440 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10Y", &time);
1441 EXPECT_STREQ_LEN(written, buffer, "+000002023");
1442
1443 time.tm_year = get_adjusted_year(900);
1444 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1Y", &time);
1445 EXPECT_STREQ_LEN(written, buffer, "900");
1446
1447 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+4Y", &time);
1448 EXPECT_STREQ_LEN(written, buffer, "0900");
1449
1450 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5Y", &time);
1451 EXPECT_STREQ_LEN(written, buffer, "+0900");
1452
1453 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10Y", &time);
1454 EXPECT_STREQ_LEN(written, buffer, "+000000900");
1455
1456 time.tm_year = get_adjusted_year(12345);
1457 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1Y", &time);
1458 EXPECT_STREQ_LEN(written, buffer, "+12345");
1459
1460 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+4Y", &time);
1461 EXPECT_STREQ_LEN(written, buffer, "+12345");
1462
1463 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5Y", &time);
1464 EXPECT_STREQ_LEN(written, buffer, "+12345");
1465
1466 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10Y", &time);
1467 EXPECT_STREQ_LEN(written, buffer, "+000012345");
1468
1469 time.tm_year = get_adjusted_year(-123);
1470 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1Y", &time);
1471 EXPECT_STREQ_LEN(written, buffer, "-123");
1472
1473 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+4Y", &time);
1474 EXPECT_STREQ_LEN(written, buffer, "-123");
1475
1476 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5Y", &time);
1477 EXPECT_STREQ_LEN(written, buffer, "-0123");
1478
1479 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10Y", &time);
1480 EXPECT_STREQ_LEN(written, buffer, "-000000123");
1481
1482 // Posix specified tests:
1483 time.tm_year = get_adjusted_year(1970);
1484 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1485 EXPECT_STREQ_LEN(written, buffer, "1970");
1486
1487 time.tm_year = get_adjusted_year(1970);
1488 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+4Y", &time);
1489 EXPECT_STREQ_LEN(written, buffer, "1970");
1490
1491 time.tm_year = get_adjusted_year(27);
1492 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1493 EXPECT_STREQ_LEN(written, buffer, "0027");
1494
1495 time.tm_year = get_adjusted_year(270);
1496 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1497 EXPECT_STREQ_LEN(written, buffer, "0270");
1498
1499 time.tm_year = get_adjusted_year(270);
1500 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+4Y", &time);
1501 EXPECT_STREQ_LEN(written, buffer, "0270");
1502
1503 time.tm_year = get_adjusted_year(12345);
1504 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%Y", &time);
1505 EXPECT_STREQ_LEN(written, buffer, "12345");
1506
1507 time.tm_year = get_adjusted_year(12345);
1508 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+4Y", &time);
1509 EXPECT_STREQ_LEN(written, buffer, "+12345");
1510
1511 time.tm_year = get_adjusted_year(12345);
1512 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05Y", &time);
1513 EXPECT_STREQ_LEN(written, buffer, "12345");
1514
1515 time.tm_year = get_adjusted_year(270);
1516 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5Y", &time);
1517 EXPECT_STREQ_LEN(written, buffer, "+0270");
1518
1519 time.tm_year = get_adjusted_year(12345);
1520 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+5Y", &time);
1521 EXPECT_STREQ_LEN(written, buffer, "+12345");
1522
1523 time.tm_year = get_adjusted_year(12345);
1524 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%06Y", &time);
1525 EXPECT_STREQ_LEN(written, buffer, "012345");
1526
1527 time.tm_year = get_adjusted_year(12345);
1528 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+6Y", &time);
1529 EXPECT_STREQ_LEN(written, buffer, "+12345");
1530
1531 time.tm_year = get_adjusted_year(123456);
1532 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%08Y", &time);
1533 EXPECT_STREQ_LEN(written, buffer, "00123456");
1534
1535 time.tm_year = get_adjusted_year(123456);
1536 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+8Y", &time);
1537 EXPECT_STREQ_LEN(written, buffer, "+0123456");
1538}
1539
1540// String conversions
1541
1542struct num_str_pair {
1543 int num;
1544 LIBC_NAMESPACE::cpp::string_view str;
1545};
1546
1547TEST(LlvmLibcStrftimeTest, ShortWeekdayName) {
1548 // this tests %a, which reads: [tm_wday]
1549 struct tm time;
1550 char buffer[100];
1551 size_t written = 0;
1552
1553 constexpr LIBC_NAMESPACE::cpp::array<
1554 num_str_pair, LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK>
1555 WEEKDAY_PAIRS = {{
1556 {LIBC_NAMESPACE::time_constants::SUNDAY, "Sun"},
1557 {LIBC_NAMESPACE::time_constants::MONDAY, "Mon"},
1558 {LIBC_NAMESPACE::time_constants::TUESDAY, "Tue"},
1559 {LIBC_NAMESPACE::time_constants::WEDNESDAY, "Wed"},
1560 {LIBC_NAMESPACE::time_constants::THURSDAY, "Thu"},
1561 {LIBC_NAMESPACE::time_constants::FRIDAY, "Fri"},
1562 {LIBC_NAMESPACE::time_constants::SATURDAY, "Sat"},
1563 }};
1564
1565 for (size_t i = 0; i < WEEKDAY_PAIRS.size(); ++i) {
1566 time.tm_wday = WEEKDAY_PAIRS[i].num;
1567 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%a", &time);
1568 EXPECT_STREQ(buffer, WEEKDAY_PAIRS[i].str.data());
1569 EXPECT_EQ(written, WEEKDAY_PAIRS[i].str.size());
1570 }
1571
1572 // check invalid weekdays
1573 time.tm_wday = -1;
1574 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%a", &time);
1575 EXPECT_STREQ_LEN(written, buffer, "?");
1576
1577 time.tm_wday = LIBC_NAMESPACE::time_constants::SATURDAY + 1;
1578 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%a", &time);
1579 EXPECT_STREQ_LEN(written, buffer, "?");
1580
1581 // padding is technically undefined for this conversion, but we support it, so
1582 // we need to test it.
1583 time.tm_wday = LIBC_NAMESPACE::time_constants::THURSDAY;
1584 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1a", &time);
1585 EXPECT_STREQ_LEN(written, buffer, "Thu");
1586
1587 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%3a", &time);
1588 EXPECT_STREQ_LEN(written, buffer, "Thu");
1589
1590 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10a", &time);
1591 EXPECT_STREQ_LEN(written, buffer, " Thu");
1592
1593 time.tm_wday = -1;
1594 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1a", &time);
1595 EXPECT_STREQ_LEN(written, buffer, "?");
1596
1597 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%3a", &time);
1598 EXPECT_STREQ_LEN(written, buffer, " ?");
1599
1600 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10a", &time);
1601 EXPECT_STREQ_LEN(written, buffer, " ?");
1602}
1603
1604TEST(LlvmLibcStrftimeTest, FullWeekdayName) {
1605 // this tests %a, which reads: [tm_wday]
1606 struct tm time;
1607 char buffer[100];
1608 size_t written = 0;
1609
1610 constexpr LIBC_NAMESPACE::cpp::array<
1611 num_str_pair, LIBC_NAMESPACE::time_constants::DAYS_PER_WEEK>
1612 WEEKDAY_PAIRS = {{
1613 {LIBC_NAMESPACE::time_constants::SUNDAY, "Sunday"},
1614 {LIBC_NAMESPACE::time_constants::MONDAY, "Monday"},
1615 {LIBC_NAMESPACE::time_constants::TUESDAY, "Tuesday"},
1616 {LIBC_NAMESPACE::time_constants::WEDNESDAY, "Wednesday"},
1617 {LIBC_NAMESPACE::time_constants::THURSDAY, "Thursday"},
1618 {LIBC_NAMESPACE::time_constants::FRIDAY, "Friday"},
1619 {LIBC_NAMESPACE::time_constants::SATURDAY, "Saturday"},
1620 }};
1621
1622 for (size_t i = 0; i < WEEKDAY_PAIRS.size(); ++i) {
1623 time.tm_wday = WEEKDAY_PAIRS[i].num;
1624 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%A", &time);
1625 EXPECT_STREQ(buffer, WEEKDAY_PAIRS[i].str.data());
1626 EXPECT_EQ(written, WEEKDAY_PAIRS[i].str.size());
1627 }
1628
1629 // check invalid weekdays
1630 time.tm_wday = -1;
1631 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%A", &time);
1632 EXPECT_STREQ_LEN(written, buffer, "?");
1633
1634 time.tm_wday = LIBC_NAMESPACE::time_constants::SATURDAY + 1;
1635 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%A", &time);
1636 EXPECT_STREQ_LEN(written, buffer, "?");
1637
1638 // padding is technically undefined for this conversion, but we support it, so
1639 // we need to test it.
1640 time.tm_wday = LIBC_NAMESPACE::time_constants::THURSDAY;
1641 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1A", &time);
1642 EXPECT_STREQ_LEN(written, buffer, "Thursday");
1643
1644 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%3A", &time);
1645 EXPECT_STREQ_LEN(written, buffer, "Thursday");
1646
1647 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10A", &time);
1648 EXPECT_STREQ_LEN(written, buffer, " Thursday");
1649
1650 time.tm_wday = -1;
1651 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1A", &time);
1652 EXPECT_STREQ_LEN(written, buffer, "?");
1653
1654 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%3A", &time);
1655 EXPECT_STREQ_LEN(written, buffer, " ?");
1656
1657 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10A", &time);
1658 EXPECT_STREQ_LEN(written, buffer, " ?");
1659}
1660
1661TEST(LlvmLibcStrftimeTest, ShortMonthName) {
1662 // this tests %b, which reads: [tm_mon]
1663 struct tm time;
1664 char buffer[100];
1665 size_t written = 0;
1666
1667 constexpr LIBC_NAMESPACE::cpp::array<
1668 num_str_pair, LIBC_NAMESPACE::time_constants::MONTHS_PER_YEAR>
1669 MONTH_PAIRS = {{
1670 {LIBC_NAMESPACE::time_constants::JANUARY, "Jan"},
1671 {LIBC_NAMESPACE::time_constants::FEBRUARY, "Feb"},
1672 {LIBC_NAMESPACE::time_constants::MARCH, "Mar"},
1673 {LIBC_NAMESPACE::time_constants::APRIL, "Apr"},
1674 {LIBC_NAMESPACE::time_constants::MAY, "May"},
1675 {LIBC_NAMESPACE::time_constants::JUNE, "Jun"},
1676 {LIBC_NAMESPACE::time_constants::JULY, "Jul"},
1677 {LIBC_NAMESPACE::time_constants::AUGUST, "Aug"},
1678 {LIBC_NAMESPACE::time_constants::SEPTEMBER, "Sep"},
1679 {LIBC_NAMESPACE::time_constants::OCTOBER, "Oct"},
1680 {LIBC_NAMESPACE::time_constants::NOVEMBER, "Nov"},
1681 {LIBC_NAMESPACE::time_constants::DECEMBER, "Dec"},
1682 }};
1683
1684 for (size_t i = 0; i < MONTH_PAIRS.size(); ++i) {
1685 time.tm_mon = MONTH_PAIRS[i].num;
1686 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%b", &time);
1687 EXPECT_STREQ(buffer, MONTH_PAIRS[i].str.data());
1688 EXPECT_EQ(written, MONTH_PAIRS[i].str.size());
1689 }
1690
1691 // check invalid weekdays
1692 time.tm_mon = -1;
1693 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%b", &time);
1694 EXPECT_STREQ_LEN(written, buffer, "?");
1695
1696 time.tm_mon = LIBC_NAMESPACE::time_constants::DECEMBER + 1;
1697 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%b", &time);
1698 EXPECT_STREQ_LEN(written, buffer, "?");
1699
1700 // Also test %h, which is identical to %b
1701 time.tm_mon = LIBC_NAMESPACE::time_constants::OCTOBER;
1702 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%h", &time);
1703 EXPECT_STREQ_LEN(written, buffer, "Oct");
1704
1705 // padding is technically undefined for this conversion, but we support it, so
1706 // we need to test it.
1707 time.tm_mon = LIBC_NAMESPACE::time_constants::OCTOBER;
1708 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1b", &time);
1709 EXPECT_STREQ_LEN(written, buffer, "Oct");
1710
1711 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%3b", &time);
1712 EXPECT_STREQ_LEN(written, buffer, "Oct");
1713
1714 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10b", &time);
1715 EXPECT_STREQ_LEN(written, buffer, " Oct");
1716
1717 time.tm_mon = -1;
1718 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1b", &time);
1719 EXPECT_STREQ_LEN(written, buffer, "?");
1720
1721 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%3b", &time);
1722 EXPECT_STREQ_LEN(written, buffer, " ?");
1723
1724 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10b", &time);
1725 EXPECT_STREQ_LEN(written, buffer, " ?");
1726}
1727
1728TEST(LlvmLibcStrftimeTest, FullMonthName) {
1729 // this tests %B, which reads: [tm_mon]
1730 struct tm time;
1731 char buffer[100];
1732 size_t written = 0;
1733
1734 constexpr LIBC_NAMESPACE::cpp::array<
1735 num_str_pair, LIBC_NAMESPACE::time_constants::MONTHS_PER_YEAR>
1736 MONTH_PAIRS = {{
1737 {LIBC_NAMESPACE::time_constants::JANUARY, "January"},
1738 {LIBC_NAMESPACE::time_constants::FEBRUARY, "February"},
1739 {LIBC_NAMESPACE::time_constants::MARCH, "March"},
1740 {LIBC_NAMESPACE::time_constants::APRIL, "April"},
1741 {LIBC_NAMESPACE::time_constants::MAY, "May"},
1742 {LIBC_NAMESPACE::time_constants::JUNE, "June"},
1743 {LIBC_NAMESPACE::time_constants::JULY, "July"},
1744 {LIBC_NAMESPACE::time_constants::AUGUST, "August"},
1745 {LIBC_NAMESPACE::time_constants::SEPTEMBER, "September"},
1746 {LIBC_NAMESPACE::time_constants::OCTOBER, "October"},
1747 {LIBC_NAMESPACE::time_constants::NOVEMBER, "November"},
1748 {LIBC_NAMESPACE::time_constants::DECEMBER, "December"},
1749 }};
1750
1751 for (size_t i = 0; i < MONTH_PAIRS.size(); ++i) {
1752 time.tm_mon = MONTH_PAIRS[i].num;
1753 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%B", &time);
1754 EXPECT_STREQ(buffer, MONTH_PAIRS[i].str.data());
1755 EXPECT_EQ(written, MONTH_PAIRS[i].str.size());
1756 }
1757
1758 // check invalid weekdays
1759 time.tm_mon = -1;
1760 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%B", &time);
1761 EXPECT_STREQ_LEN(written, buffer, "?");
1762
1763 time.tm_mon = LIBC_NAMESPACE::time_constants::DECEMBER + 1;
1764 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%B", &time);
1765 EXPECT_STREQ_LEN(written, buffer, "?");
1766
1767 // padding is technically undefined for this conversion, but we support it, so
1768 // we need to test it.
1769 time.tm_mon = LIBC_NAMESPACE::time_constants::OCTOBER;
1770 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1B", &time);
1771 EXPECT_STREQ_LEN(written, buffer, "October");
1772
1773 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%3B", &time);
1774 EXPECT_STREQ_LEN(written, buffer, "October");
1775
1776 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10B", &time);
1777 EXPECT_STREQ_LEN(written, buffer, " October");
1778
1779 time.tm_mon = -1;
1780 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1B", &time);
1781 EXPECT_STREQ_LEN(written, buffer, "?");
1782
1783 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%3B", &time);
1784 EXPECT_STREQ_LEN(written, buffer, " ?");
1785
1786 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10B", &time);
1787 EXPECT_STREQ_LEN(written, buffer, " ?");
1788}
1789
1790TEST(LlvmLibcStrftimeTest, AM_PM) {
1791 // this tests %p, which reads: [tm_hour]
1792 struct tm time;
1793 char buffer[100];
1794 size_t written = 0;
1795
1796 time.tm_hour = 0;
1797 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%p", &time);
1798 EXPECT_STREQ_LEN(written, buffer, "AM");
1799
1800 time.tm_hour = 6;
1801 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%p", &time);
1802 EXPECT_STREQ_LEN(written, buffer, "AM");
1803
1804 time.tm_hour = 12;
1805 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%p", &time);
1806 EXPECT_STREQ_LEN(written, buffer, "PM");
1807
1808 time.tm_hour = 18;
1809 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%p", &time);
1810 EXPECT_STREQ_LEN(written, buffer, "PM");
1811
1812 // padding is technically undefined for this conversion, but we support it, so
1813 // we need to test it.
1814 time.tm_hour = 6;
1815 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1p", &time);
1816 EXPECT_STREQ_LEN(written, buffer, "AM");
1817
1818 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%2p", &time);
1819 EXPECT_STREQ_LEN(written, buffer, "AM");
1820
1821 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10p", &time);
1822 EXPECT_STREQ_LEN(written, buffer, " AM");
1823
1824 time.tm_hour = 18;
1825 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1p", &time);
1826 EXPECT_STREQ_LEN(written, buffer, "PM");
1827
1828 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%2p", &time);
1829 EXPECT_STREQ_LEN(written, buffer, "PM");
1830
1831 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%10p", &time);
1832 EXPECT_STREQ_LEN(written, buffer, " PM");
1833}
1834
1835TEST(LlvmLibcStrftimeTest, DateFormatUS) {
1836 // this tests %D, which reads: [tm_mon, tm_mday, tm_year]
1837 // This is equivalent to "%m/%d/%y"
1838 struct tm time;
1839 char buffer[100];
1840 size_t written = 0;
1841
1842 // each of %m, %d, and %y have their own tests, so this test won't cover all
1843 // values of those. Instead it will do basic tests and focus on the specific
1844 // padding behavior.
1845
1846 time.tm_mon = 0; // 0 indexed, so 0 is january
1847 time.tm_mday = 2; // 1 indexed, so 2 is the 2nd
1848 time.tm_year = get_adjusted_year(1903);
1849 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%D", &time);
1850 EXPECT_STREQ_LEN(written, buffer, "01/02/03");
1851
1852 time.tm_mon = 11;
1853 time.tm_mday = 31;
1854 time.tm_year = get_adjusted_year(1999);
1855 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%D", &time);
1856 EXPECT_STREQ_LEN(written, buffer, "12/31/99");
1857
1858 // The day LLVM-libc started
1859 time.tm_mon = 8;
1860 time.tm_mday = 16;
1861 time.tm_year = get_adjusted_year(2019);
1862 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%D", &time);
1863 EXPECT_STREQ_LEN(written, buffer, "09/16/19");
1864
1865 // %x is equivalent to %D in default locale
1866 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%x", &time);
1867 EXPECT_STREQ_LEN(written, buffer, "09/16/19");
1868
1869 // padding is technically undefined for this conversion, but we support it, so
1870 // we need to test it.
1871 // Padding is handled in the same way as POSIX describes for %F
1872 time.tm_mon = 1;
1873 time.tm_mday = 5;
1874 time.tm_year = get_adjusted_year(2025);
1875 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01D", &time);
1876 EXPECT_STREQ_LEN(written, buffer, "2/05/25");
1877
1878 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%07D", &time);
1879 EXPECT_STREQ_LEN(written, buffer, "2/05/25");
1880
1881 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010D", &time);
1882 EXPECT_STREQ_LEN(written, buffer, "0002/05/25");
1883
1884 time.tm_mon = 9;
1885 time.tm_mday = 2;
1886 time.tm_year = get_adjusted_year(2000);
1887 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01D", &time);
1888 EXPECT_STREQ_LEN(written, buffer, "10/02/00");
1889
1890 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%07D", &time);
1891 EXPECT_STREQ_LEN(written, buffer, "10/02/00");
1892
1893 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010D", &time);
1894 EXPECT_STREQ_LEN(written, buffer, "0010/02/00");
1895}
1896
1897TEST(LlvmLibcStrftimeTest, DateFormatISO) {
1898 // this tests %F, which reads: [tm_year, tm_mon, tm_mday]
1899 // This is equivalent to "%Y-%m-%d"
1900 struct tm time;
1901 char buffer[100];
1902 size_t written = 0;
1903
1904 // each of %Y, %m, and %d have their own tests, so this test won't cover all
1905 // values of those. Instead it will do basic tests and focus on the specific
1906 // padding behavior.
1907
1908 time.tm_year = get_adjusted_year(1901);
1909 time.tm_mon = 1; // 0 indexed, so 1 is february
1910 time.tm_mday = 3; // 1 indexed, so 2 is the 2nd
1911 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%F", &time);
1912 EXPECT_STREQ_LEN(written, buffer, "1901-02-03");
1913
1914 time.tm_year = get_adjusted_year(1999);
1915 time.tm_mon = 11;
1916 time.tm_mday = 31;
1917 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%F", &time);
1918 EXPECT_STREQ_LEN(written, buffer, "1999-12-31");
1919
1920 time.tm_year = get_adjusted_year(2019);
1921 time.tm_mon = 8;
1922 time.tm_mday = 16;
1923 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%F", &time);
1924 EXPECT_STREQ_LEN(written, buffer, "2019-09-16");
1925
1926 time.tm_year = get_adjusted_year(123);
1927 time.tm_mon = 3;
1928 time.tm_mday = 5;
1929 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%F", &time);
1930 EXPECT_STREQ_LEN(written, buffer, "0123-04-05");
1931
1932 time.tm_year = get_adjusted_year(67);
1933 time.tm_mon = 7;
1934 time.tm_mday = 9;
1935 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%F", &time);
1936 EXPECT_STREQ_LEN(written, buffer, "0067-08-09");
1937
1938 time.tm_year = get_adjusted_year(2);
1939 time.tm_mon = 1;
1940 time.tm_mday = 14;
1941 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%F", &time);
1942 EXPECT_STREQ_LEN(written, buffer, "0002-02-14");
1943
1944 time.tm_year = get_adjusted_year(-543);
1945 time.tm_mon = 1;
1946 time.tm_mday = 1;
1947 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%F", &time);
1948 EXPECT_STREQ_LEN(written, buffer, "-543-02-01");
1949
1950 // padding tests
1951 time.tm_year = get_adjusted_year(2025);
1952 time.tm_mon = 1;
1953 time.tm_mday = 5;
1954 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01F", &time);
1955 EXPECT_STREQ_LEN(written, buffer, "2025-02-05");
1956
1957 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010F", &time);
1958 EXPECT_STREQ_LEN(written, buffer, "2025-02-05");
1959
1960 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%012F", &time);
1961 EXPECT_STREQ_LEN(written, buffer, "002025-02-05");
1962
1963 time.tm_year = get_adjusted_year(12345);
1964 time.tm_mon = 11;
1965 time.tm_mday = 25;
1966 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01F", &time);
1967 EXPECT_STREQ_LEN(written, buffer, "12345-12-25");
1968
1969 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010F", &time);
1970 EXPECT_STREQ_LEN(written, buffer, "12345-12-25");
1971
1972 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%012F", &time);
1973 EXPECT_STREQ_LEN(written, buffer, "012345-12-25");
1974
1975 time.tm_year = get_adjusted_year(476);
1976 time.tm_mon = 8;
1977 time.tm_mday = 4;
1978 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01F", &time);
1979 EXPECT_STREQ_LEN(written, buffer, "476-09-04");
1980
1981 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010F", &time);
1982 EXPECT_STREQ_LEN(written, buffer, "0476-09-04");
1983
1984 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%012F", &time);
1985 EXPECT_STREQ_LEN(written, buffer, "000476-09-04");
1986
1987 time.tm_year = get_adjusted_year(-100);
1988 time.tm_mon = 9;
1989 time.tm_mday = 31;
1990 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01F", &time);
1991 EXPECT_STREQ_LEN(written, buffer, "-100-10-31");
1992
1993 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010F", &time);
1994 EXPECT_STREQ_LEN(written, buffer, "-100-10-31");
1995
1996 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%012F", &time);
1997 EXPECT_STREQ_LEN(written, buffer, "-00100-10-31");
1998
1999 // '+' flag tests
2000 time.tm_year = get_adjusted_year(2025);
2001 time.tm_mon = 1;
2002 time.tm_mday = 5;
2003 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1F", &time);
2004 EXPECT_STREQ_LEN(written, buffer, "2025-02-05");
2005
2006 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10F", &time);
2007 EXPECT_STREQ_LEN(written, buffer, "2025-02-05");
2008
2009 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+12F", &time);
2010 EXPECT_STREQ_LEN(written, buffer, "+02025-02-05");
2011
2012 time.tm_year = get_adjusted_year(12345);
2013 time.tm_mon = 11;
2014 time.tm_mday = 25;
2015 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1F", &time);
2016 EXPECT_STREQ_LEN(written, buffer, "+12345-12-25");
2017
2018 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10F", &time);
2019 EXPECT_STREQ_LEN(written, buffer, "+12345-12-25");
2020
2021 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+12F", &time);
2022 EXPECT_STREQ_LEN(written, buffer, "+12345-12-25");
2023
2024 time.tm_year = get_adjusted_year(476);
2025 time.tm_mon = 8;
2026 time.tm_mday = 4;
2027 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1F", &time);
2028 EXPECT_STREQ_LEN(written, buffer, "476-09-04");
2029
2030 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10F", &time);
2031 EXPECT_STREQ_LEN(written, buffer, "0476-09-04");
2032
2033 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+12F", &time);
2034 EXPECT_STREQ_LEN(written, buffer, "+00476-09-04");
2035
2036 time.tm_year = get_adjusted_year(-100);
2037 time.tm_mon = 9;
2038 time.tm_mday = 31;
2039 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+1F", &time);
2040 EXPECT_STREQ_LEN(written, buffer, "-100-10-31");
2041
2042 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+10F", &time);
2043 EXPECT_STREQ_LEN(written, buffer, "-100-10-31");
2044
2045 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%+12F", &time);
2046 EXPECT_STREQ_LEN(written, buffer, "-00100-10-31");
2047}
2048
2049TEST(LlvmLibcStrftimeTest, TimeFormatAMPM) {
2050 // this tests %r, which reads: [tm_hour, tm_min, tm_sec]
2051 // This is equivalent to "%I:%M:%S %p"
2052 struct tm time;
2053 char buffer[100];
2054 size_t written = 0;
2055
2056 // each of %I, %M, %S, and %p have their own tests, so this test won't cover
2057 // all values of those. Instead it will do basic tests and focus on the
2058 // specific padding behavior.
2059
2060 time.tm_hour = 0;
2061 time.tm_min = 0;
2062 time.tm_sec = 0;
2063 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%r", &time);
2064 EXPECT_STREQ_LEN(written, buffer, "12:00:00 AM");
2065
2066 time.tm_hour = 1;
2067 time.tm_min = 23;
2068 time.tm_sec = 45;
2069 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%r", &time);
2070 EXPECT_STREQ_LEN(written, buffer, "01:23:45 AM");
2071
2072 time.tm_hour = 18;
2073 time.tm_min = 6;
2074 time.tm_sec = 2;
2075 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%r", &time);
2076 EXPECT_STREQ_LEN(written, buffer, "06:06:02 PM");
2077
2078 // padding is technically undefined for this conversion, but we support it, so
2079 // we need to test it.
2080 // Padding is handled in the same way as POSIX describes for %F
2081 time.tm_hour = 10;
2082 time.tm_min = 9;
2083 time.tm_sec = 59;
2084 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01r", &time);
2085 EXPECT_STREQ_LEN(written, buffer, "10:09:59 AM");
2086
2087 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%011r", &time);
2088 EXPECT_STREQ_LEN(written, buffer, "10:09:59 AM");
2089
2090 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%013r", &time);
2091 EXPECT_STREQ_LEN(written, buffer, "0010:09:59 AM");
2092
2093 time.tm_hour = 16;
2094 time.tm_min = 56;
2095 time.tm_sec = 9;
2096 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01r", &time);
2097 EXPECT_STREQ_LEN(written, buffer, "4:56:09 PM");
2098
2099 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%011r", &time);
2100 EXPECT_STREQ_LEN(written, buffer, "04:56:09 PM");
2101
2102 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%013r", &time);
2103 EXPECT_STREQ_LEN(written, buffer, "0004:56:09 PM");
2104}
2105
2106TEST(LlvmLibcStrftimeTest, TimeFormatMinute) {
2107 // this tests %R, which reads: [tm_hour, tm_min]
2108 // This is equivalent to "%H:%M"
2109 struct tm time;
2110 char buffer[100];
2111 size_t written = 0;
2112
2113 // each of %H and %M have their own tests, so this test won't cover
2114 // all values of those. Instead it will do basic tests and focus on the
2115 // specific padding behavior.
2116
2117 time.tm_hour = 0;
2118 time.tm_min = 0;
2119 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%R", &time);
2120 EXPECT_STREQ_LEN(written, buffer, "00:00");
2121
2122 time.tm_hour = 1;
2123 time.tm_min = 23;
2124 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%R", &time);
2125 EXPECT_STREQ_LEN(written, buffer, "01:23");
2126
2127 time.tm_hour = 18;
2128 time.tm_min = 6;
2129 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%R", &time);
2130 EXPECT_STREQ_LEN(written, buffer, "18:06");
2131
2132 // padding is technically undefined for this conversion, but we support it, so
2133 // we need to test it.
2134 // Padding is handled in the same way as POSIX describes for %F
2135 time.tm_hour = 10;
2136 time.tm_min = 9;
2137 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01R", &time);
2138 EXPECT_STREQ_LEN(written, buffer, "10:09");
2139
2140 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05R", &time);
2141 EXPECT_STREQ_LEN(written, buffer, "10:09");
2142
2143 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%07R", &time);
2144 EXPECT_STREQ_LEN(written, buffer, "0010:09");
2145
2146 time.tm_hour = 4;
2147 time.tm_min = 56;
2148 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01R", &time);
2149 EXPECT_STREQ_LEN(written, buffer, "4:56");
2150
2151 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%05R", &time);
2152 EXPECT_STREQ_LEN(written, buffer, "04:56");
2153
2154 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%07R", &time);
2155 EXPECT_STREQ_LEN(written, buffer, "0004:56");
2156}
2157
2158TEST(LlvmLibcStrftimeTest, TimeFormatSecond) {
2159 // this tests %T, which reads: [tm_hour, tm_min, tm_sec]
2160 // This is equivalent to "%H:%M:%S"
2161 struct tm time;
2162 char buffer[100];
2163 size_t written = 0;
2164
2165 // each of %H, %M, and %S have their own tests, so this test won't cover
2166 // all values of those. Instead it will do basic tests and focus on the
2167 // specific padding behavior.
2168
2169 time.tm_hour = 0;
2170 time.tm_min = 0;
2171 time.tm_sec = 0;
2172 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%T", &time);
2173 EXPECT_STREQ_LEN(written, buffer, "00:00:00");
2174
2175 time.tm_hour = 1;
2176 time.tm_min = 23;
2177 time.tm_sec = 45;
2178 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%T", &time);
2179 EXPECT_STREQ_LEN(written, buffer, "01:23:45");
2180
2181 time.tm_hour = 18;
2182 time.tm_min = 6;
2183 time.tm_sec = 2;
2184 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%T", &time);
2185 EXPECT_STREQ_LEN(written, buffer, "18:06:02");
2186
2187 // %X is equivalent to %T in default locale
2188 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%X", &time);
2189 EXPECT_STREQ_LEN(written, buffer, "18:06:02");
2190
2191 // padding is technically undefined for this conversion, but we support it, so
2192 // we need to test it.
2193 // Padding is handled in the same way as POSIX describes for %F
2194 time.tm_hour = 10;
2195 time.tm_min = 9;
2196 time.tm_sec = 59;
2197 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01T", &time);
2198 EXPECT_STREQ_LEN(written, buffer, "10:09:59");
2199
2200 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%08T", &time);
2201 EXPECT_STREQ_LEN(written, buffer, "10:09:59");
2202
2203 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010T", &time);
2204 EXPECT_STREQ_LEN(written, buffer, "0010:09:59");
2205
2206 time.tm_hour = 4;
2207 time.tm_min = 56;
2208 time.tm_sec = 9;
2209 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%01T", &time);
2210 EXPECT_STREQ_LEN(written, buffer, "4:56:09");
2211
2212 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%08T", &time);
2213 EXPECT_STREQ_LEN(written, buffer, "04:56:09");
2214
2215 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%010T", &time);
2216 EXPECT_STREQ_LEN(written, buffer, "0004:56:09");
2217}
2218
2219TEST(LlvmLibcStrftimeTest, TimeFormatFullDateTime) {
2220 // this tests %c, which reads:
2221 // [tm_wday, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_year]
2222 // This is equivalent to "%a %b %e %T %Y"
2223 struct tm time;
2224 char buffer[100];
2225 size_t written = 0;
2226
2227 // each of the individual conversions have their own tests, so this test won't
2228 // cover all values of those. Instead it will do basic tests and focus on the
2229 // specific padding behavior.
2230
2231 time.tm_wday = 0;
2232 time.tm_mon = 0;
2233 time.tm_mday = 1;
2234 time.tm_hour = 0;
2235 time.tm_min = 0;
2236 time.tm_sec = 0;
2237 time.tm_year = get_adjusted_year(1900);
2238 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%c", &time);
2239 EXPECT_STREQ_LEN(written, buffer, "Sun Jan 1 00:00:00 1900");
2240
2241 time.tm_wday = 3;
2242 time.tm_mon = 5;
2243 time.tm_mday = 15;
2244 time.tm_hour = 14;
2245 time.tm_min = 13;
2246 time.tm_sec = 12;
2247 time.tm_year = get_adjusted_year(2011);
2248 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%c", &time);
2249 EXPECT_STREQ_LEN(written, buffer, "Wed Jun 15 14:13:12 2011");
2250
2251 // now, as of the writing of this test
2252 time.tm_wday = 4;
2253 time.tm_mon = 1;
2254 time.tm_mday = 6;
2255 time.tm_hour = 12;
2256 time.tm_min = 57;
2257 time.tm_sec = 50;
2258 time.tm_year = get_adjusted_year(2025);
2259 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%c", &time);
2260 EXPECT_STREQ_LEN(written, buffer, "Thu Feb 6 12:57:50 2025");
2261
2262 time.tm_wday = 5;
2263 time.tm_mon = 8;
2264 time.tm_mday = 4;
2265 time.tm_hour = 16;
2266 time.tm_min = 57;
2267 time.tm_sec = 18;
2268 time.tm_year = get_adjusted_year(476);
2269 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%c", &time);
2270 EXPECT_STREQ_LEN(written, buffer, "Fri Sep 4 16:57:18 0476");
2271
2272 // padding is technically undefined for this conversion, but we support it, so
2273 // we need to test it.
2274 // Padding is handled in the same way as POSIX describes for %F.
2275 // This includes assuming the trailing conversions are of a fixed width, which
2276 // isn't true for years. For simplicity, we format years (%Y) to be padded to
2277 // 4 digits when possible, which means padding will work as expected for years
2278 // -999 to 9999. If the current year is large enough to trigger this bug,
2279 // congrats on making it another ~8000 years!
2280 time.tm_wday = 5;
2281 time.tm_mon = 8;
2282 time.tm_mday = 4;
2283 time.tm_hour = 16;
2284 time.tm_min = 57;
2285 time.tm_sec = 18;
2286 time.tm_year = get_adjusted_year(476);
2287 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1c", &time);
2288 EXPECT_STREQ_LEN(written, buffer, "Fri Sep 4 16:57:18 0476");
2289
2290 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%24c", &time);
2291 EXPECT_STREQ_LEN(written, buffer, "Fri Sep 4 16:57:18 0476");
2292
2293 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%26c", &time);
2294 EXPECT_STREQ_LEN(written, buffer, " Fri Sep 4 16:57:18 0476");
2295
2296 // '0' flag has no effect on the string part of the conversion, only the
2297 // numbers, and the only one of those that defaults to spaces is day of month.
2298 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%026c", &time);
2299 EXPECT_STREQ_LEN(written, buffer, " Fri Sep 04 16:57:18 0476");
2300
2301 time.tm_wday = 3;
2302 time.tm_mon = 5;
2303 time.tm_mday = 15;
2304 time.tm_hour = 14;
2305 time.tm_min = 13;
2306 time.tm_sec = 12;
2307 time.tm_year = get_adjusted_year(2011);
2308 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%1c", &time);
2309 EXPECT_STREQ_LEN(written, buffer, "Wed Jun 15 14:13:12 2011");
2310
2311 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%24c", &time);
2312 EXPECT_STREQ_LEN(written, buffer, "Wed Jun 15 14:13:12 2011");
2313
2314 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%26c", &time);
2315 EXPECT_STREQ_LEN(written, buffer, " Wed Jun 15 14:13:12 2011");
2316
2317 written = LIBC_NAMESPACE::strftime(buffer, sizeof(buffer), "%026c", &time);
2318 EXPECT_STREQ_LEN(written, buffer, " Wed Jun 15 14:13:12 2011");
2319}
2320
2321// TODO: implement %z and %Z when timezones are implemented.
2322// TEST(LlvmLibcStrftimeTest, TimezoneOffset) {
2323// // this tests %z, which reads: [tm_isdst, tm_zone]
2324// struct tm time;
2325// char buffer[100];
2326// size_t written = 0;
2327// SimplePaddedNum spn;
2328// }
2329

source code of libc/test/src/time/strftime_test.cpp