• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
out_of_range()93 LIBC_INLINE time_t out_of_range() {
94   libc_errno = EOVERFLOW;
95   return TimeConstants::OUT_OF_RANGE_RETURN_VALUE;
96 }
97 
invalid_value()98 LIBC_INLINE void invalid_value() { libc_errno = EINVAL; }
99 
asctime(const struct tm * timeptr,char * buffer,size_t bufferLength)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 
gmtime_internal(const time_t * timer,struct tm * result)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(seconds, 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