[libc-commits] [libc] eaae52c - This introduces gmtime to LLVM libc, based on C99/C2X/Single Unix Spec.
Raman Tenneti via libc-commits
libc-commits at lists.llvm.org
Tue Mar 16 16:45:09 PDT 2021
Author: Raman Tenneti
Date: 2021-03-16T16:44:48-07:00
New Revision: eaae52c1fd459f9c0a147361bc3b962238faba5c
URL: https://github.com/llvm/llvm-project/commit/eaae52c1fd459f9c0a147361bc3b962238faba5c
DIFF: https://github.com/llvm/llvm-project/commit/eaae52c1fd459f9c0a147361bc3b962238faba5c.diff
LOG: This introduces gmtime to LLVM libc, based on C99/C2X/Single Unix Spec.
This change doesn't handle TIMEZONE, tm_isdst and leap seconds.
Moved shared code between mktime and gmtime into time_utils.cpp.
Reviewed By: sivachandra
Differential Revision: https://reviews.llvm.org/D98467
Added:
libc/src/time/gmtime.cpp
libc/src/time/gmtime.h
libc/src/time/time_utils.cpp
libc/test/src/time/gmtime_test.cpp
Modified:
libc/config/linux/api.td
libc/config/linux/x86_64/entrypoints.txt
libc/spec/stdc.td
libc/src/time/CMakeLists.txt
libc/src/time/mktime.cpp
libc/src/time/time_utils.h
libc/test/src/time/CMakeLists.txt
libc/test/src/time/mktime_test.cpp
Removed:
################################################################################
diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index fa7db68466de..c8dfd3b687c9 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -249,6 +249,7 @@ def TimeAPI : PublicAPI<"time.h"> {
];
let Functions = [
+ "gmtime",
"mktime",
];
}
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index a3fffae6e578..8dca7124ae49 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -176,6 +176,7 @@ if(LLVM_LIBC_FULL_BUILD)
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
index 9b332a8c1bbc..8afb37f86760 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -5,6 +5,7 @@ def StdC : StandardSpec<"stdc"> {
RestrictedPtrType FILERestrictedPtr = RestrictedPtrType<FILE>;
NamedType StructTmType = NamedType<"struct tm">;
PtrType StructTmPtr = PtrType<StructTmType>;
+ PtrType TimeTTypePtr = PtrType<TimeTType>;
HeaderSpec Assert = HeaderSpec<
"assert.h",
@@ -597,6 +598,11 @@ def StdC : StandardSpec<"stdc"> {
],
[], // Enumerations
[
+ FunctionSpec<
+ "gmtime",
+ RetValSpec<StructTmPtr>,
+ [ArgSpec<TimeTTypePtr>]
+ >,
FunctionSpec<
"mktime",
RetValSpec<TimeTType>,
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index a4aa97f12f5f..c11a658a8ccd 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -1,11 +1,32 @@
+add_object_library(
+ time_utils
+ SRCS
+ time_utils.cpp
+ HDRS
+ time_utils.h
+)
+
+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.cpp b/libc/src/time/gmtime.cpp
new file mode 100644
index 000000000000..04991a539d07
--- /dev/null
+++ b/libc/src/time/gmtime.cpp
@@ -0,0 +1,29 @@
+//===-- 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 <limits.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(struct tm *, gmtime, (const time_t *timer)) {
+ 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/gmtime.h b/libc/src/time/gmtime.h
new file mode 100644
index 000000000000..8891a8c917ac
--- /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 <time.h>
+
+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/mktime.cpp b/libc/src/time/mktime.cpp
index a352cc60dbe9..35970b1c910b 100644
--- a/libc/src/time/mktime.cpp
+++ b/libc/src/time/mktime.cpp
@@ -29,134 +29,6 @@ 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) {
- 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 * tm_out)) {
// Unlike most C Library functions, mktime doesn't just die on bad input.
// TODO(rtenneti); Handle leap seconds.
@@ -236,7 +108,7 @@ LLVM_LIBC_FUNCTION(time_t, mktime, (struct tm * tm_out)) {
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<time_t>(seconds);
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
new file mode 100644
index 000000000000..11ac2fb0d9a0
--- /dev/null
+++ b/libc/src/time/time_utils.cpp
@@ -0,0 +1,147 @@
+//===-- Implementation of mktime 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/time_utils.h"
+#include "src/__support/common.h"
+
+#include <limits.h>
+
+namespace __llvm_libc {
+namespace time_utils {
+
+using __llvm_libc::time_utils::TimeConstants;
+
+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;
+}
+
+// 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.
+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;
+ // TODO(rtenneti): Need to handle timezone and update of tm_isdst.
+ tm->tm_isdst = 0;
+
+ return 0;
+}
+
+} // namespace time_utils
+} // namespace __llvm_libc
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 48bbf7afca19..c87124e6e753 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -59,6 +59,10 @@ static inline time_t OutOfRange() {
return static_cast<time_t>(-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/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
index 2e34ac6d6c53..690cdce67115 100644
--- a/libc/test/src/time/CMakeLists.txt
+++ b/libc/test/src/time/CMakeLists.txt
@@ -5,9 +5,11 @@ add_libc_unittest(
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
index 000000000000..ba24d1f82aca
--- /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 <errno.h>
+#include <limits.h>
+#include <string.h>
+
+using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+using __llvm_libc::time_utils::TimeConstants;
+
+TEST(LlvmLibcGmTime, OutOfRange) {
+ time_t seconds = 1 + INT_MAX * static_cast<int64_t>(
+ 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<int64_t>(TimeConstants::NumberOfSecondsInLeapYear) -
+ 1;
+ tm_data = __llvm_libc::gmtime(&seconds);
+ EXPECT_TRUE(tm_data == nullptr);
+ EXPECT_EQ(llvmlibc_errno, EOVERFLOW);
+}
+
+TEST(LlvmLibcGmTime, InvalidSeconds) {
+ 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, InvalidMinutes) {
+ 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, InvalidHours) {
+ 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, InvalidYear) {
+ // -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, InvalidMonths) {
+ 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, InvalidDays) {
+ 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, EndOf32BitEpochYear) {
+ // 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, Max64BitYear) {
+ 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
index 3e76c586cba7..93290d3ec0a4 100644
--- a/libc/test/src/time/mktime_test.cpp
+++ b/libc/test/src/time/mktime_test.cpp
@@ -50,7 +50,7 @@ TEST(LlvmLibcMkTime, FailureSetsErrno) {
EXPECT_THAT(__llvm_libc::mktime(&tm_data), Fails(EOVERFLOW));
}
-TEST(LlvmLibcMkTime, MkTimesInvalidSeconds) {
+TEST(LlvmLibcMkTime, InvalidSeconds) {
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,
@@ -96,7 +96,7 @@ TEST(LlvmLibcMkTime, MkTimesInvalidSeconds) {
tm_data);
}
-TEST(LlvmLibcMkTime, MktimeTestsInvalidMinutes) {
+TEST(LlvmLibcMkTime, InvalidMinutes) {
struct tm tm_data;
// -1 minute from 1970-01-01 00:00:00 returns 1969-12-31 23:59:00.
EXPECT_THAT(call_mktime(&tm_data,
@@ -142,7 +142,7 @@ TEST(LlvmLibcMkTime, MktimeTestsInvalidMinutes) {
tm_data);
}
-TEST(LlvmLibcMkTime, MktimeTestsInvalidHours) {
+TEST(LlvmLibcMkTime, InvalidHours) {
struct tm tm_data;
// -1 hour from 1970-01-01 00:00:00 returns 1969-12-31 23:00:00.
EXPECT_THAT(call_mktime(&tm_data,
@@ -188,7 +188,7 @@ TEST(LlvmLibcMkTime, MktimeTestsInvalidHours) {
tm_data);
}
-TEST(LlvmLibcMkTime, MktimeTestsInvalidYear) {
+TEST(LlvmLibcMkTime, InvalidYear) {
struct tm tm_data;
// -1 year from 1970-01-01 00:00:00 returns 1969-01-01 00:00:00.
EXPECT_THAT(call_mktime(&tm_data,
@@ -214,7 +214,7 @@ TEST(LlvmLibcMkTime, MktimeTestsInvalidYear) {
tm_data);
}
-TEST(LlvmLibcMkTime, MktimeTestsInvalidEndOf32BitEpochYear) {
+TEST(LlvmLibcMkTime, InvalidEndOf32BitEpochYear) {
if (sizeof(size_t) != 4)
return;
struct tm tm_data;
@@ -238,7 +238,7 @@ TEST(LlvmLibcMkTime, MktimeTestsInvalidEndOf32BitEpochYear) {
Succeeds(TimeConstants::OutOfRangeReturnValue));
}
-TEST(LlvmLibcMkTime, MktimeTestsInvalidMonths) {
+TEST(LlvmLibcMkTime, InvalidMonths) {
struct tm tm_data;
// -1 month from 1970-01-01 00:00:00 returns 1969-12-01 00:00:00.
EXPECT_THAT(call_mktime(&tm_data,
@@ -285,7 +285,7 @@ TEST(LlvmLibcMkTime, MktimeTestsInvalidMonths) {
tm_data);
}
-TEST(LlvmLibcMkTime, MktimeTestsInvalidDays) {
+TEST(LlvmLibcMkTime, InvalidDays) {
struct tm tm_data;
// -1 day from 1970-01-01 00:00:00 returns 1969-12-31 00:00:00.
EXPECT_THAT(call_mktime(&tm_data,
@@ -377,7 +377,7 @@ TEST(LlvmLibcMkTime, MktimeTestsInvalidDays) {
tm_data);
}
-TEST(LlvmLibcMkTime, MktimeTestsEndOf32BitEpochYear) {
+TEST(LlvmLibcMkTime, EndOf32BitEpochYear) {
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.
@@ -403,7 +403,7 @@ TEST(LlvmLibcMkTime, MktimeTestsEndOf32BitEpochYear) {
tm_data);
}
-TEST(LlvmLibcMkTime, MktimeTests64BitYear) {
+TEST(LlvmLibcMkTime, Max64BitYear) {
if (sizeof(time_t) == 4)
return;
// Mon Jan 1 12:50:50 2170 (200 years from 1970),
More information about the libc-commits
mailing list