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 "src/__support/common.h" |
15 | #include "src/errno/libc_errno.h" |
16 | #include "src/time/mktime.h" |
17 | |
18 | #include <stdint.h> |
19 | |
20 | namespace LIBC_NAMESPACE { |
21 | namespace time_utils { |
22 | |
23 | enum Month : int { |
24 | JANUARY, |
25 | FEBRUARY, |
26 | MARCH, |
27 | APRIL, |
28 | MAY, |
29 | JUNE, |
30 | JULY, |
31 | AUGUST, |
32 | SEPTEMBER, |
33 | OCTOBER, |
34 | NOVEMBER, |
35 | DECEMBER |
36 | }; |
37 | |
38 | struct TimeConstants { |
39 | static constexpr int SECONDS_PER_MIN = 60; |
40 | static constexpr int MINUTES_PER_HOUR = 60; |
41 | static constexpr int HOURS_PER_DAY = 24; |
42 | static constexpr int DAYS_PER_WEEK = 7; |
43 | static constexpr int MONTHS_PER_YEAR = 12; |
44 | static constexpr int DAYS_PER_NON_LEAP_YEAR = 365; |
45 | static constexpr int DAYS_PER_LEAP_YEAR = 366; |
46 | |
47 | static constexpr int SECONDS_PER_HOUR = SECONDS_PER_MIN * MINUTES_PER_HOUR; |
48 | static constexpr int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; |
49 | static constexpr int NUMBER_OF_SECONDS_IN_LEAP_YEAR = |
50 | DAYS_PER_LEAP_YEAR * SECONDS_PER_DAY; |
51 | |
52 | static constexpr int TIME_YEAR_BASE = 1900; |
53 | static constexpr int EPOCH_YEAR = 1970; |
54 | static constexpr int EPOCH_WEEK_DAY = 4; |
55 | |
56 | // For asctime the behavior is undefined if struct tm's tm_wday or tm_mon are |
57 | // not within the normal ranges as defined in <time.h>, or if struct tm's |
58 | // tm_year exceeds {INT_MAX}-1990, or if the below asctime_internal algorithm |
59 | // would attempt to generate more than 26 bytes of output (including the |
60 | // terminating null). |
61 | static constexpr int ASCTIME_BUFFER_SIZE = 256; |
62 | static constexpr int ASCTIME_MAX_BYTES = 26; |
63 | |
64 | /* 2000-03-01 (mod 400 year, immediately after feb29 */ |
65 | static constexpr int64_t SECONDS_UNTIL2000_MARCH_FIRST = |
66 | (946684800LL + SECONDS_PER_DAY * (31 + 29)); |
67 | static constexpr int WEEK_DAY_OF2000_MARCH_FIRST = 3; |
68 | |
69 | static constexpr int DAYS_PER400_YEARS = |
70 | (DAYS_PER_NON_LEAP_YEAR * 400) + (400 / 4) - 3; |
71 | static constexpr int DAYS_PER100_YEARS = |
72 | (DAYS_PER_NON_LEAP_YEAR * 100) + (100 / 4) - 1; |
73 | static constexpr int DAYS_PER4_YEARS = (DAYS_PER_NON_LEAP_YEAR * 4) + 1; |
74 | |
75 | // The latest time that can be represented in this form is 03:14:07 UTC on |
76 | // Tuesday, 19 January 2038 (corresponding to 2,147,483,647 seconds since the |
77 | // start of the epoch). This means that systems using a 32-bit time_t type are |
78 | // susceptible to the Year 2038 problem. |
79 | static constexpr int END_OF32_BIT_EPOCH_YEAR = 2038; |
80 | |
81 | static constexpr time_t OUT_OF_RANGE_RETURN_VALUE = -1; |
82 | }; |
83 | |
84 | // Update the "tm" structure's year, month, etc. members from seconds. |
85 | // "total_seconds" is the number of seconds since January 1st, 1970. |
86 | extern int64_t update_from_seconds(int64_t total_seconds, struct tm *tm); |
87 | |
88 | // TODO(michaelrj): move these functions to use ErrorOr instead of setting |
89 | // errno. They always accompany a specific return value so we only need the one |
90 | // variable. |
91 | |
92 | // POSIX.1-2017 requires this. |
93 | LIBC_INLINE time_t out_of_range() { |
94 | libc_errno = EOVERFLOW; |
95 | return TimeConstants::OUT_OF_RANGE_RETURN_VALUE; |
96 | } |
97 | |
98 | LIBC_INLINE void invalid_value() { libc_errno = EINVAL; } |
99 | |
100 | LIBC_INLINE char *asctime(const struct tm *timeptr, char *buffer, |
101 | size_t bufferLength) { |
102 | if (timeptr == nullptr || buffer == nullptr) { |
103 | invalid_value(); |
104 | return nullptr; |
105 | } |
106 | if (timeptr->tm_wday < 0 || |
107 | timeptr->tm_wday > (TimeConstants::DAYS_PER_WEEK - 1)) { |
108 | invalid_value(); |
109 | return nullptr; |
110 | } |
111 | if (timeptr->tm_mon < 0 || |
112 | timeptr->tm_mon > (TimeConstants::MONTHS_PER_YEAR - 1)) { |
113 | invalid_value(); |
114 | return nullptr; |
115 | } |
116 | |
117 | // TODO(rtenneti): i18n the following strings. |
118 | static const char *week_days_name[TimeConstants::DAYS_PER_WEEK] = { |
119 | "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" }; |
120 | |
121 | static const char *months_name[TimeConstants::MONTHS_PER_YEAR] = { |
122 | "Jan" , "Feb" , "Mar" , "Apr" , "May" , "Jun" , |
123 | "Jul" , "Aug" , "Sep" , "Oct" , "Nov" , "Dec" }; |
124 | |
125 | // TODO(michaelr): look into removing this call to __builtin_snprintf that may |
126 | // be emitted as a call to snprintf. Alternatively, look into using our |
127 | // internal printf machinery. |
128 | int written_size = __builtin_snprintf( |
129 | buffer, bufferLength, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n" , |
130 | week_days_name[timeptr->tm_wday], months_name[timeptr->tm_mon], |
131 | timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, |
132 | TimeConstants::TIME_YEAR_BASE + timeptr->tm_year); |
133 | if (written_size < 0) |
134 | return nullptr; |
135 | if (static_cast<size_t>(written_size) >= bufferLength) { |
136 | out_of_range(); |
137 | return nullptr; |
138 | } |
139 | return buffer; |
140 | } |
141 | |
142 | LIBC_INLINE struct tm *gmtime_internal(const time_t *timer, struct tm *result) { |
143 | int64_t seconds = *timer; |
144 | // Update the tm structure's year, month, day, etc. from seconds. |
145 | if (update_from_seconds(total_seconds: seconds, tm: result) < 0) { |
146 | out_of_range(); |
147 | return nullptr; |
148 | } |
149 | |
150 | return result; |
151 | } |
152 | |
153 | } // namespace time_utils |
154 | } // namespace LIBC_NAMESPACE |
155 | |
156 | #endif // LLVM_LIBC_SRC_TIME_TIME_UTILS_H |
157 | |