xref: /llvm-project/libc/src/time/time_utils.h (revision f9c2377fb68e5051b3061186c507f7b87db2a8b2)
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