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"
10eaae52c1SRaman Tenneti #include "src/__support/common.h"
11eaae52c1SRaman Tenneti
12eaae52c1SRaman Tenneti #include <limits.h>
13eaae52c1SRaman Tenneti
14eaae52c1SRaman Tenneti namespace __llvm_libc {
15eaae52c1SRaman Tenneti namespace time_utils {
16eaae52c1SRaman Tenneti
17eaae52c1SRaman Tenneti using __llvm_libc::time_utils::TimeConstants;
18eaae52c1SRaman Tenneti
computeRemainingYears(int64_t daysPerYears,int64_t quotientYears,int64_t * remainingDays)19eaae52c1SRaman Tenneti static int64_t computeRemainingYears(int64_t daysPerYears,
20eaae52c1SRaman Tenneti int64_t quotientYears,
21eaae52c1SRaman Tenneti int64_t *remainingDays) {
22eaae52c1SRaman Tenneti int64_t years = *remainingDays / daysPerYears;
23eaae52c1SRaman Tenneti if (years == quotientYears)
24eaae52c1SRaman Tenneti years--;
25eaae52c1SRaman Tenneti *remainingDays -= years * daysPerYears;
26eaae52c1SRaman Tenneti return years;
27eaae52c1SRaman Tenneti }
28eaae52c1SRaman Tenneti
29eaae52c1SRaman Tenneti // First, divide "total_seconds" by the number of seconds in a day to get the
30eaae52c1SRaman Tenneti // number of days since Jan 1 1970. The remainder will be used to calculate the
31eaae52c1SRaman Tenneti // number of Hours, Minutes and Seconds.
32eaae52c1SRaman Tenneti //
33eaae52c1SRaman Tenneti // Then, adjust that number of days by a constant to be the number of days
34eaae52c1SRaman Tenneti // since Mar 1 2000. Year 2000 is a multiple of 400, the leap year cycle. This
35eaae52c1SRaman Tenneti // makes it easier to count how many leap years have passed using division.
36eaae52c1SRaman Tenneti //
37eaae52c1SRaman Tenneti // While calculating numbers of years in the days, the following algorithm
38eaae52c1SRaman Tenneti // subdivides the days into the number of 400 years, the number of 100 years and
39eaae52c1SRaman Tenneti // the number of 4 years. These numbers of cycle years are used in calculating
40eaae52c1SRaman Tenneti // leap day. This is similar to the algorithm used in getNumOfLeapYearsBefore()
41eaae52c1SRaman Tenneti // and isLeapYear(). Then compute the total number of years in days from these
42eaae52c1SRaman Tenneti // subdivided units.
43eaae52c1SRaman Tenneti //
44eaae52c1SRaman Tenneti // Compute the number of months from the remaining days. Finally, adjust years
45eaae52c1SRaman Tenneti // to be 1900 and months to be from January.
update_from_seconds(int64_t total_seconds,struct tm * tm)46*1c92911eSMichael Jones int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
47eaae52c1SRaman Tenneti // Days in month starting from March in the year 2000.
48eaae52c1SRaman Tenneti static const char daysInMonth[] = {31 /* Mar */, 30, 31, 30, 31, 31,
49eaae52c1SRaman Tenneti 30, 31, 30, 31, 31, 29};
50eaae52c1SRaman Tenneti
51eaae52c1SRaman Tenneti if (sizeof(time_t) == 4) {
52eaae52c1SRaman Tenneti if (total_seconds < 0x80000000)
53*1c92911eSMichael Jones return time_utils::out_of_range();
54eaae52c1SRaman Tenneti if (total_seconds > 0x7FFFFFFF)
55*1c92911eSMichael Jones return time_utils::out_of_range();
56eaae52c1SRaman Tenneti } else {
57eaae52c1SRaman Tenneti if (total_seconds <
58eaae52c1SRaman Tenneti INT_MIN * static_cast<int64_t>(
59*1c92911eSMichael Jones TimeConstants::NUMBER_OF_SECONDS_IN_LEAP_YEAR) ||
60*1c92911eSMichael Jones total_seconds >
61*1c92911eSMichael Jones INT_MAX * static_cast<int64_t>(
62*1c92911eSMichael Jones TimeConstants::NUMBER_OF_SECONDS_IN_LEAP_YEAR))
63*1c92911eSMichael Jones return time_utils::out_of_range();
64eaae52c1SRaman Tenneti }
65eaae52c1SRaman Tenneti
66*1c92911eSMichael Jones int64_t seconds =
67*1c92911eSMichael Jones total_seconds - TimeConstants::SECONDS_UNTIL2000_MARCH_FIRST;
68*1c92911eSMichael Jones int64_t days = seconds / TimeConstants::SECONDS_PER_DAY;
69*1c92911eSMichael Jones int64_t remainingSeconds = seconds % TimeConstants::SECONDS_PER_DAY;
70eaae52c1SRaman Tenneti if (remainingSeconds < 0) {
71*1c92911eSMichael Jones remainingSeconds += TimeConstants::SECONDS_PER_DAY;
72eaae52c1SRaman Tenneti days--;
73eaae52c1SRaman Tenneti }
74eaae52c1SRaman Tenneti
75*1c92911eSMichael Jones int64_t wday = (TimeConstants::WEEK_DAY_OF2000_MARCH_FIRST + days) %
76*1c92911eSMichael Jones TimeConstants::DAYS_PER_WEEK;
77eaae52c1SRaman Tenneti if (wday < 0)
78*1c92911eSMichael Jones wday += TimeConstants::DAYS_PER_WEEK;
79eaae52c1SRaman Tenneti
80eaae52c1SRaman Tenneti // Compute the number of 400 year cycles.
81*1c92911eSMichael Jones int64_t numOfFourHundredYearCycles = days / TimeConstants::DAYS_PER400_YEARS;
82*1c92911eSMichael Jones int64_t remainingDays = days % TimeConstants::DAYS_PER400_YEARS;
83eaae52c1SRaman Tenneti if (remainingDays < 0) {
84*1c92911eSMichael Jones remainingDays += TimeConstants::DAYS_PER400_YEARS;
85eaae52c1SRaman Tenneti numOfFourHundredYearCycles--;
86eaae52c1SRaman Tenneti }
87eaae52c1SRaman Tenneti
88eaae52c1SRaman Tenneti // The reminder number of years after computing number of
89eaae52c1SRaman Tenneti // "four hundred year cycles" will be 4 hundred year cycles or less in 400
90eaae52c1SRaman Tenneti // years.
91*1c92911eSMichael Jones int64_t numOfHundredYearCycles = computeRemainingYears(
92*1c92911eSMichael Jones TimeConstants::DAYS_PER100_YEARS, 4, &remainingDays);
93eaae52c1SRaman Tenneti
94eaae52c1SRaman Tenneti // The reminder number of years after computing number of
95eaae52c1SRaman Tenneti // "hundred year cycles" will be 25 four year cycles or less in 100 years.
96eaae52c1SRaman Tenneti int64_t numOfFourYearCycles =
97*1c92911eSMichael Jones computeRemainingYears(TimeConstants::DAYS_PER4_YEARS, 25, &remainingDays);
98eaae52c1SRaman Tenneti
99eaae52c1SRaman Tenneti // The reminder number of years after computing number of "four year cycles"
100eaae52c1SRaman Tenneti // will be 4 one year cycles or less in 4 years.
101eaae52c1SRaman Tenneti int64_t remainingYears = computeRemainingYears(
102*1c92911eSMichael Jones TimeConstants::DAYS_PER_NON_LEAP_YEAR, 4, &remainingDays);
103eaae52c1SRaman Tenneti
104eaae52c1SRaman Tenneti // Calculate number of years from year 2000.
105eaae52c1SRaman Tenneti int64_t years = remainingYears + 4 * numOfFourYearCycles +
106eaae52c1SRaman Tenneti 100 * numOfHundredYearCycles +
107eaae52c1SRaman Tenneti 400LL * numOfFourHundredYearCycles;
108eaae52c1SRaman Tenneti
109eaae52c1SRaman Tenneti int leapDay =
110eaae52c1SRaman Tenneti !remainingYears && (numOfFourYearCycles || !numOfHundredYearCycles);
111eaae52c1SRaman Tenneti
112eaae52c1SRaman Tenneti int64_t yday = remainingDays + 31 + 28 + leapDay;
113*1c92911eSMichael Jones if (yday >= TimeConstants::DAYS_PER_NON_LEAP_YEAR + leapDay)
114*1c92911eSMichael Jones yday -= TimeConstants::DAYS_PER_NON_LEAP_YEAR + leapDay;
115eaae52c1SRaman Tenneti
116eaae52c1SRaman Tenneti int64_t months = 0;
117eaae52c1SRaman Tenneti while (daysInMonth[months] <= remainingDays) {
118eaae52c1SRaman Tenneti remainingDays -= daysInMonth[months];
119eaae52c1SRaman Tenneti months++;
120eaae52c1SRaman Tenneti }
121eaae52c1SRaman Tenneti
122*1c92911eSMichael Jones if (months >= TimeConstants::MONTHS_PER_YEAR - 2) {
123*1c92911eSMichael Jones months -= TimeConstants::MONTHS_PER_YEAR;
124eaae52c1SRaman Tenneti years++;
125eaae52c1SRaman Tenneti }
126eaae52c1SRaman Tenneti
127eaae52c1SRaman Tenneti if (years > INT_MAX || years < INT_MIN)
128*1c92911eSMichael Jones return time_utils::out_of_range();
129eaae52c1SRaman Tenneti
130eaae52c1SRaman Tenneti // All the data (years, month and remaining days) was calculated from
131eaae52c1SRaman Tenneti // March, 2000. Thus adjust the data to be from January, 1900.
132*1c92911eSMichael Jones tm->tm_year = years + 2000 - TimeConstants::TIME_YEAR_BASE;
133eaae52c1SRaman Tenneti tm->tm_mon = months + 2;
134eaae52c1SRaman Tenneti tm->tm_mday = remainingDays + 1;
135eaae52c1SRaman Tenneti tm->tm_wday = wday;
136eaae52c1SRaman Tenneti tm->tm_yday = yday;
137eaae52c1SRaman Tenneti
138*1c92911eSMichael Jones tm->tm_hour = remainingSeconds / TimeConstants::SECONDS_PER_HOUR;
139*1c92911eSMichael Jones tm->tm_min = remainingSeconds / TimeConstants::SECONDS_PER_MIN %
140*1c92911eSMichael Jones TimeConstants::SECONDS_PER_MIN;
141*1c92911eSMichael Jones tm->tm_sec = remainingSeconds % TimeConstants::SECONDS_PER_MIN;
142eaae52c1SRaman Tenneti // TODO(rtenneti): Need to handle timezone and update of tm_isdst.
143eaae52c1SRaman Tenneti tm->tm_isdst = 0;
144eaae52c1SRaman Tenneti
145eaae52c1SRaman Tenneti return 0;
146eaae52c1SRaman Tenneti }
147eaae52c1SRaman Tenneti
148eaae52c1SRaman Tenneti } // namespace time_utils
149eaae52c1SRaman Tenneti } // namespace __llvm_libc
150