16f0f844eSRaman Tenneti //===-- Implementation of mktime function ---------------------------------===// 26f0f844eSRaman Tenneti // 36f0f844eSRaman Tenneti // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 46f0f844eSRaman Tenneti // See https://llvm.org/LICENSE.txt for license information. 56f0f844eSRaman Tenneti // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 66f0f844eSRaman Tenneti // 76f0f844eSRaman Tenneti //===----------------------------------------------------------------------===// 86f0f844eSRaman Tenneti 96f0f844eSRaman Tenneti #include "src/time/mktime.h" 10034f5629SRaman Tenneti #include "src/__support/common.h" 115ff3ff33SPetr Hosek #include "src/__support/macros/config.h" 12*f9c2377fSMichael Jones #include "src/time/time_constants.h" 13034f5629SRaman Tenneti #include "src/time/time_utils.h" 14034f5629SRaman Tenneti 155ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL { 166f0f844eSRaman Tenneti 17034f5629SRaman Tenneti // Returns number of years from (1, year). 181c92911eSMichael Jones static constexpr int64_t get_num_of_leap_years_before(int64_t year) { 19034f5629SRaman Tenneti return (year / 4) - (year / 100) + (year / 400); 20034f5629SRaman Tenneti } 216f0f844eSRaman Tenneti 22034f5629SRaman Tenneti // Returns True if year is a leap year. 231c92911eSMichael Jones static constexpr bool is_leap_year(const int64_t year) { 246f0f844eSRaman Tenneti return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); 256f0f844eSRaman Tenneti } 266f0f844eSRaman Tenneti 27034f5629SRaman Tenneti LLVM_LIBC_FUNCTION(time_t, mktime, (struct tm * tm_out)) { 28034f5629SRaman Tenneti // Unlike most C Library functions, mktime doesn't just die on bad input. 29034f5629SRaman Tenneti // TODO(rtenneti); Handle leap seconds. 30*f9c2377fSMichael Jones int64_t tm_year_from_base = tm_out->tm_year + time_constants::TIME_YEAR_BASE; 316f0f844eSRaman Tenneti 326f0f844eSRaman Tenneti // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038. 33034f5629SRaman Tenneti if (sizeof(time_t) == 4 && 34*f9c2377fSMichael Jones tm_year_from_base >= time_constants::END_OF32_BIT_EPOCH_YEAR) { 35*f9c2377fSMichael Jones if (tm_year_from_base > time_constants::END_OF32_BIT_EPOCH_YEAR) 361c92911eSMichael Jones return time_utils::out_of_range(); 37034f5629SRaman Tenneti if (tm_out->tm_mon > 0) 381c92911eSMichael Jones return time_utils::out_of_range(); 39034f5629SRaman Tenneti if (tm_out->tm_mday > 19) 401c92911eSMichael Jones return time_utils::out_of_range(); 4181d82739SSimon Tatham else if (tm_out->tm_mday == 19) { 42034f5629SRaman Tenneti if (tm_out->tm_hour > 3) 431c92911eSMichael Jones return time_utils::out_of_range(); 4481d82739SSimon Tatham else if (tm_out->tm_hour == 3) { 45034f5629SRaman Tenneti if (tm_out->tm_min > 14) 461c92911eSMichael Jones return time_utils::out_of_range(); 4781d82739SSimon Tatham else if (tm_out->tm_min == 14) { 48034f5629SRaman Tenneti if (tm_out->tm_sec > 7) 491c92911eSMichael Jones return time_utils::out_of_range(); 506f0f844eSRaman Tenneti } 5181d82739SSimon Tatham } 5281d82739SSimon Tatham } 5381d82739SSimon Tatham } 546f0f844eSRaman Tenneti 556f0f844eSRaman Tenneti // Years are ints. A 32-bit year will fit into a 64-bit time_t. 566f0f844eSRaman Tenneti // A 64-bit year will not. 5780225af4SMikhail R. Gadelha static_assert( 5880225af4SMikhail R. Gadelha sizeof(int) == 4, 5980225af4SMikhail R. Gadelha "ILP64 is unimplemented. This implementation requires 32-bit integers."); 606f0f844eSRaman Tenneti 61034f5629SRaman Tenneti // Calculate number of months and years from tm_mon. 62034f5629SRaman Tenneti int64_t month = tm_out->tm_mon; 63*f9c2377fSMichael Jones if (month < 0 || month >= time_constants::MONTHS_PER_YEAR - 1) { 64034f5629SRaman Tenneti int64_t years = month / 12; 65034f5629SRaman Tenneti month %= 12; 66034f5629SRaman Tenneti if (month < 0) { 67034f5629SRaman Tenneti years--; 68034f5629SRaman Tenneti month += 12; 69034f5629SRaman Tenneti } 701c92911eSMichael Jones tm_year_from_base += years; 71034f5629SRaman Tenneti } 721c92911eSMichael Jones bool tm_year_is_leap = is_leap_year(tm_year_from_base); 736f0f844eSRaman Tenneti 74034f5629SRaman Tenneti // Calculate total number of days based on the month and the day (tm_mday). 751c92911eSMichael Jones int64_t total_days = tm_out->tm_mday - 1; 76034f5629SRaman Tenneti for (int64_t i = 0; i < month; ++i) 77*f9c2377fSMichael Jones total_days += time_constants::NON_LEAP_YEAR_DAYS_IN_MONTH[i]; 786f0f844eSRaman Tenneti // Add one day if it is a leap year and the month is after February. 791c92911eSMichael Jones if (tm_year_is_leap && month > 1) 801c92911eSMichael Jones total_days++; 816f0f844eSRaman Tenneti 82034f5629SRaman Tenneti // Calculate total numbers of days based on the year. 83*f9c2377fSMichael Jones total_days += (tm_year_from_base - time_constants::EPOCH_YEAR) * 84*f9c2377fSMichael Jones time_constants::DAYS_PER_NON_LEAP_YEAR; 85*f9c2377fSMichael Jones if (tm_year_from_base >= time_constants::EPOCH_YEAR) { 861c92911eSMichael Jones total_days += get_num_of_leap_years_before(tm_year_from_base - 1) - 87*f9c2377fSMichael Jones get_num_of_leap_years_before(time_constants::EPOCH_YEAR); 881c92911eSMichael Jones } else if (tm_year_from_base >= 1) { 89*f9c2377fSMichael Jones total_days -= get_num_of_leap_years_before(time_constants::EPOCH_YEAR) - 901c92911eSMichael Jones get_num_of_leap_years_before(tm_year_from_base - 1); 916f0f844eSRaman Tenneti } else { 92034f5629SRaman Tenneti // Calculate number of leap years until 0th year. 93*f9c2377fSMichael Jones total_days -= get_num_of_leap_years_before(time_constants::EPOCH_YEAR) - 941c92911eSMichael Jones get_num_of_leap_years_before(0); 951c92911eSMichael Jones if (tm_year_from_base <= 0) { 961c92911eSMichael Jones total_days -= 1; // Subtract 1 for 0th year. 97034f5629SRaman Tenneti // Calculate number of leap years until -1 year 981c92911eSMichael Jones if (tm_year_from_base < 0) { 991c92911eSMichael Jones total_days -= get_num_of_leap_years_before(-tm_year_from_base) - 1001c92911eSMichael Jones get_num_of_leap_years_before(1); 101034f5629SRaman Tenneti } 1026f0f844eSRaman Tenneti } 1036f0f844eSRaman Tenneti } 1046f0f844eSRaman Tenneti 1050a58a1c9SNick Desaulniers // TODO: https://github.com/llvm/llvm-project/issues/121962 1060a58a1c9SNick Desaulniers // Need to handle timezone and update of tm_isdst. 107034f5629SRaman Tenneti int64_t seconds = tm_out->tm_sec + 108*f9c2377fSMichael Jones tm_out->tm_min * time_constants::SECONDS_PER_MIN + 109*f9c2377fSMichael Jones tm_out->tm_hour * time_constants::SECONDS_PER_HOUR + 110*f9c2377fSMichael Jones total_days * time_constants::SECONDS_PER_DAY; 111034f5629SRaman Tenneti 112034f5629SRaman Tenneti // Update the tm structure's year, month, day, etc. from seconds. 1131c92911eSMichael Jones if (time_utils::update_from_seconds(seconds, tm_out) < 0) 1141c92911eSMichael Jones return time_utils::out_of_range(); 115034f5629SRaman Tenneti 116034f5629SRaman Tenneti return static_cast<time_t>(seconds); 1176f0f844eSRaman Tenneti } 1186f0f844eSRaman Tenneti 1195ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL 120