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