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

GitOrigin-RevId: eaae52c1fd459f9c0a147361bc3b962238faba5c
diff --git a/config/linux/api.td b/config/linux/api.td
index fa7db68..c8dfd3b 100644
--- a/config/linux/api.td
+++ b/config/linux/api.td
@@ -249,6 +249,7 @@
   ];
 
   let Functions = [
+    "gmtime",
     "mktime",
   ];
 }
diff --git a/config/linux/x86_64/entrypoints.txt b/config/linux/x86_64/entrypoints.txt
index a3fffae..8dca712 100644
--- a/config/linux/x86_64/entrypoints.txt
+++ b/config/linux/x86_64/entrypoints.txt
@@ -176,6 +176,7 @@
     libc.src.threads.thrd_join
 
     # time.h entrypoints
+    libc.src.time.gmtime
     libc.src.time.mktime
 
     # unistd.h entrypoints
diff --git a/spec/stdc.td b/spec/stdc.td
index 9b332a8..8afb37f 100644
--- a/spec/stdc.td
+++ b/spec/stdc.td
@@ -5,6 +5,7 @@
   RestrictedPtrType FILERestrictedPtr = RestrictedPtrType<FILE>;
   NamedType StructTmType = NamedType<"struct tm">;
   PtrType StructTmPtr = PtrType<StructTmType>;
+  PtrType TimeTTypePtr = PtrType<TimeTType>;
 
   HeaderSpec Assert = HeaderSpec<
       "assert.h",
@@ -598,6 +599,11 @@
       [], // Enumerations
       [
           FunctionSpec<
+              "gmtime",
+              RetValSpec<StructTmPtr>,
+              [ArgSpec<TimeTTypePtr>]
+          >,
+          FunctionSpec<
               "mktime",
               RetValSpec<TimeTType>,
               [ArgSpec<StructTmPtr>]
diff --git a/src/time/CMakeLists.txt b/src/time/CMakeLists.txt
index a4aa97f..c11a658 100644
--- a/src/time/CMakeLists.txt
+++ b/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/src/time/gmtime.cpp b/src/time/gmtime.cpp
new file mode 100644
index 0000000..04991a5
--- /dev/null
+++ b/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/src/time/gmtime.h b/src/time/gmtime.h
new file mode 100644
index 0000000..8891a8c
--- /dev/null
+++ b/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/src/time/mktime.cpp b/src/time/mktime.cpp
index a352cc6..35970b1 100644
--- a/src/time/mktime.cpp
+++ b/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<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 @@
                     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/src/time/time_utils.cpp b/src/time/time_utils.cpp
new file mode 100644
index 0000000..11ac2fb
--- /dev/null
+++ b/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/src/time/time_utils.h b/src/time/time_utils.h
index 48bbf7a..c87124e 100644
--- a/src/time/time_utils.h
+++ b/src/time/time_utils.h
@@ -59,6 +59,10 @@
   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/test/src/time/CMakeLists.txt b/test/src/time/CMakeLists.txt
index 2e34ac6..690cdce 100644
--- a/test/src/time/CMakeLists.txt
+++ b/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/test/src/time/gmtime_test.cpp b/test/src/time/gmtime_test.cpp
new file mode 100644
index 0000000..ba24d1f
--- /dev/null
+++ b/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/test/src/time/mktime_test.cpp b/test/src/time/mktime_test.cpp
index 3e76c58..93290d3 100644
--- a/test/src/time/mktime_test.cpp
+++ b/test/src/time/mktime_test.cpp
@@ -50,7 +50,7 @@
   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 @@
                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 @@
                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 @@
                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 @@
                tm_data);
 }
 
-TEST(LlvmLibcMkTime, MktimeTestsInvalidEndOf32BitEpochYear) {
+TEST(LlvmLibcMkTime, InvalidEndOf32BitEpochYear) {
   if (sizeof(size_t) != 4)
     return;
   struct tm tm_data;
@@ -238,7 +238,7 @@
               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 @@
                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 @@
                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 @@
                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),