diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -249,6 +249,7 @@ ]; let Functions = [ + "gmtime", "mktime", ]; } diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -76,6 +76,7 @@ libc.src.threads.thrd_join # time.h entrypoints + libc.src.time.gmtime libc.src.time.mktime # unistd.h entrypoints diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -5,6 +5,7 @@ RestrictedPtrType FILERestrictedPtr = RestrictedPtrType; NamedType StructTmType = NamedType<"struct tm">; PtrType StructTmPtr = PtrType; + PtrType TimeTTypePtr = PtrType; HeaderSpec Assert = HeaderSpec< "assert.h", @@ -597,6 +598,11 @@ ], [], // Enumerations [ + FunctionSpec< + "gmtime", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "mktime", RetValSpec, diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt --- a/libc/src/time/CMakeLists.txt +++ b/libc/src/time/CMakeLists.txt @@ -1,11 +1,34 @@ +add_object_library( + time_utils + SRCS + time_utils.cpp + HDRS + time_utils.h + DEPENDS + libc.utils.CPP.standalone_cpp +) + +add_entrypoint_object( + gmtime + SRCS + gmtime.cpp + HDRS + gmtime.h + DEPENDS + .time_utils + libc.include.errno + libc.include.time + libc.src.errno.__errno_location +) + add_entrypoint_object( mktime SRCS mktime.cpp HDRS mktime.h - time_utils.h DEPENDS + .time_utils libc.include.errno libc.include.time libc.src.errno.__errno_location diff --git a/libc/src/time/gmtime.h b/libc/src/time/gmtime.h new file mode 100644 --- /dev/null +++ b/libc/src/time/gmtime.h @@ -0,0 +1,22 @@ +//===-- Implementation header of gmtime -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_TIME_GMTIME_H +#define LLVM_LIBC_SRC_TIME_GMTIME_H + +#include + +namespace __llvm_libc { + +struct tm *gmtime(const time_t *timer); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_TIME_GMTIME_H + +#include "include/time.h" diff --git a/libc/src/time/gmtime.cpp b/libc/src/time/gmtime.cpp new file mode 100644 --- /dev/null +++ b/libc/src/time/gmtime.cpp @@ -0,0 +1,30 @@ +//===-- Implementation of gmtime function ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/time/gmtime.h" +#include "src/__support/common.h" +#include "src/time/time_utils.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(struct tm *, gmtime, (const time_t *timer)) { + // TODO(rtenneti): This method is not thread safe. + static struct tm tm_out; + time_t seconds = *timer; + // Update the tm structure's year, month, day, etc. from seconds. + if (time_utils::UpdateFromSeconds(seconds, &tm_out) < 0) { + time_utils::OutOfRange(); + return nullptr; + } + + return &tm_out; +} + +} // namespace __llvm_libc diff --git a/libc/src/time/mktime.cpp b/libc/src/time/mktime.cpp --- a/libc/src/time/mktime.cpp +++ b/libc/src/time/mktime.cpp @@ -29,134 +29,6 @@ return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); } -static int64_t computeRemainingYears(int64_t daysPerYears, - int64_t quotientYears, - int64_t *remainingDays) { - int64_t years = *remainingDays / daysPerYears; - if (years == quotientYears) - years--; - *remainingDays -= years * daysPerYears; - return years; -} - -// Update the "tm" structure's year, month, etc. members from seconds. -// "total_seconds" is the number of seconds since January 1st, 1970. -// -// First, divide "total_seconds" by the number of seconds in a day to get the -// number of days since Jan 1 1970. The remainder will be used to calculate the -// number of Hours, Minutes and Seconds. -// -// Then, adjust that number of days by a constant to be the number of days -// since Mar 1 2000. Year 2000 is a multiple of 400, the leap year cycle. This -// makes it easier to count how many leap years have passed using division. -// -// While calculating numbers of years in the days, the following algorithm -// subdivides the days into the number of 400 years, the number of 100 years and -// the number of 4 years. These numbers of cycle years are used in calculating -// leap day. This is similar to the algorithm used in getNumOfLeapYearsBefore() -// and isLeapYear(). Then compute the total number of years in days from these -// subdivided units. -// -// Compute the number of months from the remaining days. Finally, adjust years -// to be 1900 and months to be from January. -static int64_t updateFromSeconds(int64_t total_seconds, struct tm *tm) { - // Days in month starting from March in the year 2000. - static const char daysInMonth[] = {31 /* Mar */, 30, 31, 30, 31, 31, - 30, 31, 30, 31, 31, 29}; - - if (sizeof(time_t) == 4) { - if (total_seconds < 0x80000000) - return time_utils::OutOfRange(); - if (total_seconds > 0x7FFFFFFF) - return time_utils::OutOfRange(); - } else { - if (total_seconds < - INT_MIN * static_cast( - TimeConstants::NumberOfSecondsInLeapYear) || - total_seconds > INT_MAX * static_cast( - TimeConstants::NumberOfSecondsInLeapYear)) - return time_utils::OutOfRange(); - } - - int64_t seconds = total_seconds - TimeConstants::SecondsUntil2000MarchFirst; - int64_t days = seconds / TimeConstants::SecondsPerDay; - int64_t remainingSeconds = seconds % TimeConstants::SecondsPerDay; - if (remainingSeconds < 0) { - remainingSeconds += TimeConstants::SecondsPerDay; - days--; - } - - int64_t wday = (TimeConstants::WeekDayOf2000MarchFirst + days) % - TimeConstants::DaysPerWeek; - if (wday < 0) - wday += TimeConstants::DaysPerWeek; - - // Compute the number of 400 year cycles. - int64_t numOfFourHundredYearCycles = days / TimeConstants::DaysPer400Years; - int64_t remainingDays = days % TimeConstants::DaysPer400Years; - if (remainingDays < 0) { - remainingDays += TimeConstants::DaysPer400Years; - numOfFourHundredYearCycles--; - } - - // The reminder number of years after computing number of - // "four hundred year cycles" will be 4 hundred year cycles or less in 400 - // years. - int64_t numOfHundredYearCycles = - computeRemainingYears(TimeConstants::DaysPer100Years, 4, &remainingDays); - - // The reminder number of years after computing number of - // "hundred year cycles" will be 25 four year cycles or less in 100 years. - int64_t numOfFourYearCycles = - computeRemainingYears(TimeConstants::DaysPer4Years, 25, &remainingDays); - - // The reminder number of years after computing number of "four year cycles" - // will be 4 one year cycles or less in 4 years. - int64_t remainingYears = computeRemainingYears( - TimeConstants::DaysPerNonLeapYear, 4, &remainingDays); - - // Calculate number of years from year 2000. - int64_t years = remainingYears + 4 * numOfFourYearCycles + - 100 * numOfHundredYearCycles + - 400LL * numOfFourHundredYearCycles; - - int leapDay = - !remainingYears && (numOfFourYearCycles || !numOfHundredYearCycles); - - int64_t yday = remainingDays + 31 + 28 + leapDay; - if (yday >= TimeConstants::DaysPerNonLeapYear + leapDay) - yday -= TimeConstants::DaysPerNonLeapYear + leapDay; - - int64_t months = 0; - while (daysInMonth[months] <= remainingDays) { - remainingDays -= daysInMonth[months]; - months++; - } - - if (months >= TimeConstants::MonthsPerYear - 2) { - months -= TimeConstants::MonthsPerYear; - years++; - } - - if (years > INT_MAX || years < INT_MIN) - return time_utils::OutOfRange(); - - // All the data (years, month and remaining days) was calculated from - // March, 2000. Thus adjust the data to be from January, 1900. - tm->tm_year = years + 2000 - TimeConstants::TimeYearBase; - tm->tm_mon = months + 2; - tm->tm_mday = remainingDays + 1; - tm->tm_wday = wday; - tm->tm_yday = yday; - - tm->tm_hour = remainingSeconds / TimeConstants::SecondsPerHour; - tm->tm_min = remainingSeconds / TimeConstants::SecondsPerMin % - TimeConstants::SecondsPerMin; - tm->tm_sec = remainingSeconds % TimeConstants::SecondsPerMin; - - return 0; -} - LLVM_LIBC_FUNCTION(time_t, mktime, (struct tm * tm_out)) { // Unlike most C Library functions, mktime doesn't just die on bad input. // TODO(rtenneti); Handle leap seconds. @@ -236,7 +108,7 @@ totalDays * TimeConstants::SecondsPerDay; // Update the tm structure's year, month, day, etc. from seconds. - if (updateFromSeconds(seconds, tm_out) < 0) + if (time_utils::UpdateFromSeconds(seconds, tm_out) < 0) return time_utils::OutOfRange(); return static_cast(seconds); diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h --- a/libc/src/time/time_utils.h +++ b/libc/src/time/time_utils.h @@ -59,6 +59,10 @@ return static_cast(-1); } +// Update the "tm" structure's year, month, etc. members from seconds. +// "total_seconds" is the number of seconds since January 1st, 1970. +extern int64_t UpdateFromSeconds(int64_t total_seconds, struct tm *tm); + } // namespace time_utils } // namespace __llvm_libc diff --git a/libc/src/time/mktime.cpp b/libc/src/time/time_utils.cpp copy from libc/src/time/mktime.cpp copy to libc/src/time/time_utils.cpp --- a/libc/src/time/mktime.cpp +++ b/libc/src/time/time_utils.cpp @@ -6,29 +6,16 @@ // //===----------------------------------------------------------------------===// -#include "src/time/mktime.h" -#include "src/__support/common.h" #include "src/time/time_utils.h" +#include "src/__support/common.h" #include namespace __llvm_libc { +namespace time_utils { using __llvm_libc::time_utils::TimeConstants; -static constexpr int NonLeapYearDaysInMonth[] = {31, 28, 31, 30, 31, 30, - 31, 31, 30, 31, 30, 31}; - -// Returns number of years from (1, year). -static constexpr int64_t getNumOfLeapYearsBefore(int64_t year) { - return (year / 4) - (year / 100) + (year / 400); -} - -// Returns True if year is a leap year. -static constexpr bool isLeapYear(const int64_t year) { - return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); -} - static int64_t computeRemainingYears(int64_t daysPerYears, int64_t quotientYears, int64_t *remainingDays) { @@ -39,9 +26,6 @@ return years; } -// Update the "tm" structure's year, month, etc. members from seconds. -// "total_seconds" is the number of seconds since January 1st, 1970. -// // First, divide "total_seconds" by the number of seconds in a day to get the // number of days since Jan 1 1970. The remainder will be used to calculate the // number of Hours, Minutes and Seconds. @@ -59,7 +43,7 @@ // // Compute the number of months from the remaining days. Finally, adjust years // to be 1900 and months to be from January. -static int64_t updateFromSeconds(int64_t total_seconds, struct tm *tm) { +int64_t UpdateFromSeconds(int64_t total_seconds, struct tm *tm) { // Days in month starting from March in the year 2000. static const char daysInMonth[] = {31 /* Mar */, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; @@ -153,93 +137,11 @@ tm->tm_min = remainingSeconds / TimeConstants::SecondsPerMin % TimeConstants::SecondsPerMin; tm->tm_sec = remainingSeconds % TimeConstants::SecondsPerMin; - - return 0; -} - -LLVM_LIBC_FUNCTION(time_t, mktime, (struct tm * tm_out)) { - // Unlike most C Library functions, mktime doesn't just die on bad input. - // TODO(rtenneti); Handle leap seconds. - int64_t tmYearFromBase = tm_out->tm_year + TimeConstants::TimeYearBase; - - // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038. - if (sizeof(time_t) == 4 && - tmYearFromBase >= TimeConstants::EndOf32BitEpochYear) { - if (tmYearFromBase > TimeConstants::EndOf32BitEpochYear) - return time_utils::OutOfRange(); - if (tm_out->tm_mon > 0) - return time_utils::OutOfRange(); - if (tm_out->tm_mday > 19) - return time_utils::OutOfRange(); - if (tm_out->tm_hour > 3) - return time_utils::OutOfRange(); - if (tm_out->tm_min > 14) - return time_utils::OutOfRange(); - if (tm_out->tm_sec > 7) - return time_utils::OutOfRange(); - } - - // Years are ints. A 32-bit year will fit into a 64-bit time_t. - // A 64-bit year will not. - static_assert(sizeof(int) == 4, - "ILP64 is unimplemented. This implementation requires " - "32-bit integers."); - - // Calculate number of months and years from tm_mon. - int64_t month = tm_out->tm_mon; - if (month < 0 || month >= TimeConstants::MonthsPerYear - 1) { - int64_t years = month / 12; - month %= 12; - if (month < 0) { - years--; - month += 12; - } - tmYearFromBase += years; - } - bool tmYearIsLeap = isLeapYear(tmYearFromBase); - - // Calculate total number of days based on the month and the day (tm_mday). - int64_t totalDays = tm_out->tm_mday - 1; - for (int64_t i = 0; i < month; ++i) - totalDays += NonLeapYearDaysInMonth[i]; - // Add one day if it is a leap year and the month is after February. - if (tmYearIsLeap && month > 1) - totalDays++; - - // Calculate total numbers of days based on the year. - totalDays += (tmYearFromBase - TimeConstants::EpochYear) * - TimeConstants::DaysPerNonLeapYear; - if (tmYearFromBase >= TimeConstants::EpochYear) { - totalDays += getNumOfLeapYearsBefore(tmYearFromBase - 1) - - getNumOfLeapYearsBefore(TimeConstants::EpochYear); - } else if (tmYearFromBase >= 1) { - totalDays -= getNumOfLeapYearsBefore(TimeConstants::EpochYear) - - getNumOfLeapYearsBefore(tmYearFromBase - 1); - } else { - // Calculate number of leap years until 0th year. - totalDays -= getNumOfLeapYearsBefore(TimeConstants::EpochYear) - - getNumOfLeapYearsBefore(0); - if (tmYearFromBase <= 0) { - totalDays -= 1; // Subtract 1 for 0th year. - // Calculate number of leap years until -1 year - if (tmYearFromBase < 0) { - totalDays -= getNumOfLeapYearsBefore(-tmYearFromBase) - - getNumOfLeapYearsBefore(1); - } - } - } - // TODO(rtenneti): Need to handle timezone and update of tm_isdst. - int64_t seconds = tm_out->tm_sec + - tm_out->tm_min * TimeConstants::SecondsPerMin + - tm_out->tm_hour * TimeConstants::SecondsPerHour + - totalDays * TimeConstants::SecondsPerDay; + tm->tm_isdst = 0; - // Update the tm structure's year, month, day, etc. from seconds. - if (updateFromSeconds(seconds, tm_out) < 0) - return time_utils::OutOfRange(); - - return static_cast(seconds); + return 0; } +} // namespace time_utils } // namespace __llvm_libc diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt --- a/libc/test/src/time/CMakeLists.txt +++ b/libc/test/src/time/CMakeLists.txt @@ -5,9 +5,11 @@ SUITE libc_time_unittests SRCS + gmtime_test.cpp mktime_test.cpp HDRS TmMatcher.h DEPENDS + libc.src.time.gmtime libc.src.time.mktime ) diff --git a/libc/test/src/time/gmtime_test.cpp b/libc/test/src/time/gmtime_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/time/gmtime_test.cpp @@ -0,0 +1,288 @@ +//===-- Unittests for gmtime ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/time/gmtime.h" +#include "src/time/time_utils.h" +#include "test/ErrnoSetterMatcher.h" +#include "test/src/time/TmMatcher.h" +#include "utils/UnitTest/Test.h" + +#include +#include +#include + +using __llvm_libc::testing::ErrnoSetterMatcher::Fails; +using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; +using __llvm_libc::time_utils::TimeConstants; + +TEST(LlvmLibcGmTime, GmTimeTestsOutOfRange) { + time_t seconds = 1 + INT_MAX * static_cast( + TimeConstants::NumberOfSecondsInLeapYear); + struct tm *tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TRUE(tm_data == nullptr); + EXPECT_EQ(llvmlibc_errno, EOVERFLOW); + + llvmlibc_errno = 0; + seconds = + INT_MIN * static_cast(TimeConstants::NumberOfSecondsInLeapYear) - + 1; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TRUE(tm_data == nullptr); + EXPECT_EQ(llvmlibc_errno, EOVERFLOW); +} + +TEST(LlvmLibcGmTime, GmTimeTestsInvalidSeconds) { + time_t seconds = 0; + struct tm *tm_data = nullptr; + // -1 second from 1970-01-01 00:00:00 returns 1969-12-31 23:59:59. + seconds = -1; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{59, // sec + 59, // min + 23, // hr + 31, // day + 12 - 1, // tm_mon starts with 0 for Jan + 1969 - TimeConstants::TimeYearBase, // year + 3, // wday + 364, // yday + 0}), + *tm_data); + // 60 seconds from 1970-01-01 00:00:00 returns 1970-01-01 00:01:00. + seconds = 60; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 1, // min + 0, // hr + 1, // day + 0, // tm_mon starts with 0 for Jan + 1970 - TimeConstants::TimeYearBase, // year + 4, // wday + 0, // yday + 0}), + *tm_data); +} + +TEST(LlvmLibcGmTime, GmTimeTestsInvalidMinutes) { + time_t seconds = 0; + struct tm *tm_data = nullptr; + // -1 minute from 1970-01-01 00:00:00 returns 1969-12-31 23:59:00. + seconds = -TimeConstants::SecondsPerMin; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 59, // min + 23, // hr + 31, // day + 11, // tm_mon starts with 0 for Jan + 1969 - TimeConstants::TimeYearBase, // year + 3, // wday + 0, // yday + 0}), + *tm_data); + // 60 minutes from 1970-01-01 00:00:00 returns 1970-01-01 01:00:00. + seconds = 60 * TimeConstants::SecondsPerMin; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 1, // hr + 1, // day + 0, // tm_mon starts with 0 for Jan + 1970 - TimeConstants::TimeYearBase, // year + 4, // wday + 0, // yday + 0}), + *tm_data); +} + +TEST(LlvmLibcGmTime, GmTimeTestsInvalidHours) { + time_t seconds = 0; + struct tm *tm_data = nullptr; + // -1 hour from 1970-01-01 00:00:00 returns 1969-12-31 23:00:00. + seconds = -TimeConstants::SecondsPerHour; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 23, // hr + 31, // day + 11, // tm_mon starts with 0 for Jan + 1969 - TimeConstants::TimeYearBase, // year + 3, // wday + 0, // yday + 0}), + *tm_data); + // 24 hours from 1970-01-01 00:00:00 returns 1970-01-02 00:00:00. + seconds = 24 * TimeConstants::SecondsPerHour; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 0, // hr + 2, // day + 0, // tm_mon starts with 0 for Jan + 1970 - TimeConstants::TimeYearBase, // year + 5, // wday + 0, // yday + 0}), + *tm_data); +} + +TEST(LlvmLibcGmTime, GmTimeTestsInvalidYear) { + // -1 year from 1970-01-01 00:00:00 returns 1969-01-01 00:00:00. + time_t seconds = + -TimeConstants::DaysPerNonLeapYear * TimeConstants::SecondsPerDay; + struct tm *tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 0, // hr + 1, // day + 0, // tm_mon starts with 0 for Jan + 1969 - TimeConstants::TimeYearBase, // year + 3, // wday + 0, // yday + 0}), + *tm_data); +} + +TEST(LlvmLibcGmTime, GmTimeTestsInvalidMonths) { + time_t seconds = 0; + struct tm *tm_data = nullptr; + // -1 month from 1970-01-01 00:00:00 returns 1969-12-01 00:00:00. + seconds = -31 * TimeConstants::SecondsPerDay; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 0, // hr + 1, // day + 12 - 1, // tm_mon starts with 0 for Jan + 1969 - TimeConstants::TimeYearBase, // year + 1, // wday + 0, // yday + 0}), + *tm_data); + // 1970-13-01 00:00:00 returns 1971-01-01 00:00:00. + seconds = TimeConstants::DaysPerNonLeapYear * TimeConstants::SecondsPerDay; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 0, // hr + 1, // day + 0, // tm_mon starts with 0 for Jan + 1971 - TimeConstants::TimeYearBase, // year + 5, // wday + 0, // yday + 0}), + *tm_data); +} + +TEST(LlvmLibcGmTime, GmTimeTestsInvalidDays) { + time_t seconds = 0; + struct tm *tm_data = nullptr; + // -1 day from 1970-01-01 00:00:00 returns 1969-12-31 00:00:00. + seconds = -1 * TimeConstants::SecondsPerDay; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 0, // hr + 31, // day + 11, // tm_mon starts with 0 for Jan + 1969 - TimeConstants::TimeYearBase, // year + 3, // wday + 0, // yday + 0}), + *tm_data); + + // 1970-01-32 00:00:00 returns 1970-02-01 00:00:00. + seconds = 31 * TimeConstants::SecondsPerDay; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 0, // hr + 1, // day + 0, // tm_mon starts with 0 for Jan + 1970 - TimeConstants::TimeYearBase, // year + 0, // wday + 0, // yday + 0}), + *tm_data); + + // 1970-02-29 00:00:00 returns 1970-03-01 00:00:00. + seconds = 59 * TimeConstants::SecondsPerDay; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 0, // hr + 1, // day + 2, // tm_mon starts with 0 for Jan + 1970 - TimeConstants::TimeYearBase, // year + 0, // wday + 0, // yday + 0}), + *tm_data); + + // 1972-02-30 00:00:00 returns 1972-03-01 00:00:00. + seconds = ((2 * TimeConstants::DaysPerNonLeapYear) + 60) * + TimeConstants::SecondsPerDay; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{0, // sec + 0, // min + 0, // hr + 1, // day + 2, // tm_mon starts with 0 for Jan + 1972 - TimeConstants::TimeYearBase, // year + 3, // wday + 0, // yday + 0}), + *tm_data); +} + +TEST(LlvmLibcGmTime, GmTimeTestsEndOf32BitEpochYear) { + // Test for maximum value of a signed 32-bit integer. + // Test implementation can encode time for Tue 19 January 2038 03:14:07 UTC. + time_t seconds = 0x7FFFFFFF; + struct tm *tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{7, // sec + 14, // min + 3, // hr + 19, // day + 0, // tm_mon starts with 0 for Jan + 2038 - TimeConstants::TimeYearBase, // year + 2, // wday + 7, // yday + 0}), + *tm_data); +} + +TEST(LlvmLibcGmTime, GmTimeTests64BitYear) { + if (sizeof(time_t) == 4) + return; + // Mon Jan 1 12:50:50 2170 (200 years from 1970), + time_t seconds = 6311479850; + struct tm *tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{50, // sec + 50, // min + 12, // hr + 1, // day + 0, // tm_mon starts with 0 for Jan + 2170 - TimeConstants::TimeYearBase, // year + 1, // wday + 50, // yday + 0}), + *tm_data); + + // Test for Tue Jan 1 12:50:50 in 2,147,483,647th year. + seconds = 67767976202043050; + tm_data = __llvm_libc::gmtime(&seconds); + EXPECT_TM_EQ((tm{50, // sec + 50, // min + 12, // hr + 1, // day + 0, // tm_mon starts with 0 for Jan + 2147483647 - TimeConstants::TimeYearBase, // year + 2, // wday + 50, // yday + 0}), + *tm_data); +} diff --git a/libc/test/src/time/mktime_test.cpp b/libc/test/src/time/mktime_test.cpp --- a/libc/test/src/time/mktime_test.cpp +++ b/libc/test/src/time/mktime_test.cpp @@ -50,7 +50,7 @@ EXPECT_THAT(__llvm_libc::mktime(&tm_data), Fails(EOVERFLOW)); } -TEST(LlvmLibcMkTime, MkTimesInvalidSeconds) { +TEST(LlvmLibcMkTime, MkTimeTestsInvalidSeconds) { struct tm tm_data; // -1 second from 1970-01-01 00:00:00 returns 1969-12-31 23:59:59. EXPECT_THAT(call_mktime(&tm_data,