xref: /llvm-project/libc/src/time/time_utils.cpp (revision f9c2377fb68e5051b3061186c507f7b87db2a8b2)
1eaae52c1SRaman Tenneti //===-- Implementation of mktime function ---------------------------------===//
2eaae52c1SRaman Tenneti //
3eaae52c1SRaman Tenneti // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4eaae52c1SRaman Tenneti // See https://llvm.org/LICENSE.txt for license information.
5eaae52c1SRaman Tenneti // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6eaae52c1SRaman Tenneti //
7eaae52c1SRaman Tenneti //===----------------------------------------------------------------------===//
8eaae52c1SRaman Tenneti 
9eaae52c1SRaman Tenneti #include "src/time/time_utils.h"
1072ce6294Slntue #include "src/__support/CPP/limits.h" // INT_MIN, INT_MAX
11eaae52c1SRaman Tenneti #include "src/__support/common.h"
125ff3ff33SPetr Hosek #include "src/__support/macros/config.h"
13*f9c2377fSMichael Jones #include "src/time/time_constants.h"
14eaae52c1SRaman Tenneti 
155ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL {
16eaae52c1SRaman Tenneti namespace time_utils {
17eaae52c1SRaman Tenneti 
18eaae52c1SRaman Tenneti static int64_t computeRemainingYears(int64_t daysPerYears,
19eaae52c1SRaman Tenneti                                      int64_t quotientYears,
20eaae52c1SRaman Tenneti                                      int64_t *remainingDays) {
21eaae52c1SRaman Tenneti   int64_t years = *remainingDays / daysPerYears;
22eaae52c1SRaman Tenneti   if (years == quotientYears)
23eaae52c1SRaman Tenneti     years--;
24eaae52c1SRaman Tenneti   *remainingDays -= years * daysPerYears;
25eaae52c1SRaman Tenneti   return years;
26eaae52c1SRaman Tenneti }
27eaae52c1SRaman Tenneti 
28eaae52c1SRaman Tenneti // First, divide "total_seconds" by the number of seconds in a day to get the
29eaae52c1SRaman Tenneti // number of days since Jan 1 1970. The remainder will be used to calculate the
30eaae52c1SRaman Tenneti // number of Hours, Minutes and Seconds.
31eaae52c1SRaman Tenneti //
32eaae52c1SRaman Tenneti // Then, adjust that number of days by a constant to be the number of days
33eaae52c1SRaman Tenneti // since Mar 1 2000. Year 2000 is a multiple of 400, the leap year cycle. This
34eaae52c1SRaman Tenneti // makes it easier to count how many leap years have passed using division.
35eaae52c1SRaman Tenneti //
36eaae52c1SRaman Tenneti // While calculating numbers of years in the days, the following algorithm
37eaae52c1SRaman Tenneti // subdivides the days into the number of 400 years, the number of 100 years and
38eaae52c1SRaman Tenneti // the number of 4 years. These numbers of cycle years are used in calculating
39eaae52c1SRaman Tenneti // leap day. This is similar to the algorithm used in  getNumOfLeapYearsBefore()
40eaae52c1SRaman Tenneti // and isLeapYear(). Then compute the total number of years in days from these
41eaae52c1SRaman Tenneti // subdivided units.
42eaae52c1SRaman Tenneti //
43eaae52c1SRaman Tenneti // Compute the number of months from the remaining days. Finally, adjust years
44eaae52c1SRaman Tenneti // to be 1900 and months to be from January.
451c92911eSMichael Jones int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
46eaae52c1SRaman Tenneti   // Days in month starting from March in the year 2000.
47eaae52c1SRaman Tenneti   static const char daysInMonth[] = {31 /* Mar */, 30, 31, 30, 31, 31,
48eaae52c1SRaman Tenneti                                      30,           31, 30, 31, 31, 29};
49eaae52c1SRaman Tenneti 
5080225af4SMikhail R. Gadelha   constexpr time_t time_min =
5180225af4SMikhail R. Gadelha       (sizeof(time_t) == 4)
5280225af4SMikhail R. Gadelha           ? INT_MIN
5380225af4SMikhail R. Gadelha           : INT_MIN * static_cast<int64_t>(
54*f9c2377fSMichael Jones                           time_constants::NUMBER_OF_SECONDS_IN_LEAP_YEAR);
5580225af4SMikhail R. Gadelha   constexpr time_t time_max =
5680225af4SMikhail R. Gadelha       (sizeof(time_t) == 4)
5780225af4SMikhail R. Gadelha           ? INT_MAX
5880225af4SMikhail R. Gadelha           : INT_MAX * static_cast<int64_t>(
59*f9c2377fSMichael Jones                           time_constants::NUMBER_OF_SECONDS_IN_LEAP_YEAR);
6080225af4SMikhail R. Gadelha 
6180225af4SMikhail R. Gadelha   time_t ts = static_cast<time_t>(total_seconds);
6280225af4SMikhail R. Gadelha   if (ts < time_min || ts > time_max)
631c92911eSMichael Jones     return time_utils::out_of_range();
64eaae52c1SRaman Tenneti 
651c92911eSMichael Jones   int64_t seconds =
66*f9c2377fSMichael Jones       total_seconds - time_constants::SECONDS_UNTIL2000_MARCH_FIRST;
67*f9c2377fSMichael Jones   int64_t days = seconds / time_constants::SECONDS_PER_DAY;
68*f9c2377fSMichael Jones   int64_t remainingSeconds = seconds % time_constants::SECONDS_PER_DAY;
69eaae52c1SRaman Tenneti   if (remainingSeconds < 0) {
70*f9c2377fSMichael Jones     remainingSeconds += time_constants::SECONDS_PER_DAY;
71eaae52c1SRaman Tenneti     days--;
72eaae52c1SRaman Tenneti   }
73eaae52c1SRaman Tenneti 
74*f9c2377fSMichael Jones   int64_t wday = (time_constants::WEEK_DAY_OF2000_MARCH_FIRST + days) %
75*f9c2377fSMichael Jones                  time_constants::DAYS_PER_WEEK;
76eaae52c1SRaman Tenneti   if (wday < 0)
77*f9c2377fSMichael Jones     wday += time_constants::DAYS_PER_WEEK;
78eaae52c1SRaman Tenneti 
79eaae52c1SRaman Tenneti   // Compute the number of 400 year cycles.
80*f9c2377fSMichael Jones   int64_t numOfFourHundredYearCycles = days / time_constants::DAYS_PER400_YEARS;
81*f9c2377fSMichael Jones   int64_t remainingDays = days % time_constants::DAYS_PER400_YEARS;
82eaae52c1SRaman Tenneti   if (remainingDays < 0) {
83*f9c2377fSMichael Jones     remainingDays += time_constants::DAYS_PER400_YEARS;
84eaae52c1SRaman Tenneti     numOfFourHundredYearCycles--;
85eaae52c1SRaman Tenneti   }
86eaae52c1SRaman Tenneti 
872dc97921SMichael Jones   // The remaining number of years after computing the number of
88eaae52c1SRaman Tenneti   // "four hundred year cycles" will be 4 hundred year cycles or less in 400
89eaae52c1SRaman Tenneti   // years.
901c92911eSMichael Jones   int64_t numOfHundredYearCycles = computeRemainingYears(
91*f9c2377fSMichael Jones       time_constants::DAYS_PER100_YEARS, 4, &remainingDays);
92eaae52c1SRaman Tenneti 
932dc97921SMichael Jones   // The remaining number of years after computing the number of
94eaae52c1SRaman Tenneti   // "hundred year cycles" will be 25 four year cycles or less in 100 years.
95*f9c2377fSMichael Jones   int64_t numOfFourYearCycles = computeRemainingYears(
96*f9c2377fSMichael Jones       time_constants::DAYS_PER4_YEARS, 25, &remainingDays);
97eaae52c1SRaman Tenneti 
982dc97921SMichael Jones   // The remaining number of years after computing the number of
992dc97921SMichael Jones   // "four year cycles" will be 4 one year cycles or less in 4 years.
100eaae52c1SRaman Tenneti   int64_t remainingYears = computeRemainingYears(
101*f9c2377fSMichael Jones       time_constants::DAYS_PER_NON_LEAP_YEAR, 4, &remainingDays);
102eaae52c1SRaman Tenneti 
103eaae52c1SRaman Tenneti   // Calculate number of years from year 2000.
104eaae52c1SRaman Tenneti   int64_t years = remainingYears + 4 * numOfFourYearCycles +
105eaae52c1SRaman Tenneti                   100 * numOfHundredYearCycles +
106eaae52c1SRaman Tenneti                   400LL * numOfFourHundredYearCycles;
107eaae52c1SRaman Tenneti 
108eaae52c1SRaman Tenneti   int leapDay =
109eaae52c1SRaman Tenneti       !remainingYears && (numOfFourYearCycles || !numOfHundredYearCycles);
110eaae52c1SRaman Tenneti 
1112dc97921SMichael Jones   // We add 31 and 28 for the number of days in January and February, since our
1122dc97921SMichael Jones   // starting point was March 1st.
113eaae52c1SRaman Tenneti   int64_t yday = remainingDays + 31 + 28 + leapDay;
114*f9c2377fSMichael Jones   if (yday >= time_constants::DAYS_PER_NON_LEAP_YEAR + leapDay)
115*f9c2377fSMichael Jones     yday -= time_constants::DAYS_PER_NON_LEAP_YEAR + leapDay;
116eaae52c1SRaman Tenneti 
117eaae52c1SRaman Tenneti   int64_t months = 0;
118eaae52c1SRaman Tenneti   while (daysInMonth[months] <= remainingDays) {
119eaae52c1SRaman Tenneti     remainingDays -= daysInMonth[months];
120eaae52c1SRaman Tenneti     months++;
121eaae52c1SRaman Tenneti   }
122eaae52c1SRaman Tenneti 
123*f9c2377fSMichael Jones   if (months >= time_constants::MONTHS_PER_YEAR - 2) {
124*f9c2377fSMichael Jones     months -= time_constants::MONTHS_PER_YEAR;
125eaae52c1SRaman Tenneti     years++;
126eaae52c1SRaman Tenneti   }
127eaae52c1SRaman Tenneti 
128eaae52c1SRaman Tenneti   if (years > INT_MAX || years < INT_MIN)
1291c92911eSMichael Jones     return time_utils::out_of_range();
130eaae52c1SRaman Tenneti 
131eaae52c1SRaman Tenneti   // All the data (years, month and remaining days) was calculated from
132eaae52c1SRaman Tenneti   // March, 2000. Thus adjust the data to be from January, 1900.
133*f9c2377fSMichael Jones   tm->tm_year = static_cast<int>(years + 2000 - time_constants::TIME_YEAR_BASE);
134f0a3954eSMichael Jones   tm->tm_mon = static_cast<int>(months + 2);
135f0a3954eSMichael Jones   tm->tm_mday = static_cast<int>(remainingDays + 1);
136f0a3954eSMichael Jones   tm->tm_wday = static_cast<int>(wday);
137f0a3954eSMichael Jones   tm->tm_yday = static_cast<int>(yday);
138eaae52c1SRaman Tenneti 
139f0a3954eSMichael Jones   tm->tm_hour =
140*f9c2377fSMichael Jones       static_cast<int>(remainingSeconds / time_constants::SECONDS_PER_HOUR);
141f0a3954eSMichael Jones   tm->tm_min =
142*f9c2377fSMichael Jones       static_cast<int>(remainingSeconds / time_constants::SECONDS_PER_MIN %
143*f9c2377fSMichael Jones                        time_constants::SECONDS_PER_MIN);
144f0a3954eSMichael Jones   tm->tm_sec =
145*f9c2377fSMichael Jones       static_cast<int>(remainingSeconds % time_constants::SECONDS_PER_MIN);
146eaae52c1SRaman Tenneti   // TODO(rtenneti): Need to handle timezone and update of tm_isdst.
147eaae52c1SRaman Tenneti   tm->tm_isdst = 0;
148eaae52c1SRaman Tenneti 
149eaae52c1SRaman Tenneti   return 0;
150eaae52c1SRaman Tenneti }
151eaae52c1SRaman Tenneti 
152eaae52c1SRaman Tenneti } // namespace time_utils
1535ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL
154