[libc-commits] [libc] 6f0f844 - Initial commit of mktime.

Raman Tenneti via libc-commits libc-commits at lists.llvm.org
Mon Nov 30 21:12:12 PST 2020


Author: Raman Tenneti
Date: 2020-11-30T21:07:16-08:00
New Revision: 6f0f844e9af98dc935f80d8149f6e4fcebddf8f1

URL: https://github.com/llvm/llvm-project/commit/6f0f844e9af98dc935f80d8149f6e4fcebddf8f1
DIFF: https://github.com/llvm/llvm-project/commit/6f0f844e9af98dc935f80d8149f6e4fcebddf8f1.diff

LOG: Initial commit of mktime.

This introduces mktime to LLVM libc, based on C99/C2X/Single Unix Spec.

Co-authored-by: Jeff Bailey <jeffbailey at google.com>

This change doesn't handle TIMEZONE,  tm_isdst and leap seconds.  It returns -1 for invalid dates. I have verified the return results for all the possible dates with glibc's mktime.

TODO:
+ Handle leap seconds.
+ Handle out of range time and date values that don't overflow or underflow.
+ 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.
+ Handle timezone and update of tm_isdst

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D91551

Added: 
    libc/include/time.h.def
    libc/src/time/CMakeLists.txt
    libc/src/time/mktime.cpp
    libc/src/time/mktime.h
    libc/test/src/time/CMakeLists.txt
    libc/test/src/time/mktime_test.cpp

Modified: 
    libc/config/linux/api.td
    libc/config/linux/x86_64/entrypoints.txt
    libc/config/linux/x86_64/headers.txt
    libc/include/CMakeLists.txt
    libc/spec/spec.td
    libc/spec/stdc.td
    libc/src/CMakeLists.txt
    libc/test/src/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 8415e8032d28..d66d55f65027 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -20,6 +20,28 @@ def SSizeT : TypeDecl<"ssize_t"> {
   }];
 }
 
