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 SECONDS_PER_MIN = 60; 26 static constexpr int SECONDS_PER_HOUR = 3600; 27 static constexpr int SECONDS_PER_DAY = 86400; 28 static constexpr int DAYS_PER_WEEK = 7; 29 static constexpr int MONTHS_PER_YEAR = 12; 30 static constexpr int DAYS_PER_NON_LEAP_YEAR = 365; 31 static constexpr int DAYS_PER_LEAP_YEAR = 366; 32 static constexpr int TIME_YEAR_BASE = 1900; 33 static constexpr int EPOCH_YEAR = 1970; 34 static constexpr int EPOCH_WEEK_DAY = 4; 35 static constexpr int NUMBER_OF_SECONDS_IN_LEAP_YEAR = 36 (DAYS_PER_NON_LEAP_YEAR + 1) * SECONDS_PER_DAY; 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 ASCTIME_BUFFER_SIZE = 256; 44 static constexpr int ASCTIME_MAX_BYTES = 26; 45 46 /* 2000-03-01 (mod 400 year, immediately after feb29 */ 47 static constexpr int64_t SECONDS_UNTIL2000_MARCH_FIRST = 48 (946684800LL + SECONDS_PER_DAY * (31 + 29)); 49 static constexpr int WEEK_DAY_OF2000_MARCH_FIRST = 3; 50 51 static constexpr int DAYS_PER400_YEARS = 52 (DAYS_PER_NON_LEAP_YEAR * 400 + (400 / 4) - 3); 53 static constexpr int DAYS_PER100_YEARS = 54 (DAYS_PER_NON_LEAP_YEAR * 100 + (100 / 4) - 1); 55 static constexpr int DAYS_PER4_YEARS = (DAYS_PER_NON_LEAP_YEAR * 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 END_OF32_BIT_EPOCH_YEAR = 2038; 62 63 static constexpr time_t OUT_OF_RANGE_RETURN_VALUE = -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 update_from_seconds(int64_t total_seconds, struct tm *tm); 69 70 // POSIX.1-2017 requires this. 71 static inline time_t out_of_range() { 72 llvmlibc_errno = EOVERFLOW; 73 return static_cast<time_t>(-1); 74 } 75 76 static inline void invalid_value() { llvmlibc_errno = EINVAL; } 77 78 static inline char *asctime(const struct tm *timeptr, char *buffer, 79 size_t bufferLength) { 80 if (timeptr == nullptr || buffer == nullptr) { 81 invalid_value(); 82 return nullptr; 83 } 84 if (timeptr->tm_wday < 0 || 85 timeptr->tm_wday > (TimeConstants::DAYS_PER_WEEK - 1)) { 86 invalid_value(); 87 return nullptr; 88 } 89 if (timeptr->tm_mon < 0 || 90 timeptr->tm_mon > (TimeConstants::MONTHS_PER_YEAR - 1)) { 91 invalid_value(); 92 return nullptr; 93 } 94 95 // TODO(rtenneti): i18n the following strings. 96 static const char *week_days_name[TimeConstants::DAYS_PER_WEEK] = { 97 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 98 99 static const char *months_name[TimeConstants::MONTHS_PER_YEAR] = { 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 week_days_name[timeptr->tm_wday], months_name[timeptr->tm_mon], 105 timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, 106 TimeConstants::TIME_YEAR_BASE + timeptr->tm_year); 107 if (written_size < 0) 108 return nullptr; 109 if (static_cast<size_t>(written_size) >= bufferLength) { 110 out_of_range(); 111 return nullptr; 112 } 113 return buffer; 114 } 115 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 (update_from_seconds(seconds, result) < 0) { 121 out_of_range(); 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