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 "hdr/types/size_t.h" 13 #include "hdr/types/struct_tm.h" 14 #include "hdr/types/time_t.h" 15 #include "src/__support/common.h" 16 #include "src/__support/macros/config.h" 17 #include "src/errno/libc_errno.h" 18 #include "time_constants.h" 19 20 #include <stdint.h> 21 22 namespace LIBC_NAMESPACE_DECL { 23 namespace time_utils { 24 25 // Update the "tm" structure's year, month, etc. members from seconds. 26 // "total_seconds" is the number of seconds since January 1st, 1970. 27 extern int64_t update_from_seconds(int64_t total_seconds, struct tm *tm); 28 29 // TODO(michaelrj): move these functions to use ErrorOr instead of setting 30 // errno. They always accompany a specific return value so we only need the one 31 // variable. 32 33 // POSIX.1-2017 requires this. 34 LIBC_INLINE time_t out_of_range() { 35 #ifdef EOVERFLOW 36 // For non-POSIX uses of the standard C time functions, where EOVERFLOW is 37 // not defined, it's OK not to set errno at all. The plain C standard doesn't 38 // require it. 39 libc_errno = EOVERFLOW; 40 #endif 41 return time_constants::OUT_OF_RANGE_RETURN_VALUE; 42 } 43 44 LIBC_INLINE void invalid_value() { libc_errno = EINVAL; } 45 46 LIBC_INLINE char *asctime(const struct tm *timeptr, char *buffer, 47 size_t bufferLength) { 48 if (timeptr == nullptr || buffer == nullptr) { 49 invalid_value(); 50 return nullptr; 51 } 52 if (timeptr->tm_wday < 0 || 53 timeptr->tm_wday > (time_constants::DAYS_PER_WEEK - 1)) { 54 invalid_value(); 55 return nullptr; 56 } 57 if (timeptr->tm_mon < 0 || 58 timeptr->tm_mon > (time_constants::MONTHS_PER_YEAR - 1)) { 59 invalid_value(); 60 return nullptr; 61 } 62 63 // TODO(michaelr): move this to use the strftime machinery 64 int written_size = __builtin_snprintf( 65 buffer, bufferLength, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", 66 time_constants::WEEK_DAY_NAMES[timeptr->tm_wday].data(), 67 time_constants::MONTH_NAMES[timeptr->tm_mon].data(), timeptr->tm_mday, 68 timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, 69 time_constants::TIME_YEAR_BASE + timeptr->tm_year); 70 if (written_size < 0) 71 return nullptr; 72 if (static_cast<size_t>(written_size) >= bufferLength) { 73 out_of_range(); 74 return nullptr; 75 } 76 return buffer; 77 } 78 79 LIBC_INLINE struct tm *gmtime_internal(const time_t *timer, struct tm *result) { 80 int64_t seconds = *timer; 81 // Update the tm structure's year, month, day, etc. from seconds. 82 if (update_from_seconds(seconds, result) < 0) { 83 out_of_range(); 84 return nullptr; 85 } 86 87 return result; 88 } 89 90 // TODO: localtime is not yet implemented and a temporary solution is to 91 // use gmtime, https://github.com/llvm/llvm-project/issues/107597 92 LIBC_INLINE struct tm *localtime(const time_t *t_ptr) { 93 static struct tm result; 94 return time_utils::gmtime_internal(t_ptr, &result); 95 } 96 97 } // namespace time_utils 98 } // namespace LIBC_NAMESPACE_DECL 99 100 #endif // LLVM_LIBC_SRC_TIME_TIME_UTILS_H 101