//
//===----------------------------------------------------------------------===//
-#include "include/errno.h"
-
-#include "src/__support/common.h"
-#include "src/errno/llvmlibc_errno.h"
#include "src/time/mktime.h"
+#include "src/__support/common.h"
+#include "src/time/time_utils.h"
+
+#include <limits.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
-// susceptible to the Year 2038 problem.
-constexpr int EndOf32BitEpochYear = 2038;
-
-constexpr int NonLeapYearDaysInMonth[] = {31 /* Jan */, 28, 31, 30, 31, 30,
- 31, 31, 30, 31, 30, 31};
-
-constexpr bool isLeapYear(const time_t year) {
+using __llvm_libc::time_utils::TimeConstants;
+
+// 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));
}
-// POSIX.1-2017 requires this.
-static inline time_t outOfRange() {
- llvmlibc_errno = EOVERFLOW;
- return static_cast<time_t>(-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;
+}
+
+// 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<int64_t>(
+ TimeConstants::NumberOfSecondsInLeapYear) ||
+ total_seconds > INT_MAX * static_cast<int64_t>(
+ 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 * 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 + TimeConstants::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)
- return outOfRange();
- if (t1->tm_mday > 19)
- return outOfRange();
- if (t1->tm_hour > 3)
- return outOfRange();
- if (t1->tm_min > 14)
- return outOfRange();
- if (t1->tm_sec > 7)
- return outOfRange();
+ 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.
"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 >= TimeConstants::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)
- totalDays += NonLeapYearDaysInMonth[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 += TimeConstants::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 - 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);
+ }
}
}
- 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 * TimeConstants::SecondsPerMin +
+ tm_out->tm_hour * TimeConstants::SecondsPerHour +
+ totalDays * TimeConstants::SecondsPerDay;
+
+ // Update the tm structure's year, month, day, etc. from seconds.
+ if (updateFromSeconds(seconds, tm_out) < 0)
+ return time_utils::OutOfRange();
+
+ return static_cast<time_t>(seconds);
}
} // namespace __llvm_libc
//===----------------------------------------------------------------------===//
#include "src/time/mktime.h"
+#include "src/time/time_utils.h"
#include "test/ErrnoSetterMatcher.h"
+#include "test/src/time/TmMatcher.h"
#include "utils/UnitTest/Test.h"
#include <errno.h>
+#include <limits.h>
#include <string.h>
using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
-
-static constexpr time_t OutOfRangeReturnValue = -1;
+using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+using __llvm_libc::time_utils::TimeConstants;
// A helper function to initialize tm data structure.
static inline void initialize_tm_data(struct tm *tm_data, int year, int month,
- int mday, int hour, int min, int sec) {
+ int mday, int hour, int min, int sec,
+ int wday, int yday) {
struct tm temp = {.tm_sec = sec,
.tm_min = min,
.tm_hour = hour,
.tm_mday = mday,
- .tm_mon = month,
- .tm_year = year - 1900};
+ .tm_mon = month - 1, // tm_mon starts with 0 for Jan
+ // years since 1900
+ .tm_year = year - TimeConstants::TimeYearBase,
+ .tm_wday = wday,
+ .tm_yday = yday};
*tm_data = temp;
}
static inline time_t call_mktime(struct tm *tm_data, int year, int month,
- int mday, int hour, int min, int sec) {
- initialize_tm_data(tm_data, year, month, mday, hour, min, sec);
+ int mday, int hour, int min, int sec, int wday,
+ int yday) {
+ initialize_tm_data(tm_data, year, month, mday, hour, min, sec, wday, yday);
return __llvm_libc::mktime(tm_data);
}
TEST(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,
+ 0, 0);
EXPECT_THAT(__llvm_libc::mktime(&tm_data), Fails(EOVERFLOW));
}
-TEST(LlvmLibcMkTime, MktimeTestsInvalidSeconds) {
+TEST(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);
+ // -1 second from 1970-01-01 00:00:00 returns 1969-12-31 23:59:59.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 1, // month
+ 1, // day
+ 0, // hr
+ 0, // min
+ -1, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(-1));
+ 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.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 1, // month
+ 1, // day
+ 0, // hr
+ 0, // min
+ 60, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(60));
+ 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(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);
+ // -1 minute from 1970-01-01 00:00:00 returns 1969-12-31 23:59:00.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 1, // month
+ 1, // day
+ 0, // hr
+ -1, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(-TimeConstants::SecondsPerMin));
+ 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.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 1, // month
+ 1, // day
+ 0, // hr
+ 60, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(60 * TimeConstants::SecondsPerMin));
+ 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(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);
+ // -1 hour from 1970-01-01 00:00:00 returns 1969-12-31 23:00:00.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 1, // month
+ 1, // day
+ -1, // hr
+ 0, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(-TimeConstants::SecondsPerHour));
+ 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.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 1, // month
+ 1, // day
+ 24, // hr
+ 0, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(24 * TimeConstants::SecondsPerHour));
+ 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(LlvmLibcMkTime, MktimeTestsInvalidYear) {
struct tm tm_data;
- EXPECT_EQ(call_mktime(&tm_data, 1969, 0, 0, 0, 0, 0), OutOfRangeReturnValue);
+ // -1 year from 1970-01-01 00:00:00 returns 1969-01-01 00:00:00.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1969, // year
+ 1, // month
+ 1, // day
+ 0, // hr
+ 0, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(-TimeConstants::DaysPerNonLeapYear *
+ TimeConstants::SecondsPerDay));
+ 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(LlvmLibcMkTime, MktimeTestsInvalidEndOf32BitEpochYear) {
- if (sizeof(time_t) != 4)
+ 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),
- OutOfRangeReturnValue);
+ EXPECT_THAT(call_mktime(&tm_data, 2038, 1, 19, 3, 14, 8, 0, 0),
+ Succeeds(TimeConstants::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),
- OutOfRangeReturnValue);
+ EXPECT_THAT(call_mktime(&tm_data, 2038, 1, 19, 3, 15, 7, 0, 0),
+ Succeeds(TimeConstants::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),
- OutOfRangeReturnValue);
+ EXPECT_THAT(call_mktime(&tm_data, 2038, 1, 19, 4, 14, 7, 0, 0),
+ Succeeds(TimeConstants::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),
- OutOfRangeReturnValue);
+ EXPECT_THAT(call_mktime(&tm_data, 2038, 1, 20, 3, 14, 7, 0, 0),
+ Succeeds(TimeConstants::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),
- OutOfRangeReturnValue);
+ EXPECT_THAT(call_mktime(&tm_data, 2038, 2, 19, 3, 14, 7, 0, 0),
+ Succeeds(TimeConstants::OutOfRangeReturnValue));
// 2039-01-19 03:14:07 tests overflow of the year.
- EXPECT_EQ(call_mktime(&tm_data, 2039, 0, 19, 3, 14, 7),
- OutOfRangeReturnValue);
+ EXPECT_THAT(call_mktime(&tm_data, 2039, 1, 19, 3, 14, 7, 0, 0),
+ Succeeds(TimeConstants::OutOfRangeReturnValue));
}
TEST(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);
+ // -1 month from 1970-01-01 00:00:00 returns 1969-12-01 00:00:00.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 0, // month
+ 1, // day
+ 0, // hr
+ 0, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(-31 * TimeConstants::SecondsPerDay));
+ 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.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 13, // month
+ 1, // day
+ 0, // hr
+ 0, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(TimeConstants::DaysPerNonLeapYear *
+ TimeConstants::SecondsPerDay));
+ 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(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);
-}
+ // -1 day from 1970-01-01 00:00:00 returns 1969-12-31 00:00:00.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 1, // month
+ 0, // day
+ 0, // hr
+ 0, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(-1 * TimeConstants::SecondsPerDay));
+ 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);
-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<time_t>(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.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 1, // month
+ 32, // day
+ 0, // hr
+ 0, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(31 * TimeConstants::SecondsPerDay));
+ 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);
-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<time_t>(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.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1970, // year
+ 2, // month
+ 29, // day
+ 0, // hr
+ 0, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(59 * TimeConstants::SecondsPerDay));
+ 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.
+ EXPECT_THAT(call_mktime(&tm_data,
+ 1972, // year
+ 2, // month
+ 30, // day
+ 0, // hr
+ 0, // min
+ 0, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(((2 * TimeConstants::DaysPerNonLeapYear) + 60) *
+ TimeConstants::SecondsPerDay));
+ 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(LlvmLibcMkTime, MktimeTestsEndOf32BitEpochYear) {
struct tm tm_data;
// 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<time_t>(0x7FFFFFFF));
- EXPECT_EQ(2, tm_data.tm_wday);
- EXPECT_EQ(18, tm_data.tm_yday);
+ EXPECT_THAT(call_mktime(&tm_data,
+ 2038, // year
+ 1, // month
+ 19, // day
+ 3, // hr
+ 14, // min
+ 7, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(0x7FFFFFFF));
+ 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(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<time_t>(6311479850));
- EXPECT_EQ(1, tm_data.tm_wday);
- EXPECT_EQ(0, tm_data.tm_yday);
+ EXPECT_THAT(call_mktime(&tm_data,
+ 2170, // year
+ 1, // month
+ 1, // day
+ 12, // hr
+ 50, // min
+ 50, // sec
+ 0, // wday
+ 0), // yday
+ Succeeds(6311479850));
+ 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.
- EXPECT_EQ(call_mktime(&tm_data, 2147483647, 0, 1, 12, 50, 50),
- static_cast<time_t>(67767976202043050));
- EXPECT_EQ(2, tm_data.tm_wday);
- EXPECT_EQ(0, tm_data.tm_yday);
+ EXPECT_THAT(call_mktime(&tm_data, 2147483647, 1, 1, 12, 50, 50, 0, 0),
+ Succeeds(67767976202043050));
+ 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);
}