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