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