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
20namespace LIBC_NAMESPACE {
21namespace time_utils {
22
23enum 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
38struct 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.
86extern 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.
93LIBC_INLINE time_t out_of_range() {
94 libc_errno = EOVERFLOW;
95 return TimeConstants::OUT_OF_RANGE_RETURN_VALUE;
96}
97
98LIBC_INLINE void invalid_value() { libc_errno = EINVAL; }
99
100LIBC_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
142LIBC_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

source code of libc/src/time/time_utils.h