xref: /llvm-project/libc/test/src/time/mktime_test.cpp (revision b98c1906d6a5483ef709afaa1c05f36fb783d73d)
1 //===-- Unittests for mktime ----------------------------------------------===//
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 "src/time/mktime.h"
10 #include "src/time/time_utils.h"
11 #include "test/ErrnoSetterMatcher.h"
12 #include "test/UnitTest/Test.h"
13 #include "test/src/time/TmHelper.h"
14 #include "test/src/time/TmMatcher.h"
15 
16 #include <limits.h>
17 
18 using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
19 using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
20 using __llvm_libc::time_utils::Month;
21 
22 static inline constexpr int tm_year(int year) {
23   return year - TimeConstants::TIME_YEAR_BASE;
24 }
25 
26 TEST(LlvmLibcMkTime, FailureSetsErrno) {
27   struct tm tm_data {
28     .tm_sec = INT_MAX, .tm_min = INT_MAX, .tm_hour = INT_MAX,
29     .tm_mday = INT_MAX, .tm_mon = INT_MAX - 1, .tm_year = tm_year(INT_MAX),
30     .tm_wday = 0, .tm_yday = 0
31   };
32   EXPECT_THAT(__llvm_libc::mktime(&tm_data), Fails(EOVERFLOW));
33 }
34 
35 TEST(LlvmLibcMkTime, InvalidSeconds) {
36   {
37     // -1 second from 1970-01-01 00:00:00 returns 1969-12-31 23:59:59.
38     struct tm tm_data {
39       .tm_sec = -1, .tm_min = 0, .tm_hour = 0, .tm_mday = 1,
40       .tm_mon = Month::JANUARY, .tm_year = tm_year(1970), .tm_wday = 0,
41       .tm_yday = 0
42     };
43     EXPECT_THAT(__llvm_libc::mktime(&tm_data), Succeeds(-1));
44     EXPECT_TM_EQ((tm{.tm_sec = 59,
45                      .tm_min = 59,
46                      .tm_hour = 23,
47                      .tm_mday = 31,
48                      .tm_mon = Month::DECEMBER,
49                      .tm_year = tm_year(1969),
50                      .tm_wday = 3,
51                      .tm_yday = 364}),
52                  tm_data);
53   }
54 
55   {
56     // 60 seconds from 1970-01-01 00:00:00 returns 1970-01-01 00:01:00.
57     struct tm tm_data {
58       .tm_sec = 60, .tm_min = 0, .tm_hour = 0, .tm_mday = 1,
59       .tm_mon = Month::JANUARY, .tm_year = tm_year(1970), .tm_wday = 0,
60       .tm_yday = 0
61     };
62     EXPECT_THAT(__llvm_libc::mktime(&tm_data), Succeeds(60));
63     EXPECT_TM_EQ((tm{.tm_sec = 0,
64                      .tm_min = 1,
65                      .tm_hour = 0,
66                      .tm_mday = 1,
67                      .tm_mon = Month::JANUARY,
68                      .tm_year = tm_year(1970),
69                      .tm_wday = 4,
70                      .tm_yday = 0}),
71                  tm_data);
72   }
73 }
74 
75 TEST(LlvmLibcMkTime, InvalidMinutes) {
76   {
77     // -1 minute from 1970-01-01 00:00:00 returns 1969-12-31 23:59:00.
78     struct tm tm_data {
79       .tm_sec = 0, .tm_min = -1, .tm_hour = 0, .tm_mday = 1,
80       .tm_mon = Month::JANUARY, .tm_year = tm_year(1970), .tm_wday = 0,
81       .tm_yday = 0
82     };
83     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
84                 Succeeds(-TimeConstants::SECONDS_PER_MIN));
85     EXPECT_TM_EQ((tm{.tm_sec = 0,
86                      .tm_min = 59,
87                      .tm_hour = 23,
88                      .tm_mday = 31,
89                      .tm_mon = Month::DECEMBER,
90                      .tm_year = tm_year(1969),
91                      .tm_wday = 3,
92                      .tm_yday = 0}),
93                  tm_data);
94   }
95 
96   {
97     // 60 minutes from 1970-01-01 00:00:00 returns 1970-01-01 01:00:00.
98     struct tm tm_data {
99       .tm_sec = 0, .tm_min = 60, .tm_hour = 0, .tm_mday = 1,
100       .tm_mon = Month::JANUARY, .tm_year = tm_year(1970), .tm_wday = 0,
101       .tm_yday = 0
102     };
103     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
104                 Succeeds(60 * TimeConstants::SECONDS_PER_MIN));
105     EXPECT_TM_EQ((tm{.tm_sec = 0,
106                      .tm_min = 0,
107                      .tm_hour = 1,
108                      .tm_mday = 1,
109                      .tm_mon = Month::JANUARY,
110                      .tm_year = tm_year(1970),
111                      .tm_wday = 4,
112                      .tm_yday = 0}),
113                  tm_data);
114   }
115 }
116 
117 TEST(LlvmLibcMkTime, InvalidHours) {
118   {
119     // -1 hour from 1970-01-01 00:00:00 returns 1969-12-31 23:00:00.
120     struct tm tm_data {
121       .tm_sec = 0, .tm_min = 0, .tm_hour = -1, .tm_mday = 1,
122       .tm_mon = Month::JANUARY, .tm_year = tm_year(1970), .tm_wday = 0,
123       .tm_yday = 0
124     };
125     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
126                 Succeeds(-TimeConstants::SECONDS_PER_HOUR));
127     EXPECT_TM_EQ((tm{.tm_sec = 0,
128                      .tm_min = 0,
129                      .tm_hour = 23,
130                      .tm_mday = 31,
131                      .tm_mon = Month::DECEMBER,
132                      .tm_year = tm_year(1969),
133                      .tm_wday = 3,
134                      .tm_yday = 0}),
135                  tm_data);
136   }
137 
138   {
139     // 24 hours from 1970-01-01 00:00:00 returns 1970-01-02 00:00:00.
140     struct tm tm_data {
141       .tm_sec = 0, .tm_min = 0, .tm_hour = 24, .tm_mday = 1,
142       .tm_mon = Month::JANUARY, .tm_year = tm_year(1970), .tm_wday = 0,
143       .tm_yday = 0
144     };
145     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
146                 Succeeds(24 * TimeConstants::SECONDS_PER_HOUR));
147     EXPECT_TM_EQ((tm{.tm_sec = 0,
148                      .tm_min = 0,
149                      .tm_hour = 0,
150                      .tm_mday = 2,
151                      .tm_mon = Month::JANUARY,
152                      .tm_year = tm_year(1970),
153                      .tm_wday = 5,
154                      .tm_yday = 0}),
155                  tm_data);
156   }
157 }
158 
159 TEST(LlvmLibcMkTime, InvalidYear) {
160   // -1 year from 1970-01-01 00:00:00 returns 1969-01-01 00:00:00.
161   struct tm tm_data {
162     .tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = 1,
163     .tm_mon = Month::JANUARY, .tm_year = tm_year(1969), .tm_wday = 0,
164     .tm_yday = 0
165   };
166   EXPECT_THAT(__llvm_libc::mktime(&tm_data),
167               Succeeds(-TimeConstants::DAYS_PER_NON_LEAP_YEAR *
168                        TimeConstants::SECONDS_PER_DAY));
169   EXPECT_TM_EQ((tm{.tm_sec = 0,
170                    .tm_min = 0,
171                    .tm_hour = 0,
172                    .tm_mday = 1,
173                    .tm_mon = Month::JANUARY,
174                    .tm_year = tm_year(1969),
175                    .tm_wday = 3,
176                    .tm_yday = 0}),
177                tm_data);
178 }
179 
180 TEST(LlvmLibcMkTime, InvalidEndOf32BitEpochYear) {
181   if (sizeof(size_t) != 4)
182     return;
183   {
184     // 2038-01-19 03:14:08 tests overflow of the second in 2038.
185     struct tm tm_data {
186       .tm_sec = 8, .tm_min = 14, .tm_hour = 3, .tm_mday = 19,
187       .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
188       .tm_yday = 0
189     };
190     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
191                 Succeeds(TimeConstants::OUT_OF_RANGE_RETURN_VALUE));
192   }
193 
194   {
195     // 2038-01-19 03:15:07 tests overflow of the minute in 2038.
196     struct tm tm_data {
197       .tm_sec = 7, .tm_min = 15, .tm_hour = 3, .tm_mday = 19,
198       .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
199       .tm_yday = 0
200     };
201     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
202                 Succeeds(TimeConstants::OUT_OF_RANGE_RETURN_VALUE));
203   }
204 
205   {
206     // 2038-01-19 04:14:07 tests overflow of the hour in 2038.
207     struct tm tm_data {
208       .tm_sec = 7, .tm_min = 14, .tm_hour = 4, .tm_mday = 19,
209       .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
210       .tm_yday = 0
211     };
212     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
213                 Succeeds(TimeConstants::OUT_OF_RANGE_RETURN_VALUE));
214   }
215 
216   {
217     // 2038-01-20 03:14:07 tests overflow of the day in 2038.
218     struct tm tm_data {
219       .tm_sec = 7, .tm_min = 14, .tm_hour = 3, .tm_mday = 20,
220       .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
221       .tm_yday = 0
222     };
223     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
224                 Succeeds(TimeConstants::OUT_OF_RANGE_RETURN_VALUE));
225   }
226 
227   {
228     // 2038-02-19 03:14:07 tests overflow of the month in 2038.
229     struct tm tm_data {
230       .tm_sec = 7, .tm_min = 14, .tm_hour = 3, .tm_mday = 19,
231       .tm_mon = Month::FEBRUARY, .tm_year = tm_year(2038), .tm_wday = 0,
232       .tm_yday = 0
233     };
234     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
235                 Succeeds(TimeConstants::OUT_OF_RANGE_RETURN_VALUE));
236   }
237 
238   {
239     // 2039-01-19 03:14:07 tests overflow of the year.
240     struct tm tm_data {
241       .tm_sec = 7, .tm_min = 14, .tm_hour = 3, .tm_mday = 19,
242       .tm_mon = Month::JANUARY, .tm_year = tm_year(2039), .tm_wday = 0,
243       .tm_yday = 0
244     };
245     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
246                 Succeeds(TimeConstants::OUT_OF_RANGE_RETURN_VALUE));
247   }
248 }
249 
250 TEST(LlvmLibcMkTime, InvalidMonths) {
251   {
252     // -1 month from 1970-01-01 00:00:00 returns 1969-12-01 00:00:00.
253     struct tm tm_data {
254       .tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = 0, .tm_mon = -1,
255       .tm_year = tm_year(1970), .tm_wday = 0, .tm_yday = 0
256     };
257     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
258                 Succeeds(-32 * TimeConstants::SECONDS_PER_DAY));
259     EXPECT_TM_EQ((tm{.tm_sec = 0,
260                      .tm_min = 0,
261                      .tm_hour = 0,
262                      .tm_mday = 1,
263                      .tm_mon = Month::DECEMBER,
264                      .tm_year = tm_year(1969),
265                      .tm_wday = 1,
266                      .tm_yday = 0}),
267                  tm_data);
268   }
269 
270   {
271     // 1970-13-01 00:00:00 returns 1971-01-01 00:00:00.
272     struct tm tm_data {
273       .tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = 1, .tm_mon = 12,
274       .tm_year = tm_year(1970), .tm_wday = 0, .tm_yday = 0
275     };
276     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
277                 Succeeds(TimeConstants::DAYS_PER_NON_LEAP_YEAR *
278                          TimeConstants::SECONDS_PER_DAY));
279     EXPECT_TM_EQ((tm{.tm_sec = 0,
280                      .tm_min = 0,
281                      .tm_hour = 0,
282                      .tm_mday = 1,
283                      .tm_mon = Month::JANUARY,
284                      .tm_year = tm_year(1971),
285                      .tm_wday = 5,
286                      .tm_yday = 0}),
287                  tm_data);
288   }
289 }
290 
291 TEST(LlvmLibcMkTime, InvalidDays) {
292   {
293     // -1 day from 1970-01-01 00:00:00 returns 1969-12-31 00:00:00.
294     struct tm tm_data {
295       .tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = (1 - 1),
296       .tm_mon = Month::JANUARY, .tm_year = tm_year(1970), .tm_wday = 0,
297       .tm_yday = 0
298     };
299     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
300                 Succeeds(-1 * TimeConstants::SECONDS_PER_DAY));
301     EXPECT_TM_EQ((tm{.tm_sec = 0,
302                      .tm_min = 0,
303                      .tm_hour = 0,
304                      .tm_mday = 31,
305                      .tm_mon = Month::DECEMBER,
306                      .tm_year = tm_year(1969),
307                      .tm_wday = 3,
308                      .tm_yday = 0}),
309                  tm_data);
310   }
311 
312   {
313     // 1970-01-32 00:00:00 returns 1970-02-01 00:00:00.
314     struct tm tm_data {
315       .tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = 32,
316       .tm_mon = Month::JANUARY, .tm_year = tm_year(1970), .tm_wday = 0,
317       .tm_yday = 0
318     };
319     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
320                 Succeeds(31 * TimeConstants::SECONDS_PER_DAY));
321     EXPECT_TM_EQ((tm{.tm_sec = 0,
322                      .tm_min = 0,
323                      .tm_hour = 0,
324                      .tm_mday = 1,
325                      .tm_mon = Month::FEBRUARY,
326                      .tm_year = tm_year(1970),
327                      .tm_wday = 0,
328                      .tm_yday = 0}),
329                  tm_data);
330   }
331 
332   {
333     // 1970-02-29 00:00:00 returns 1970-03-01 00:00:00.
334     struct tm tm_data {
335       .tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = 29,
336       .tm_mon = Month::FEBRUARY, .tm_year = tm_year(1970), .tm_wday = 0,
337       .tm_yday = 0
338     };
339     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
340                 Succeeds(59 * TimeConstants::SECONDS_PER_DAY));
341     EXPECT_TM_EQ((tm{.tm_sec = 0,
342                      .tm_min = 0,
343                      .tm_hour = 0,
344                      .tm_mday = 1,
345                      .tm_mon = Month::MARCH,
346                      .tm_year = tm_year(1970),
347                      .tm_wday = 0,
348                      .tm_yday = 0}),
349                  tm_data);
350   }
351 
352   {
353     // 1972-02-30 00:00:00 returns 1972-03-01 00:00:00.
354     struct tm tm_data {
355       .tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = 30,
356       .tm_mon = Month::FEBRUARY, .tm_year = tm_year(1972), .tm_wday = 0,
357       .tm_yday = 0
358     };
359     EXPECT_THAT(__llvm_libc::mktime(&tm_data),
360                 Succeeds(((2 * TimeConstants::DAYS_PER_NON_LEAP_YEAR) + 60) *
361                          TimeConstants::SECONDS_PER_DAY));
362     EXPECT_TM_EQ((tm{.tm_sec = 0,
363                      .tm_min = 0,
364                      .tm_hour = 0,
365                      .tm_mday = 1,
366                      .tm_mon = Month::MARCH,
367                      .tm_year = tm_year(1972),
368                      .tm_wday = 3,
369                      .tm_yday = 0}),
370                  tm_data);
371   }
372 }
373 
374 TEST(LlvmLibcMkTime, EndOf32BitEpochYear) {
375   // Test for maximum value of a signed 32-bit integer.
376   // Test implementation can encode time for Tue 19 January 2038 03:14:07 UTC.
377   struct tm tm_data {
378     .tm_sec = 7, .tm_min = 14, .tm_hour = 3, .tm_mday = 19,
379     .tm_mon = Month::JANUARY, .tm_year = tm_year(2038), .tm_wday = 0,
380     .tm_yday = 0
381   };
382   EXPECT_THAT(__llvm_libc::mktime(&tm_data), Succeeds(0x7FFFFFFF));
383   EXPECT_TM_EQ((tm{.tm_sec = 7,
384                    .tm_min = 14,
385                    .tm_hour = 3,
386                    .tm_mday = 19,
387                    .tm_mon = Month::JANUARY,
388                    .tm_year = tm_year(2038),
389                    .tm_wday = 2,
390                    .tm_yday = 7}),
391                tm_data);
392 }
393 
394 TEST(LlvmLibcMkTime, Max64BitYear) {
395   if (sizeof(time_t) == 4)
396     return;
397   {
398     // Mon Jan 1 12:50:50 2170 (200 years from 1970),
399     struct tm tm_data {
400       .tm_sec = 50, .tm_min = 50, .tm_hour = 12, .tm_mday = 1,
401       .tm_mon = Month::JANUARY, .tm_year = tm_year(2170), .tm_wday = 0,
402       .tm_yday = 0
403     };
404     EXPECT_THAT(__llvm_libc::mktime(&tm_data), Succeeds(6311479850));
405     EXPECT_TM_EQ((tm{.tm_sec = 50,
406                      .tm_min = 50,
407                      .tm_hour = 12,
408                      .tm_mday = 1,
409                      .tm_mon = Month::JANUARY,
410                      .tm_year = tm_year(2170),
411                      .tm_wday = 1,
412                      .tm_yday = 50}),
413                  tm_data);
414   }
415 
416   {
417     // Test for Tue Jan 1 12:50:50 in 2,147,483,647th year.
418     struct tm tm_data {
419       .tm_sec = 50, .tm_min = 50, .tm_hour = 12, .tm_mday = 1,
420       .tm_mon = Month::JANUARY, .tm_year = tm_year(2147483647), .tm_wday = 0,
421       .tm_yday = 0
422     };
423     EXPECT_THAT(__llvm_libc::mktime(&tm_data), Succeeds(67767976202043050));
424     EXPECT_TM_EQ((tm{.tm_sec = 50,
425                      .tm_min = 50,
426                      .tm_hour = 12,
427                      .tm_mday = 1,
428                      .tm_mon = Month::JANUARY,
429                      .tm_year = tm_year(2147483647),
430                      .tm_wday = 2,
431                      .tm_yday = 50}),
432                  tm_data);
433   }
434 }
435