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