aboutsummaryrefslogtreecommitdiff
path: root/libc/src/time/time_utils.h
blob: a5da3ad74f34339246974779edb5ddeea24b1b93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//===-- Collection of utils for mktime and friends --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_TIME_TIME_UTILS_H
#define LLVM_LIBC_SRC_TIME_TIME_UTILS_H

#include <stddef.h> // For size_t.

#include "src/__support/common.h"
#include "src/errno/libc_errno.h"
#include "src/time/mktime.h"

#include <stdint.h>

namespace LIBC_NAMESPACE {
namespace time_utils {

enum Month : int {
  JANUARY,
  FEBRUARY,
  MARCH,
  APRIL,
  MAY,
  JUNE,
  JULY,
  AUGUST,
  SEPTEMBER,
  OCTOBER,
  NOVEMBER,
  DECEMBER
};

struct TimeConstants {
  static constexpr int SECONDS_PER_MIN = 60;
  static constexpr int MINUTES_PER_HOUR = 60;
  static constexpr int HOURS_PER_DAY = 24;
  static constexpr int DAYS_PER_WEEK = 7;
  static constexpr int MONTHS_PER_YEAR = 12;
  static constexpr int DAYS_PER_NON_LEAP_YEAR = 365;
  static constexpr int DAYS_PER_LEAP_YEAR = 366;

  static constexpr int SECONDS_PER_HOUR = SECONDS_PER_MIN * MINUTES_PER_HOUR;
  static constexpr int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
  static constexpr int NUMBER_OF_SECONDS_IN_LEAP_YEAR =
      DAYS_PER_LEAP_YEAR * SECONDS_PER_DAY;

  static constexpr int TIME_YEAR_BASE = 1900;
  static constexpr int EPOCH_YEAR = 1970;
  static constexpr int EPOCH_WEEK_DAY = 4;

  // For asctime the behavior is undefined if struct tm's tm_wday or tm_mon are
  // not within the normal ranges as defined in <time.h>, or if struct tm's
  // tm_year exceeds {INT_MAX}-1990, or if the below asctime_internal algorithm
  // would attempt to generate more than 26 bytes of output (including the
  // terminating null).
  static constexpr int ASCTIME_BUFFER_SIZE = 256;
  static constexpr int ASCTIME_MAX_BYTES = 26;

  /* 2000-03-01 (mod 400 year, immediately after feb29 */
  static constexpr int64_t SECONDS_UNTIL2000_MARCH_FIRST =
      (946684800LL + SECONDS_PER_DAY * (31 + 29));
  static constexpr int WEEK_DAY_OF2000_MARCH_FIRST = 3;

  static constexpr int DAYS_PER400_YEARS =
      (DAYS_PER_NON_LEAP_YEAR * 400) + (400 / 4) - 3;
  static constexpr int DAYS_PER100_YEARS =
      (DAYS_PER_NON_LEAP_YEAR * 100) + (100 / 4) - 1;
  static constexpr int DAYS_PER4_YEARS = (DAYS_PER_NON_LEAP_YEAR * 4) + 1;

  // The latest time that can be represented in this form is 03:14:07 UTC on
  // Tuesday, 19 January 2038 (corresponding to 2,147,483,647 seconds since the
  // start of the epoch). This means that systems using a 32-bit time_t type are
  // susceptible to the Year 2038 problem.
  static constexpr int END_OF32_BIT_EPOCH_YEAR = 2038;

  static constexpr time_t OUT_OF_RANGE_RETURN_VALUE = -1;
};

// Update the "tm" structure's year, month, etc. members from seconds.
// "total_seconds" is the number of seconds since January 1st, 1970.
extern int64_t update_from_seconds(int64_t total_seconds, struct tm *tm);

// TODO(michaelrj): move these functions to use ErrorOr instead of setting
// errno. They always accompany a specific return value so we only need the one
// variable.

// POSIX.1-2017 requires this.
LIBC_INLINE time_t out_of_range() {
  libc_errno = EOVERFLOW;
  return TimeConstants::OUT_OF_RANGE_RETURN_VALUE;
}

LIBC_INLINE void invalid_value() { libc_errno = EINVAL; }

LIBC_INLINE char *asctime(const struct tm *timeptr, char *buffer,
                          size_t bufferLength) {
  if (timeptr == nullptr || buffer == nullptr) {
    invalid_value();
    return nullptr;
  }
  if (timeptr->tm_wday < 0 ||
      timeptr->tm_wday > (TimeConstants::DAYS_PER_WEEK - 1)) {
    invalid_value();
    return nullptr;
  }
  if (timeptr->tm_mon < 0 ||
      timeptr->tm_mon > (TimeConstants::MONTHS_PER_YEAR - 1)) {
    invalid_value();
    return nullptr;
  }

  // TODO(rtenneti): i18n the following strings.
  static const char *week_days_name[TimeConstants::DAYS_PER_WEEK] = {
      "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

  static const char *months_name[TimeConstants::MONTHS_PER_YEAR] = {
      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

  // TODO(michaelr): look into removing this call to __builtin_snprintf that may
  // be emitted as a call to snprintf. Alternatively, look into using our
  // internal printf machinery.
  int written_size = __builtin_snprintf(
      buffer, bufferLength, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
      week_days_name[timeptr->tm_wday], months_name[timeptr->tm_mon],
      timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec,
      TimeConstants::TIME_YEAR_BASE + timeptr->tm_year);
  if (written_size < 0)
    return nullptr;
  if (static_cast<size_t>(written_size) >= bufferLength) {
    out_of_range();
    return nullptr;
  }
  return buffer;
}

LIBC_INLINE struct tm *gmtime_internal(const time_t *timer, struct tm *result) {
  int64_t seconds = *timer;
  // Update the tm structure's year, month, day, etc. from seconds.
  if (update_from_seconds(seconds, result) < 0) {
    out_of_range();
    return nullptr;
  }

  return result;
}

} // namespace time_utils
} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_TIME_TIME_UTILS_H