1 //===-- Collection of utils for mktime and friends --------------*- C++ -*-===//
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 #ifndef LLVM_LIBC_SRC_TIME_TIME_UTILS_H
10 #define LLVM_LIBC_SRC_TIME_TIME_UTILS_H
11 
12 #include <stddef.h> // For size_t.
13 
14 #include "include/errno.h"
15 
16 #include "src/errno/llvmlibc_errno.h"
17 #include "src/time/mktime.h"
18 
19 #include <stdint.h>
20 
21 namespace __llvm_libc {
22 namespace time_utils {
23 
24 struct TimeConstants {
25   static constexpr int SecondsPerMin = 60;
26   static constexpr int SecondsPerHour = 3600;
27   static constexpr int SecondsPerDay = 86400;
28   static constexpr int DaysPerWeek = 7;
29   static constexpr int MonthsPerYear = 12;
30   static constexpr int DaysPerNonLeapYear = 365;
31   static constexpr int DaysPerLeapYear = 366;
32   static constexpr int TimeYearBase = 1900;
33   static constexpr int EpochYear = 1970;
34   static constexpr int EpochWeekDay = 4;
35   static constexpr int NumberOfSecondsInLeapYear =
36       (DaysPerNonLeapYear + 1) * SecondsPerDay;
37 
38   // For asctime the behavior is undefined if struct tm's tm_wday or tm_mon are
39   // not within the normal ranges as defined in <time.h>, or if struct tm's
40   // tm_year exceeds {INT_MAX}-1990, or if the below asctime_internal algorithm
41   // would attempt to generate more than 26 bytes of output (including the
42   // terminating null).
43   static constexpr int AsctimeBufferSize = 256;
44   static constexpr int AsctimeMaxBytes = 26;
45 
46   /* 2000-03-01 (mod 400 year, immediately after feb29 */
47   static constexpr int64_t SecondsUntil2000MarchFirst =
48       (946684800LL + SecondsPerDay * (31 + 29));
49   static constexpr int WeekDayOf2000MarchFirst = 3;
50 
51   static constexpr int DaysPer400Years =
52       (DaysPerNonLeapYear * 400 + (400 / 4) - 3);
53   static constexpr int DaysPer100Years =
54       (DaysPerNonLeapYear * 100 + (100 / 4) - 1);
55   static constexpr int DaysPer4Years = (DaysPerNonLeapYear * 4 + 1);
56 
57   // The latest time that can be represented in this form is 03:14:07 UTC on
58   // Tuesday, 19 January 2038 (corresponding to 2,147,483,647 seconds since the
59   // start of the epoch). This means that systems using a 32-bit time_t type are
60   // susceptible to the Year 2038 problem.
61   static constexpr int EndOf32BitEpochYear = 2038;
62 
63   static constexpr time_t OutOfRangeReturnValue = -1;
64 };
65 
66 // Update the "tm" structure's year, month, etc. members from seconds.
67 // "total_seconds" is the number of seconds since January 1st, 1970.
68 extern int64_t UpdateFromSeconds(int64_t total_seconds, struct tm *tm);
69 
70 // POSIX.1-2017 requires this.
OutOfRange()71 static inline time_t OutOfRange() {
72   llvmlibc_errno = EOVERFLOW;
73   return static_cast<time_t>(-1);
74 }
75 
InvalidValue()76 static inline void InvalidValue() { llvmlibc_errno = EINVAL; }
77 
asctime(const struct tm * timeptr,char * buffer,size_t bufferLength)78 static inline char *asctime(const struct tm *timeptr, char *buffer,
79                             size_t bufferLength) {
80   if (timeptr == nullptr || buffer == nullptr) {
81     InvalidValue();
82     return nullptr;
83   }
84   if (timeptr->tm_wday < 0 ||
85       timeptr->tm_wday > (TimeConstants::DaysPerWeek - 1)) {
86     InvalidValue();
87     return nullptr;
88   }
89   if (timeptr->tm_mon < 0 ||
90       timeptr->tm_mon > (TimeConstants::MonthsPerYear - 1)) {
91     InvalidValue();
92     return nullptr;
93   }
94 
95   // TODO(rtenneti): i18n the following strings.
96   static const char *WeekDaysName[TimeConstants::DaysPerWeek] = {
97       "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
98 
99   static const char *MonthsName[TimeConstants::MonthsPerYear] = {
100       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
101       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
102   int written_size = __builtin_snprintf(
103       buffer, bufferLength, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
104       WeekDaysName[timeptr->tm_wday], MonthsName[timeptr->tm_mon],
105       timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec,
106       TimeConstants::TimeYearBase + timeptr->tm_year);
107   if (written_size < 0)
108     return nullptr;
109   if (static_cast<size_t>(written_size) >= bufferLength) {
110     OutOfRange();
111     return nullptr;
112   }
113   return buffer;
114 }
115 
gmtime_internal(const time_t * timer,struct tm * result)116 static inline struct tm *gmtime_internal(const time_t *timer,
117                                          struct tm *result) {
118   int64_t seconds = *timer;
119   // Update the tm structure's year, month, day, etc. from seconds.
120   if (UpdateFromSeconds(seconds, result) < 0) {
121     OutOfRange();
122     return nullptr;
123   }
124 
125   return result;
126 }
127 
128 } // namespace time_utils
129 } // namespace __llvm_libc
130 
131 #endif // LLVM_LIBC_SRC_TIME_TIME_UTILS_H
132