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 @@ -2,8 +2,10 @@ mktime SRCS mktime.cpp + time_utils.cpp HDRS mktime.h + time_utils.h DEPENDS libc.include.errno libc.include.time diff --git a/libc/src/time/mktime.h b/libc/src/time/mktime.h --- a/libc/src/time/mktime.h +++ b/libc/src/time/mktime.h @@ -14,8 +14,18 @@ namespace __llvm_libc { +constexpr int SecondsPerMin = 60; +constexpr int SecondsPerHour = 3600; +constexpr int SecondsPerDay = 86400; +constexpr int DaysPerWeek = 7; +constexpr int MonthsPerYear = 12; +constexpr int DaysPerNonLeapYear = 365; +constexpr int TimeYearBase = 1900; + time_t mktime(struct tm *t1); +time_t outOfRange(); + } // namespace __llvm_libc #endif // LLVM_LIBC_SRC_TIME_MKTIME_H 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 @@ -11,18 +11,11 @@ #include "src/__support/common.h" #include "src/errno/llvmlibc_errno.h" #include "src/time/mktime.h" +#include "src/time/time_utils.h" namespace __llvm_libc { -constexpr int SecondsPerMin = 60; -constexpr int MinutesPerHour = 60; -constexpr int HoursPerDay = 24; -constexpr int DaysPerWeek = 7; -constexpr int MonthsPerYear = 12; -constexpr int DaysPerNonLeapYear = 365; -constexpr int TimeYearBase = 1900; constexpr int EpochYear = 1970; -constexpr int EpochWeekDay = 4; // The latest time that can be represented in this form is 03:14:07 UTC on // Tuesday, 19 January 2038 (corresponding to 2,147,483,647 seconds since the // start of the epoch). This means that systems using a 32-bit time_t type are @@ -32,50 +25,40 @@ constexpr int NonLeapYearDaysInMonth[] = {31 /* Jan */, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -constexpr bool isLeapYear(const time_t year) { +// Returns number of years from (1, year). +constexpr int64_t getNumOfLeapYearsBefore(int64_t year) { + return (year / 4) - (year / 100) + (year / 400); +} + +// Returns True if year is a leap year. +constexpr bool isLeapYear(const int64_t year) { return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); } // POSIX.1-2017 requires this. -static inline time_t outOfRange() { +time_t outOfRange() { llvmlibc_errno = EOVERFLOW; return static_cast(-1); } -LLVM_LIBC_FUNCTION(time_t, mktime, (struct tm * t1)) { +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. Handle out of range time and date - // values that don't overflow or underflow. - // TODO (rtenneti): Implement the following suggestion Siva: "As we start - // accumulating the seconds, we should be able to check if the next amount of - // seconds to be added can lead to an overflow. If it does, return the - // overflow value. If not keep accumulating. The benefit is that, we don't - // have to validate every input, and also do not need the special cases for - // sizeof(time_t) == 4". - if (t1->tm_sec < 0 || t1->tm_sec > (SecondsPerMin - 1)) - return outOfRange(); - if (t1->tm_min < 0 || t1->tm_min > (MinutesPerHour - 1)) - return outOfRange(); - if (t1->tm_hour < 0 || t1->tm_hour > (HoursPerDay - 1)) - return outOfRange(); - time_t tmYearFromBase = t1->tm_year + TimeYearBase; - - if (tmYearFromBase < EpochYear) - return outOfRange(); + // TODO(rtenneti); Handle leap seconds. + int64_t tmYearFromBase = tm_out->tm_year + TimeYearBase; // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038. if (sizeof(time_t) == 4 && tmYearFromBase >= EndOf32BitEpochYear) { if (tmYearFromBase > EndOf32BitEpochYear) return outOfRange(); - if (t1->tm_mon > 0) + if (tm_out->tm_mon > 0) return outOfRange(); - if (t1->tm_mday > 19) + if (tm_out->tm_mday > 19) return outOfRange(); - if (t1->tm_hour > 3) + if (tm_out->tm_hour > 3) return outOfRange(); - if (t1->tm_min > 14) + if (tm_out->tm_min > 14) return outOfRange(); - if (t1->tm_sec > 7) + if (tm_out->tm_sec > 7) return outOfRange(); } @@ -85,42 +68,59 @@ "ILP64 is unimplemented. This implementation requires " "32-bit integers."); - if (t1->tm_mon < 0 || t1->tm_mon > (MonthsPerYear - 1)) - return outOfRange(); + // Calculate number of months and years from tm_mon. + int64_t month = tm_out->tm_mon; + if (month < 0 || month >= MonthsPerYear - 1) { + int64_t years = month / 12; + month %= 12; + if (month < 0) { + years--; + month += 12; + } + tmYearFromBase += years; + } bool tmYearIsLeap = isLeapYear(tmYearFromBase); - time_t daysInMonth = NonLeapYearDaysInMonth[t1->tm_mon]; - // Add one day if it is a leap year and the month is February. - if (tmYearIsLeap && t1->tm_mon == 1) - ++daysInMonth; - if (t1->tm_mday < 1 || t1->tm_mday > daysInMonth) - return outOfRange(); - time_t totalDays = t1->tm_mday - 1; - for (int i = 0; i < t1->tm_mon; ++i) + // 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 && t1->tm_mon > 1) + if (tmYearIsLeap && month > 1) totalDays++; - t1->tm_yday = totalDays; - totalDays += (tmYearFromBase - EpochYear) * DaysPerNonLeapYear; - // Add an extra day for each leap year, starting with 1972 - for (time_t year = EpochYear + 2; year < tmYearFromBase;) { - if (isLeapYear(year)) { - totalDays += 1; - year += 4; - } else { - year++; + // Calculate total numbers of days based on the year. + totalDays += (tmYearFromBase - EpochYear) * DaysPerNonLeapYear; + if (tmYearFromBase >= EpochYear) { + totalDays += getNumOfLeapYearsBefore(tmYearFromBase - 1) - + getNumOfLeapYearsBefore(EpochYear); + } else if (tmYearFromBase >= 1) { + totalDays -= getNumOfLeapYearsBefore(EpochYear) - + getNumOfLeapYearsBefore(tmYearFromBase - 1); + } else { + // Calculate number of leap years until 0th year. + totalDays -= + getNumOfLeapYearsBefore(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); + } } } - t1->tm_wday = (EpochWeekDay + totalDays) % DaysPerWeek; - if (t1->tm_wday < 0) - t1->tm_wday += DaysPerWeek; // TODO(rtenneti): Need to handle timezone and update of tm_isdst. - return t1->tm_sec + t1->tm_min * SecondsPerMin + - t1->tm_hour * MinutesPerHour * SecondsPerMin + - totalDays * HoursPerDay * MinutesPerHour * SecondsPerMin; + int64_t seconds = tm_out->tm_sec + tm_out->tm_min * SecondsPerMin + + tm_out->tm_hour * SecondsPerHour + + totalDays * SecondsPerDay; + + // Update the tm structure's year, month, day, etc. from seconds. + if (updateFromSeconds(seconds, tm_out) < 0) + return outOfRange(); + + return static_cast(seconds); } } // namespace __llvm_libc diff --git a/libc/src/time/mktime.h b/libc/src/time/time_utils.h copy from libc/src/time/mktime.h copy to libc/src/time/time_utils.h --- a/libc/src/time/mktime.h +++ b/libc/src/time/time_utils.h @@ -1,4 +1,4 @@ -//===-- Implementation header of mktime -------------------------*- C++ -*-===// +//===-- Collection of utils for mktime and friends --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,18 +6,18 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIBC_SRC_TIME_MKTIME_H -#define LLVM_LIBC_SRC_TIME_MKTIME_H +#ifndef LLVM_LIBC_SRC_TIME_TIME_UTILS_H +#define LLVM_LIBC_SRC_TIME_TIME_UTILS_H #include "src/time/mktime.h" -#include + +#include namespace __llvm_libc { -time_t mktime(struct tm *t1); +// Update the tm structure's year, month, etc members from seconds. +int64_t updateFromSeconds(int64_t seconds, struct tm *tm); } // namespace __llvm_libc -#endif // LLVM_LIBC_SRC_TIME_MKTIME_H - -#include "include/time.h" +#endif // LLVM_LIBC_SRC_TIME_TIME_UTILS_H diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp new file mode 100644 --- /dev/null +++ b/libc/src/time/time_utils.cpp @@ -0,0 +1,132 @@ +//===-- Collection of utils for mktime and friends --------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "src/time/time_utils.h" +#include "src/time/mktime.h" + +#include + +namespace __llvm_libc { + +/* From https://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c */ +/* 2000-03-01 (mod 400 year, immediately after feb29 */ +constexpr int64_t SecondsUntil2000MarchFirst = + (946684800LL + SecondsPerDay * (31 + 29)); + +constexpr int64_t NumberOfSecondsInLeapYear = + (DaysPerNonLeapYear + 1) * SecondsPerDay; +constexpr int WeekDayOf2000MarchFirst = 3; + +constexpr int DaysPer400Years = (DaysPerNonLeapYear * 400 + (400 / 4) - 3); +constexpr int DaysPer100Years = (DaysPerNonLeapYear * 100 + (100 / 4) - 1); +constexpr int DaysPer4Years = (DaysPerNonLeapYear * 4 + 1); + +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; +} + +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 outOfRange(); + if (total_seconds > 0x7FFFFFFF) + return outOfRange(); + } else { + if (total_seconds < INT_MIN * NumberOfSecondsInLeapYear || + total_seconds > INT_MAX * NumberOfSecondsInLeapYear) + return outOfRange(); + } + + int64_t seconds = total_seconds - SecondsUntil2000MarchFirst; + int64_t days = seconds / SecondsPerDay; + int64_t remainingSeconds = seconds % SecondsPerDay; + if (remainingSeconds < 0) { + remainingSeconds += SecondsPerDay; + days--; + } + + int64_t wday = (WeekDayOf2000MarchFirst + days) % DaysPerWeek; + if (wday < 0) + wday += DaysPerWeek; + + // Compute the number of 400 year cycles. + int64_t numOfFourHundredYearCycles = days / DaysPer400Years; + int64_t remainingDays = days % DaysPer400Years; + if (remainingDays < 0) { + remainingDays += 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(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(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(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 >= DaysPerNonLeapYear + leapDay) + yday -= DaysPerNonLeapYear + leapDay; + + int64_t months = 0; + while (daysInMonth[months] <= remainingDays) { + remainingDays -= daysInMonth[months]; + months++; + } + + if (months >= MonthsPerYear - 2) { + months -= MonthsPerYear; + years++; + } + + if (years > INT_MAX || years < INT_MIN) + return 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 - TimeYearBase; + tm->tm_mon = months + 2; + tm->tm_mday = remainingDays + 1; + tm->tm_wday = wday; + tm->tm_yday = yday; + + tm->tm_hour = remainingSeconds / SecondsPerHour; + tm->tm_min = remainingSeconds / SecondsPerMin % SecondsPerMin; + tm->tm_sec = remainingSeconds % SecondsPerMin; + + return 0; +} + +} // namespace __llvm_libc 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 @@ -11,6 +11,7 @@ #include "utils/UnitTest/Test.h" #include +#include #include using __llvm_libc::testing::ErrnoSetterMatcher::Fails; @@ -24,8 +25,10 @@ .tm_min = min, .tm_hour = hour, .tm_mday = mday, - .tm_mon = month, - .tm_year = year - 1900}; + // tm_mon starts with 0 for Jan + .tm_mon = month - 1, + // years since 1900 + .tm_year = year - __llvm_libc::TimeYearBase}; *tm_data = temp; } @@ -35,123 +38,383 @@ return __llvm_libc::mktime(tm_data); } -TEST(LlvmLibcMkTime, FailureSetsErrno) { +class LlvmLibcMkTime : public __llvm_libc::testing::Test { +public: + /* year, month, .. are all actual date and time */ + void check_mktime(struct tm *tm_data, int year, int month, int mday, int hour, + int min, int sec, int wday, int yday) { + // tm_data->tm_year is "years" since 1900. + EXPECT_EQ(tm_data->tm_year + __llvm_libc::TimeYearBase, year); + // tm_data->tm_mon is months since January (January is 0). + EXPECT_EQ(tm_data->tm_mon + 1, month); + EXPECT_EQ(tm_data->tm_mday, mday); + EXPECT_EQ(tm_data->tm_hour, hour); + EXPECT_EQ(tm_data->tm_min, min); + EXPECT_EQ(tm_data->tm_sec, sec); + EXPECT_EQ(tm_data->tm_wday, wday); + EXPECT_EQ(tm_data->tm_yday, yday); + } +}; + +TEST_F(LlvmLibcMkTime, FailureSetsErrno) { struct tm tm_data; - initialize_tm_data(&tm_data, 0, 0, 0, 0, 0, -1); + initialize_tm_data(&tm_data, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, -1); EXPECT_THAT(__llvm_libc::mktime(&tm_data), Fails(EOVERFLOW)); } -TEST(LlvmLibcMkTime, MktimeTestsInvalidSeconds) { +TEST_F(LlvmLibcMkTime, MkTimesInvalidSeconds) { struct tm tm_data; - EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, 0, 0, -1), OutOfRangeReturnValue); - EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, 0, 0, 60), OutOfRangeReturnValue); + time_t seconds; + // -1 second from 1970-01-01 00:00:00 returns 1969-12-31 23:59:59. + seconds = call_mktime(&tm_data, + 1970, // year + 1, // month + 1, // day + 0, // hr + 0, // min + -1); // sec + EXPECT_EQ(seconds, static_cast(-1)); + check_mktime(&tm_data, + 1969, // year + 12, // month + 31, // day + 23, // hr + 59, // min + 59, // sec + 3, // wday + 364); // yday + // 60 seconds from 1970-01-01 00:00:00 returns 1970-01-01 00:01:00. + seconds = call_mktime(&tm_data, + 1970, // year + 1, // month + 1, // day + 0, // hr + 0, // min + 60); // sec + EXPECT_EQ(seconds, static_cast(60)); + check_mktime(&tm_data, + 1970, // year + 1, // month + 1, // day + 0, // hr + 1, // min + 0, // sec + 4, // wday + 0); // yday } -TEST(LlvmLibcMkTime, MktimeTestsInvalidMinutes) { +TEST_F(LlvmLibcMkTime, MktimeTestsInvalidMinutes) { struct tm tm_data; - EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, 0, -1, 0), OutOfRangeReturnValue); - EXPECT_EQ(call_mktime(&tm_data, 0, 0, 1, 0, 60, 0), OutOfRangeReturnValue); + time_t seconds; + // -1 minute from 1970-01-01 00:00:00 returns 1969-12-31 23:59:00. + seconds = call_mktime(&tm_data, + 1970, // year + 1, // month + 1, // day + 0, // hr + -1, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(-60)); + check_mktime(&tm_data, + 1969, // year + 12, // month + 31, // day + 23, // hr + 59, // min + 0, // sec + 3, // wday + 364); // yday + // 60 minutes from 1970-01-01 00:00:00 returns 1970-01-01 01:00:00. + seconds = call_mktime(&tm_data, + 1970, // year + 1, // month + 1, // day + 0, // hr + 60, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(60 * __llvm_libc::SecondsPerMin)); + check_mktime(&tm_data, + 1970, // year + 1, // month + 1, // day + 1, // hr + 0, // min + 0, // sec + 4, // wday + 0); // yday } -TEST(LlvmLibcMkTime, MktimeTestsInvalidHours) { +TEST_F(LlvmLibcMkTime, MktimeTestsInvalidHours) { struct tm tm_data; - EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, -1, 0, 0), OutOfRangeReturnValue); - EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, 24, 0, 0), OutOfRangeReturnValue); + time_t seconds; + // -1 hour from 1970-01-01 00:00:00 returns 1969-12-31 23:00:00. + seconds = call_mktime(&tm_data, + 1970, // year + 1, // month + 1, // day + -1, // hr + 0, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(-__llvm_libc::SecondsPerHour)); + check_mktime(&tm_data, + 1969, // year + 12, // month + 31, // day + 23, // hr + 0, // min + 0, // sec + 3, // wday + 364); // yday + // 24 hours from 1970-01-01 00:00:00 returns 1970-01-02 00:00:00. + seconds = call_mktime(&tm_data, + 1970, // year + 1, // month + 1, // day + 24, // hr + 0, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(24 * __llvm_libc::SecondsPerHour)); + check_mktime(&tm_data, + 1970, // year + 1, // month + 2, // day + 0, // hr + 0, // min + 0, // sec + 5, // wday + 1); // yday } -TEST(LlvmLibcMkTime, MktimeTestsInvalidYear) { +TEST_F(LlvmLibcMkTime, MktimeTestsInvalidYear) { struct tm tm_data; - EXPECT_EQ(call_mktime(&tm_data, 1969, 0, 0, 0, 0, 0), OutOfRangeReturnValue); + time_t seconds; + // -1 year from 1970-01-01 00:00:00 returns 1969-01-01 00:00:00. + seconds = call_mktime(&tm_data, + 1969, // year + 1, // month + 1, // day + 0, // hr + 0, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(-__llvm_libc::DaysPerNonLeapYear * + __llvm_libc::SecondsPerDay)); + check_mktime(&tm_data, + 1969, // year + 1, // month + 1, // day + 0, // hr + 0, // min + 0, // sec + 3, // wday + 0); // yday } -TEST(LlvmLibcMkTime, MktimeTestsInvalidEndOf32BitEpochYear) { - if (sizeof(time_t) != 4) +TEST_F(LlvmLibcMkTime, MktimeTestsInvalidEndOf32BitEpochYear) { + if (sizeof(size_t) != 4) return; struct tm tm_data; // 2038-01-19 03:14:08 tests overflow of the second in 2038. - EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 19, 3, 14, 8), + EXPECT_EQ(call_mktime(&tm_data, 2038, 1, 19, 3, 14, 8), OutOfRangeReturnValue); // 2038-01-19 03:15:07 tests overflow of the minute in 2038. - EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 19, 3, 15, 7), + EXPECT_EQ(call_mktime(&tm_data, 2038, 1, 19, 3, 15, 7), OutOfRangeReturnValue); // 2038-01-19 04:14:07 tests overflow of the hour in 2038. - EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 19, 4, 14, 7), + EXPECT_EQ(call_mktime(&tm_data, 2038, 1, 19, 4, 14, 7), OutOfRangeReturnValue); // 2038-01-20 03:14:07 tests overflow of the day in 2038. - EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 20, 3, 14, 7), + EXPECT_EQ(call_mktime(&tm_data, 2038, 1, 20, 3, 14, 7), OutOfRangeReturnValue); // 2038-02-19 03:14:07 tests overflow of the month in 2038. - EXPECT_EQ(call_mktime(&tm_data, 2038, 1, 19, 3, 14, 7), + EXPECT_EQ(call_mktime(&tm_data, 2038, 2, 19, 3, 14, 7), OutOfRangeReturnValue); // 2039-01-19 03:14:07 tests overflow of the year. - EXPECT_EQ(call_mktime(&tm_data, 2039, 0, 19, 3, 14, 7), + EXPECT_EQ(call_mktime(&tm_data, 2039, 1, 19, 3, 14, 7), OutOfRangeReturnValue); } -TEST(LlvmLibcMkTime, MktimeTestsInvalidMonths) { +TEST_F(LlvmLibcMkTime, MktimeTestsInvalidMonths) { struct tm tm_data; - // Before Jan of 1970 - EXPECT_EQ(call_mktime(&tm_data, 1970, -1, 15, 0, 0, 0), - OutOfRangeReturnValue); - // After Dec of 1970 - EXPECT_EQ(call_mktime(&tm_data, 1970, 12, 15, 0, 0, 0), - OutOfRangeReturnValue); + time_t seconds; + // -1 month from 1970-01-01 00:00:00 returns 1969-12-01 00:00:00. + seconds = call_mktime(&tm_data, + 1970, // year + 0, // month + 1, // day + 0, // hr + 0, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(-31 * __llvm_libc::SecondsPerDay)); + check_mktime(&tm_data, + 1969, // year + 12, // month + 1, // day + 0, // hr + 0, // min + 0, // sec + 1, // wday + 334); // yday + // 1970-13-01 00:00:00 returns 1971-01-01 00:00:00. + seconds = call_mktime(&tm_data, + 1970, // year + 13, // month + 1, // day + 0, // hr + 0, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(__llvm_libc::DaysPerNonLeapYear * + __llvm_libc::SecondsPerDay)); + check_mktime(&tm_data, + 1971, // year + 1, // month + 1, // day + 0, // hr + 0, // min + 0, // sec + 5, // wday + 0); // yday } -TEST(LlvmLibcMkTime, MktimeTestsInvalidDays) { +TEST_F(LlvmLibcMkTime, MktimeTestsInvalidDays) { struct tm tm_data; - // -1 day of Jan, 1970 - EXPECT_EQ(call_mktime(&tm_data, 1970, 0, -1, 0, 0, 0), OutOfRangeReturnValue); - // 32 day of Jan, 1970 - EXPECT_EQ(call_mktime(&tm_data, 1970, 0, 32, 0, 0, 0), OutOfRangeReturnValue); - // 29 day of Feb, 1970 - EXPECT_EQ(call_mktime(&tm_data, 1970, 1, 29, 0, 0, 0), OutOfRangeReturnValue); - // 30 day of Feb, 1972 - EXPECT_EQ(call_mktime(&tm_data, 1972, 1, 30, 0, 0, 0), OutOfRangeReturnValue); - // 31 day of Apr, 1970 - EXPECT_EQ(call_mktime(&tm_data, 1970, 3, 31, 0, 0, 0), OutOfRangeReturnValue); -} + time_t seconds; + // -1 day from 1970-01-01 00:00:00 returns 1969-12-31 00:00:00. + seconds = call_mktime(&tm_data, + 1970, // year + 1, // month + 0, // day + 0, // hr + 0, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(-1 * __llvm_libc::SecondsPerDay)); + check_mktime(&tm_data, + 1969, // year + 12, // month + 31, // day + 0, // hr + 0, // min + 0, // sec + 3, // wday + 364); // yday -TEST(LlvmLibcMkTime, MktimeTestsStartEpochYear) { - // Thu Jan 1 00:00:00 1970 - struct tm tm_data; - EXPECT_EQ(call_mktime(&tm_data, 1970, 0, 1, 0, 0, 0), static_cast(0)); - EXPECT_EQ(4, tm_data.tm_wday); - EXPECT_EQ(0, tm_data.tm_yday); -} + // 1970-01-32 00:00:00 returns 1970-02-01 00:00:00. + seconds = call_mktime(&tm_data, + 1970, // year + 1, // month + 32, // day + 0, // hr + 0, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(31 * __llvm_libc::SecondsPerDay)); + check_mktime(&tm_data, + 1970, // year + 2, // month + 1, // day + 0, // hr + 0, // min + 0, // sec + 0, // wday + 31); // yday -TEST(LlvmLibcMkTime, MktimeTestsEpochYearRandomTime) { - // Thu Jan 1 12:50:50 1970 - struct tm tm_data; - EXPECT_EQ(call_mktime(&tm_data, 1970, 0, 1, 12, 50, 50), - static_cast(46250)); - EXPECT_EQ(4, tm_data.tm_wday); - EXPECT_EQ(0, tm_data.tm_yday); + // 1970-02-29 00:00:00 returns 1970-03-01 00:00:00. + seconds = call_mktime(&tm_data, + 1970, // year + 2, // month + 29, // day + 0, // hr + 0, // min + 0); // sec + EXPECT_EQ(seconds, static_cast(59 * __llvm_libc::SecondsPerDay)); + check_mktime(&tm_data, + 1970, // year + 3, // month + 1, // day + 0, // hr + 0, // min + 0, // sec + 0, // wday + 59); // yday + + // 1972-02-30 00:00:00 returns 1972-03-01 00:00:00. + seconds = call_mktime(&tm_data, + 1972, // year + 2, // month + 30, // day + 0, // hr + 0, // min + 0); // sec + EXPECT_EQ(seconds, + static_cast(((2 * __llvm_libc::DaysPerNonLeapYear) + 60) * + __llvm_libc::SecondsPerDay)); + check_mktime(&tm_data, + 1972, // year + 3, // month + 1, // day + 0, // hr + 0, // min + 0, // sec + 3, // wday + 60); // yday } -TEST(LlvmLibcMkTime, MktimeTestsEndOf32BitEpochYear) { +TEST_F(LlvmLibcMkTime, MktimeTestsEndOf32BitEpochYear) { struct tm tm_data; + time_t seconds; // Test for maximum value of a signed 32-bit integer. // Test implementation can encode time for Tue 19 January 2038 03:14:07 UTC. - EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 19, 3, 14, 7), - static_cast(0x7FFFFFFF)); - EXPECT_EQ(2, tm_data.tm_wday); - EXPECT_EQ(18, tm_data.tm_yday); + seconds = call_mktime(&tm_data, + 2038, // year + 1, // month + 19, // day + 3, // hr + 14, // min + 7); // sec + EXPECT_EQ(seconds, static_cast(0x7FFFFFFF)); + check_mktime(&tm_data, + 2038, // year + 1, // month + 19, // day + 3, // hr + 14, // min + 7, // sec + 2, // wday + 18); // yday } -TEST(LlvmLibcMkTime, MktimeTests64BitYear) { +TEST_F(LlvmLibcMkTime, MktimeTests64BitYear) { if (sizeof(time_t) == 4) return; - // Mon Jan 1 12:50:50 2170 + // Mon Jan 1 12:50:50 2170 (200 years from 1970), struct tm tm_data; - EXPECT_EQ(call_mktime(&tm_data, 2170, 0, 1, 12, 50, 50), - static_cast(6311479850)); - EXPECT_EQ(1, tm_data.tm_wday); - EXPECT_EQ(0, tm_data.tm_yday); + time_t seconds; + seconds = call_mktime(&tm_data, + 2170, // year + 1, // month + 1, // day + 12, // hr + 50, // min + 50); // sec + EXPECT_EQ(seconds, static_cast(6311479850)); + check_mktime(&tm_data, + 2170, // year + 1, // month + 1, // day + 12, // hr + 50, // min + 50, // sec + 1, // wday + 0); // yday // Test for Tue Jan 1 12:50:50 in 2,147,483,647th year. - EXPECT_EQ(call_mktime(&tm_data, 2147483647, 0, 1, 12, 50, 50), + EXPECT_EQ(call_mktime(&tm_data, 2147483647, 1, 1, 12, 50, 50), static_cast(67767976202043050)); - EXPECT_EQ(2, tm_data.tm_wday); - EXPECT_EQ(0, tm_data.tm_yday); + check_mktime(&tm_data, + 2147483647, // year + 1, // month + 1, // day + 12, // hr + 50, // min + 50, // sec + 2, // wday + 0); // yday }