1 //===-- Collection of utils for mktime and friends --------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC_TIME_TIME_UTILS_H
10 #define LLVM_LIBC_SRC_TIME_TIME_UTILS_H
12 #include <stddef.h> // For size_t.
14 #include "src/__support/common.h"
15 #include "src/__support/macros/config.h"
16 #include "src/errno/libc_errno.h"
17 #include "src/time/mktime.h"
21 namespace LIBC_NAMESPACE_DECL
{
22 namespace time_utils
{
39 struct TimeConstants
{
40 static constexpr int SECONDS_PER_MIN
= 60;
41 static constexpr int MINUTES_PER_HOUR
= 60;
42 static constexpr int HOURS_PER_DAY
= 24;
43 static constexpr int DAYS_PER_WEEK
= 7;
44 static constexpr int MONTHS_PER_YEAR
= 12;
45 static constexpr int DAYS_PER_NON_LEAP_YEAR
= 365;
46 static constexpr int DAYS_PER_LEAP_YEAR
= 366;
48 static constexpr int SECONDS_PER_HOUR
= SECONDS_PER_MIN
* MINUTES_PER_HOUR
;
49 static constexpr int SECONDS_PER_DAY
= SECONDS_PER_HOUR
* HOURS_PER_DAY
;
50 static constexpr int NUMBER_OF_SECONDS_IN_LEAP_YEAR
=
51 DAYS_PER_LEAP_YEAR
* SECONDS_PER_DAY
;
53 static constexpr int TIME_YEAR_BASE
= 1900;
54 static constexpr int EPOCH_YEAR
= 1970;
55 static constexpr int EPOCH_WEEK_DAY
= 4;
57 // For asctime the behavior is undefined if struct tm's tm_wday or tm_mon are
58 // not within the normal ranges as defined in <time.h>, or if struct tm's
59 // tm_year exceeds {INT_MAX}-1990, or if the below asctime_internal algorithm
60 // would attempt to generate more than 26 bytes of output (including the
62 static constexpr int ASCTIME_BUFFER_SIZE
= 256;
63 static constexpr int ASCTIME_MAX_BYTES
= 26;
65 /* 2000-03-01 (mod 400 year, immediately after feb29 */
66 static constexpr int64_t SECONDS_UNTIL2000_MARCH_FIRST
=
67 (946684800LL + SECONDS_PER_DAY
* (31 + 29));
68 static constexpr int WEEK_DAY_OF2000_MARCH_FIRST
= 3;
70 static constexpr int DAYS_PER400_YEARS
=
71 (DAYS_PER_NON_LEAP_YEAR
* 400) + (400 / 4) - 3;
72 static constexpr int DAYS_PER100_YEARS
=
73 (DAYS_PER_NON_LEAP_YEAR
* 100) + (100 / 4) - 1;
74 static constexpr int DAYS_PER4_YEARS
= (DAYS_PER_NON_LEAP_YEAR
* 4) + 1;
76 // The latest time that can be represented in this form is 03:14:07 UTC on
77 // Tuesday, 19 January 2038 (corresponding to 2,147,483,647 seconds since the
78 // start of the epoch). This means that systems using a 32-bit time_t type are
79 // susceptible to the Year 2038 problem.
80 static constexpr int END_OF32_BIT_EPOCH_YEAR
= 2038;
82 static constexpr time_t OUT_OF_RANGE_RETURN_VALUE
= -1;
85 // Update the "tm" structure's year, month, etc. members from seconds.
86 // "total_seconds" is the number of seconds since January 1st, 1970.
87 extern int64_t update_from_seconds(int64_t total_seconds
, struct tm
*tm
);
89 // TODO(michaelrj): move these functions to use ErrorOr instead of setting
90 // errno. They always accompany a specific return value so we only need the one
93 // POSIX.1-2017 requires this.
94 LIBC_INLINE
time_t out_of_range() {
96 // For non-POSIX uses of the standard C time functions, where EOVERFLOW is
97 // not defined, it's OK not to set errno at all. The plain C standard doesn't
99 libc_errno
= EOVERFLOW
;
101 return TimeConstants::OUT_OF_RANGE_RETURN_VALUE
;
104 LIBC_INLINE
void invalid_value() { libc_errno
= EINVAL
; }
106 LIBC_INLINE
char *asctime(const struct tm
*timeptr
, char *buffer
,
107 size_t bufferLength
) {
108 if (timeptr
== nullptr || buffer
== nullptr) {
112 if (timeptr
->tm_wday
< 0 ||
113 timeptr
->tm_wday
> (TimeConstants::DAYS_PER_WEEK
- 1)) {
117 if (timeptr
->tm_mon
< 0 ||
118 timeptr
->tm_mon
> (TimeConstants::MONTHS_PER_YEAR
- 1)) {
123 // TODO(rtenneti): i18n the following strings.
124 static const char *week_days_name
[TimeConstants::DAYS_PER_WEEK
] = {
125 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
127 static const char *months_name
[TimeConstants::MONTHS_PER_YEAR
] = {
128 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
129 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
131 // TODO(michaelr): look into removing this call to __builtin_snprintf that may
132 // be emitted as a call to snprintf. Alternatively, look into using our
133 // internal printf machinery.
134 int written_size
= __builtin_snprintf(
135 buffer
, bufferLength
, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
136 week_days_name
[timeptr
->tm_wday
], months_name
[timeptr
->tm_mon
],
137 timeptr
->tm_mday
, timeptr
->tm_hour
, timeptr
->tm_min
, timeptr
->tm_sec
,
138 TimeConstants::TIME_YEAR_BASE
+ timeptr
->tm_year
);
139 if (written_size
< 0)
141 if (static_cast<size_t>(written_size
) >= bufferLength
) {
148 LIBC_INLINE
struct tm
*gmtime_internal(const time_t *timer
, struct tm
*result
) {
149 int64_t seconds
= *timer
;
150 // Update the tm structure's year, month, day, etc. from seconds.
151 if (update_from_seconds(seconds
, result
) < 0) {
159 // TODO: localtime is not yet implemented and a temporary solution is to
160 // use gmtime, https://github.com/llvm/llvm-project/issues/107597
161 LIBC_INLINE
struct tm
*localtime(const time_t *t_ptr
) {
162 static struct tm result
;
163 return time_utils::gmtime_internal(t_ptr
, &result
);
166 } // namespace time_utils
167 } // namespace LIBC_NAMESPACE_DECL
169 #endif // LLVM_LIBC_SRC_TIME_TIME_UTILS_H