+def StructTm: TypeDecl<"struct tm"> {
+  let Decl = [{
+    struct tm {
+      int tm_sec; // seconds after the minute
+      int tm_min; // minutes after the hour
+      int tm_hour; // hours since midnight
+      int tm_mday; // day of the month
+      int tm_mon; // months since January
+      int tm_year; // years since 1900
+      int tm_wday; // days since Sunday
+      int tm_yday; // days since January
+      int tm_isdst; // Daylight Saving Time flag
+    };
+  }];
+}
+
+def TimeT: TypeDecl<"time_t"> {
+   let Decl = [{
+   typedef long time_t;
+  }];
+}
+
 def OffT : TypeDecl<"off_t"> {
   let Decl = [{
     #define __need_off_t
@@ -177,6 +199,17 @@ def StdIOAPI : PublicAPI<"stdio.h"> {
 def StdlibAPI : PublicAPI<"stdlib.h"> {
 }
 
+def TimeAPI : PublicAPI<"time.h"> {
+  let TypeDeclarations = [
+    StructTm,
+    TimeT,
+  ];
+
+  let Functions = [
+    "mktime",
+  ];
+}
+
 def ErrnoAPI : PublicAPI<"errno.h"> {
   let Macros = [
     ErrnoMacro,

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index a0773ef0fce6..4eb964b2c047 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -67,6 +67,9 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.threads.thrd_create
     libc.src.threads.thrd_join
 
+    # time.h entrypoints
+    libc.src.time.mktime
+
     # unistd.h entrypoints
     libc.src.unistd.write
 )

diff  --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt
index 550f172c32c1..2d4cdb11cb8a 100644
--- a/libc/config/linux/x86_64/headers.txt
+++ b/libc/config/linux/x86_64/headers.txt
@@ -9,5 +9,6 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.sys_mman
     libc.include.sys_syscall
     libc.include.threads
+    libc.include.time
     libc.include.unistd
 )

diff  --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index f676342a134e..ddfdb8caa2ac 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -50,6 +50,14 @@ add_gen_header(
     .llvm_libc_common_h
 )
 
+add_gen_header(
+  time
+  DEF_FILE time.h.def
+  GEN_HDR time.h
+  DEPENDS
+    .llvm_libc_common_h
+)
+
 add_gen_header(
   threads
   DEF_FILE threads.h.def

diff  --git a/libc/include/time.h.def b/libc/include/time.h.def
new file mode 100644
index 000000000000..0c90f9b76c2e
--- /dev/null
+++ b/libc/include/time.h.def
@@ -0,0 +1,16 @@
+//===-- C standard library header time.h ----------------------------------===//
+//
+// 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_TIME_H
+#define LLVM_LIBC_TIME_H
+
+#include <__llvm-libc-common.h>
+
+%%public_api()
+
+#endif // LLVM_LIBC_TIME_H

diff  --git a/libc/spec/spec.td b/libc/spec/spec.td
index 35d51e206b09..29c11a9c2199 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -79,6 +79,8 @@ def DoublePtr : PtrType<DoubleType>;
 
 def SigHandlerT : NamedType<"__sighandler_t">;
 
+def TimeTType : NamedType<"time_t">;
+
 //added because __assert_fail needs it.
 def UnsignedType : NamedType<"unsigned">;
 

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 854566ca544b..7275f1e0aacf 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -3,6 +3,8 @@ def StdC : StandardSpec<"stdc"> {
   NamedType FILE = NamedType<"FILE">;
   PtrType FILEPtr = PtrType<FILE>;
   RestrictedPtrType FILERestrictedPtr = RestrictedPtrType<FILE>;
+  NamedType StructTmType = NamedType<"struct tm">;
+  PtrType StructTmPtr = PtrType<StructTmType>;
 
   HeaderSpec Assert = HeaderSpec<
       "assert.h",
@@ -467,6 +469,23 @@ def StdC : StandardSpec<"stdc"> {
       ]
   >;
 
+  HeaderSpec Time = HeaderSpec<
+      "time.h",
+      [], // Macros
+      [ // Types
+       StructTmType,
+       TimeTType,
+      ],
+      [], // Enumerations
+      [
+          FunctionSpec<
+              "mktime",
+              RetValSpec<TimeTType>,
+              [ArgSpec<StructTmPtr>]
+          >,
+      ]
+  >;
+
   let Headers = [
     Assert,
     CType,
@@ -477,5 +496,6 @@ def StdC : StandardSpec<"stdc"> {
     StdLib,
     Signal,
     Threads,
+    Time,
   ];
 }

diff  --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 9e5b06f201ce..11213e90e579 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -9,6 +9,7 @@ add_subdirectory(string)
 # TODO: Add this target conditional to the target OS.
 add_subdirectory(sys)
 add_subdirectory(threads)
+add_subdirectory(time)
 add_subdirectory(unistd)
 
 add_subdirectory(__support)

diff  --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
new file mode 100644
index 000000000000..dc2d95717d89
--- /dev/null
+++ b/libc/src/time/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_entrypoint_object(
+  mktime
+  SRCS
+    mktime.cpp
+  HDRS
+    mktime.h
+  DEPENDS
+    libc.include.errno
+    libc.include.time
+    libc.src.errno.__errno_location
+)

diff  --git a/libc/src/time/mktime.cpp b/libc/src/time/mktime.cpp
new file mode 100644
index 000000000000..418797158968
--- /dev/null
+++ b/libc/src/time/mktime.cpp
@@ -0,0 +1,126 @@
+//===-- 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 "include/errno.h"
+
+#include "src/__support/common.h"
+#include "src/errno/llvmlibc_errno.h"
+#include "src/time/mktime.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) {
+  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);
+}
+
+time_t LLVM_LIBC_ENTRYPOINT(mktime)(struct tm *t1) {
+  // 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();
+
+  // 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();
+  }
+
+  // Years are ints.  A 32-bit year will fit into a 64-bit time_t.
+  // A 64-bit year will not.
+  static_assert(sizeof(int) == 4,
+                "ILP64 is unimplemented.  This implementation requires "
+                "32-bit integers.");
+
+  if (t1->tm_mon < 0 || t1->tm_mon > (MonthsPerYear - 1))
+    return outOfRange();
+  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];
+  // Add one day if it is a leap year and the month is after February.
+  if (tmYearIsLeap && t1->tm_mon > 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++;
+    }
+  }
+
+  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;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/time/mktime.h b/libc/src/time/mktime.h
new file mode 100644
index 000000000000..ecd604c12fc3
--- /dev/null
+++ b/libc/src/time/mktime.h
@@ -0,0 +1,23 @@
+//===-- Implementation header of mktime -------------------------*- 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_MKTIME_H
+#define LLVM_LIBC_SRC_TIME_MKTIME_H
+
+#include "src/time/mktime.h"
+#include <time.h>
+
+namespace __llvm_libc {
+
+time_t mktime(struct tm *t1);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_TIME_MKTIME_H
+
+#include "include/time.h"

diff  --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index aa606ae630bc..026e7eab8aef 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -8,6 +8,7 @@ add_subdirectory(stdlib)
 add_subdirectory(string)
 add_subdirectory(sys)
 add_subdirectory(threads)
+add_subdirectory(time)
 add_subdirectory(unistd)
 
 set(public_test ${CMAKE_CURRENT_BINARY_DIR}/public_integration_test.cpp)

diff  --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
new file mode 100644
index 000000000000..c466af4214e2
--- /dev/null
+++ b/libc/test/src/time/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_libc_testsuite(libc_time_unittests)
+
+add_libc_unittest(
+  mktime
+  SUITE
+    libc_time_unittests
+  SRCS
+    mktime_test.cpp
+  DEPENDS
+    libc.src.time.mktime
+)

diff  --git a/libc/test/src/time/mktime_test.cpp b/libc/test/src/time/mktime_test.cpp
new file mode 100644
index 000000000000..1e4b60a3bb17
--- /dev/null
+++ b/libc/test/src/time/mktime_test.cpp
@@ -0,0 +1,157 @@
+//===-- Unittests for mktime ----------------------------------------------===//
+//
+// 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/mktime.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+#include <string.h>
+
+using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+
+static constexpr time_t OutOfRangeReturnValue = -1;
+
+// 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) {
+  struct tm temp = {.tm_sec = sec,
+                    .tm_min = min,
+                    .tm_hour = hour,
+                    .tm_mday = mday,
+                    .tm_mon = month,
+                    .tm_year = year - 1900};
+  *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);
+  return __llvm_libc::mktime(tm_data);
+}
+
+TEST(MkTime, FailureSetsErrno) {
+  struct tm tm_data;
+  initialize_tm_data(&tm_data, 0, 0, 0, 0, 0, -1);
+  EXPECT_THAT(__llvm_libc::mktime(&tm_data), Fails(EOVERFLOW));
+}
+
+TEST(MkTime, MktimeTestsInvalidSeconds) {
+  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);
+}
+
+TEST(MkTime, 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);
+}
+
+TEST(MkTime, 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);
+}
+
+TEST(MkTime, MktimeTestsInvalidYear) {
+  struct tm tm_data;
+  EXPECT_EQ(call_mktime(&tm_data, 1969, 0, 0, 0, 0, 0), OutOfRangeReturnValue);
+}
+
+TEST(MkTime, MktimeTestsInvalidEndOf32BitEpochYear) {
+  if (sizeof(time_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);
+  // 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);
+  // 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);
+  // 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);
+  // 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);
+  // 2039-01-19 03:14:07 tests overflow of the year.
+  EXPECT_EQ(call_mktime(&tm_data, 2039, 0, 19, 3, 14, 7),
+            OutOfRangeReturnValue);
+}
+
+TEST(MkTime, 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);
+}
+
+TEST(MkTime, 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);
+}
+
+TEST(MkTime, 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);
+}
+
+TEST(MkTime, 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);
+}
+
+TEST(MkTime, 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);
+}
+
+TEST(MkTime, MktimeTests64BitYear) {
+  if (sizeof(time_t) == 4)
+    return;
+  // Mon Jan 1 12:50:50 2170
+  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);
+
+  // 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);
+}


        


More information about the libc-commits mailing list