[libc] implement template functions for localtime (#110363)
This is an implementation for template functions of localtime.
Update for this pull request: Implementation as been removed from this
pull request and will be added to a new one. This is because this pull
request is getting big. This pull request will only contain template
functions in order to implement localtime.
Update: The implementation is available in
https://github.com/zimirza/llvm-project/tree/localtime_implementation.
---------
Co-authored-by: Зишан Мирза <zmirza@tutanota.de>
Co-authored-by: Zishan Mirza <zmirza@posteo.de>
GitOrigin-RevId: 17bddd12245324311d10a681d606961914174c88
diff --git a/config/baremetal/aarch64/entrypoints.txt b/config/baremetal/aarch64/entrypoints.txt
index 782769e..04bf636 100644
--- a/config/baremetal/aarch64/entrypoints.txt
+++ b/config/baremetal/aarch64/entrypoints.txt
@@ -269,6 +269,8 @@
libc.src.time.difftime
libc.src.time.gmtime
libc.src.time.gmtime_r
+ libc.src.time.localtime
+ libc.src.time.localtime_r
libc.src.time.mktime
libc.src.time.strftime
libc.src.time.strftime_l
diff --git a/config/linux/x86_64/entrypoints.txt b/config/linux/x86_64/entrypoints.txt
index 9a73e18..35425eb 100644
--- a/config/linux/x86_64/entrypoints.txt
+++ b/config/linux/x86_64/entrypoints.txt
@@ -1307,6 +1307,8 @@
libc.src.time.gettimeofday
libc.src.time.gmtime
libc.src.time.gmtime_r
+ libc.src.time.localtime
+ libc.src.time.localtime_r
libc.src.time.mktime
libc.src.time.nanosleep
libc.src.time.strftime
diff --git a/docs/headers/time.rst b/docs/headers/time.rst
index 9733a17..a30d489 100644
--- a/docs/headers/time.rst
+++ b/docs/headers/time.rst
@@ -87,9 +87,9 @@
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| gmtime_r | |check| | |check| | | |check| | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
-| localtime | | | | | | | | | | | | | |
+| localtime | |check| | |check| | | |check| | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
-| localtime_r | | | | | | | | | | | | | |
+| localtime_r | |check| | |check| | | |check| | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| mktime | |check| | |check| | | |check| | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
@@ -112,4 +112,4 @@
| timer_settime | | | | | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| tzset | | | | | | | | | | | | | |
-+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
\ No newline at end of file
++---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
diff --git a/hdr/localtime_overlay.h b/hdr/localtime_overlay.h
new file mode 100644
index 0000000..8628277
--- /dev/null
+++ b/hdr/localtime_overlay.h
@@ -0,0 +1,45 @@
+//===-- Including localtime.h in overlay mode -----------------------------===//
+//
+// 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_HDR_LOCALTIME_OVERLAY_H
+#define LLVM_LIBC_HDR_LOCALTIME_OVERLAY_H
+
+#ifdef LIBC_FULL_BUILD
+#error "This header should only be included in overlay mode"
+#endif
+
+// Overlay mode
+
+// glibc <unistd.h> header might provide extern inline definitions for few
+// functions, causing external alias errors. They are guarded by
+// `__USE_EXTERN_INLINES` macro.
+
+#ifdef __USE_EXTERN_INLINES
+#define LIBC_OLD_USE_EXTERN_INLINES
+#undef __USE_EXTERN_INLINES
+#endif
+
+#ifndef __NO_INLINE__
+#define __NO_INLINE__ 1
+#define LIBC_SET_NO_INLINE
+#endif
+
+#include <localtime.h>
+#include <localtime_r.h>
+
+#ifdef LIBC_SET_NO_INLINE
+#undef __NO_INLINE__
+#undef LIBC_SET_NO_INLINE
+#endif
+
+#ifdef LIBC_OLD_USE_EXTERN_INLINES
+#define __USE_EXTERN_INLINES
+#undef LIBC_OLD_USE_EXTERN_INLINES
+#endif
+
+#endif // LLVM_LIBC_HDR_LOCALTIME_OVERLAY_H
diff --git a/include/time.yaml b/include/time.yaml
index 3b9d77c..2f80242 100644
--- a/include/time.yaml
+++ b/include/time.yaml
@@ -41,6 +41,19 @@
arguments:
- type: const time_t *
- type: char *
+ - name: localtime
+ standard:
+ - stdc
+ return_type: struct tm *
+ arguments:
+ - type: const time_t *
+ - name: localtime_r
+ standard:
+ - stdc
+ return_type: struct tm *
+ arguments:
+ - type: const time_t *
+ - type: struct tm *
- name: clock
standard:
- stdc
diff --git a/src/time/CMakeLists.txt b/src/time/CMakeLists.txt
index 304b3f2..ec942e3 100644
--- a/src/time/CMakeLists.txt
+++ b/src/time/CMakeLists.txt
@@ -86,6 +86,30 @@
)
add_entrypoint_object(
+ localtime
+ SRCS
+ localtime.cpp
+ HDRS
+ localtime.h
+ DEPENDS
+ .time_utils
+ libc.hdr.types.time_t
+ libc.hdr.types.struct_tm
+)
+
+add_entrypoint_object(
+ localtime_r
+ SRCS
+ localtime_r.cpp
+ HDRS
+ localtime_r.h
+ DEPENDS
+ .time_utils
+ libc.hdr.types.time_t
+ libc.hdr.types.struct_tm
+)
+
+add_entrypoint_object(
difftime
SRCS
difftime.cpp
diff --git a/src/time/baremetal/CMakeLists.txt b/src/time/baremetal/CMakeLists.txt
index 3072c8b..cbe9cf3 100644
--- a/src/time/baremetal/CMakeLists.txt
+++ b/src/time/baremetal/CMakeLists.txt
@@ -19,3 +19,29 @@
libc.hdr.time_macros
libc.hdr.types.struct_timespec
)
+
+add_entrypoint_object(
+ localtime
+ SRCS
+ localtime.cpp
+ HDRS
+ ../localtime.h
+ time_utils.h
+ DEPENDS
+ .time_utils
+ libc.hdr.types.struct_tm
+ libc.hdr.types.time_t
+)
+
+add_entrypoint_object(
+ localtime_r
+ SRCS
+ localtime_r.cpp
+ HDRS
+ ../localtime.h
+ time_utils.h
+ DEPENDS
+ .time_utils
+ libc.hdr.types.struct_tm
+ libc.hdr.types.time_t
+)
diff --git a/src/time/baremetal/localtime.cpp b/src/time/baremetal/localtime.cpp
new file mode 100644
index 0000000..d39c273
--- /dev/null
+++ b/src/time/baremetal/localtime.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of localtime for baremetal -------------------------===//
+//
+// 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/localtime.h"
+#include "hdr/types/struct_tm.h"
+#include "hdr/types/time_t.h"
+#include "src/time/time_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(struct tm *, localtime, (time_t *timer)) {
+ static struct tm tm_out;
+
+ return time_utils::localtime_internal(timer, &tm_out);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/time/baremetal/localtime_r.cpp b/src/time/baremetal/localtime_r.cpp
new file mode 100644
index 0000000..3b57450
--- /dev/null
+++ b/src/time/baremetal/localtime_r.cpp
@@ -0,0 +1,24 @@
+//===-- Implementation of localtime_r for baremetal -----------------------===//
+//
+// 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/localtime_r.h"
+#include "hdr/types/struct_tm.h"
+#include "hdr/types/time_t.h"
+#include "src/__support/macros/null_check.h"
+#include "src/time/time_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(struct tm *, localtime_r,
+ (const time_t *timer, struct tm *buf)) {
+ LIBC_CRASH_ON_NULLPTR(timer);
+
+ return time_utils::localtime_internal(timer, buf);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/time/localtime.cpp b/src/time/localtime.cpp
new file mode 100644
index 0000000..90a2961
--- /dev/null
+++ b/src/time/localtime.cpp
@@ -0,0 +1,24 @@
+//===-- Linux implementation of the localtime 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/localtime.h"
+#include "hdr/types/struct_tm.h"
+#include "hdr/types/time_t.h"
+#include "src/__support/macros/null_check.h"
+#include "src/time/time_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(struct tm *, localtime, (const time_t *timer)) {
+ LIBC_CRASH_ON_NULLPTR(timer);
+
+ static struct tm tm_out;
+ return time_utils::localtime_internal(timer, &tm_out);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/time/localtime.h b/src/time/localtime.h
new file mode 100644
index 0000000..663aa70
--- /dev/null
+++ b/src/time/localtime.h
@@ -0,0 +1,22 @@
+//===-- Implementation header of localtime ----------------------*- 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_LOCALTIME_H
+#define LLVM_LIBC_SRC_TIME_LOCALTIME_H
+
+#include "hdr/types/struct_tm.h"
+#include "hdr/types/time_t.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct tm *localtime(const time_t *timer);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_LOCALTIME_H
diff --git a/src/time/localtime_r.cpp b/src/time/localtime_r.cpp
new file mode 100644
index 0000000..70bbbee
--- /dev/null
+++ b/src/time/localtime_r.cpp
@@ -0,0 +1,25 @@
+//===-- Linux implementation of localtime_r 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/localtime_r.h"
+#include "hdr/types/struct_tm.h"
+#include "hdr/types/time_t.h"
+#include "src/__support/macros/null_check.h"
+#include "src/time/time_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(struct tm *, localtime_r,
+ (const time_t *timer, struct tm *buf)) {
+ LIBC_CRASH_ON_NULLPTR(timer);
+ LIBC_CRASH_ON_NULLPTR(buf);
+
+ return time_utils::localtime_internal(timer, buf);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/time/localtime_r.h b/src/time/localtime_r.h
new file mode 100644
index 0000000..6fea402
--- /dev/null
+++ b/src/time/localtime_r.h
@@ -0,0 +1,22 @@
+//===-- Implementation header of localtime_r --------------------*- 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_LOCALTIME_R_H
+#define LLVM_LIBC_SRC_TIME_LOCALTIME_R_H
+
+#include "hdr/types/struct_tm.h"
+#include "hdr/types/time_t.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct tm *localtime_r(const time_t *timer, struct tm *buf);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_LOCALTIME_R_H
diff --git a/src/time/time_utils.h b/src/time/time_utils.h
index 84d412c..18dc287 100644
--- a/src/time/time_utils.h
+++ b/src/time/time_utils.h
@@ -93,11 +93,22 @@
return result;
}
-// TODO: localtime is not yet implemented and a temporary solution is to
-// use gmtime, https://github.com/llvm/llvm-project/issues/107597
+LIBC_INLINE tm *localtime_internal(const time_t *timer, tm *result) {
+ time_t seconds = *timer;
+ // Update the tm structure's year, month, day, etc. from seconds.
+ if (update_from_seconds(seconds, result) < 0) {
+ out_of_range();
+ return nullptr;
+ }
+
+ // TODO(zimirza): implement timezone database
+
+ return result;
+}
+
LIBC_INLINE tm *localtime(const time_t *t_ptr) {
static tm result;
- return time_utils::gmtime_internal(t_ptr, &result);
+ return time_utils::localtime_internal(t_ptr, &result);
}
// Returns number of years from (1, year).
diff --git a/test/src/time/CMakeLists.txt b/test/src/time/CMakeLists.txt
index be7aa6f..66753b8 100644
--- a/test/src/time/CMakeLists.txt
+++ b/test/src/time/CMakeLists.txt
@@ -72,6 +72,29 @@
libc.hdr.types.struct_tm
)
+add_libc_unittest(
+ localtime_test
+ SUITE
+ libc_time_unittests
+ SRCS
+ localtime_test.cpp
+ DEPENDS
+ libc.hdr.types.time_t
+ libc.src.time.localtime
+)
+
+add_libc_unittest(
+ localtime_r_test
+ SUITE
+ libc_time_unittests
+ SRCS
+ localtime_r_test.cpp
+ DEPENDS
+ libc.hdr.types.struct_tm
+ libc.hdr.types.time_t
+ libc.src.time.localtime_r
+)
+
add_libc_test(
clock_gettime_test
SUITE
diff --git a/test/src/time/localtime_r_test.cpp b/test/src/time/localtime_r_test.cpp
new file mode 100644
index 0000000..8f7a79e
--- /dev/null
+++ b/test/src/time/localtime_r_test.cpp
@@ -0,0 +1,93 @@
+//===-- Unittests for localtime_r -----------------------------------------===//
+//
+// 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/localtime_r.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
+ struct tm input = {.tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 0,
+ .tm_mon = 0,
+ .tm_year = 0,
+ .tm_wday = 0,
+ .tm_yday = 0,
+ .tm_isdst = 0};
+ const time_t timer = 0;
+
+ struct tm *result = LIBC_NAMESPACE::localtime_r(&timer, &input);
+
+ ASSERT_EQ(70, result->tm_year);
+ ASSERT_EQ(0, result->tm_mon);
+ ASSERT_EQ(1, result->tm_mday);
+ ASSERT_EQ(0, result->tm_hour);
+ ASSERT_EQ(0, result->tm_min);
+ ASSERT_EQ(0, result->tm_sec);
+ ASSERT_EQ(4, result->tm_wday);
+ ASSERT_EQ(0, result->tm_yday);
+ ASSERT_EQ(0, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltime, NullPtr) {
+ EXPECT_DEATH([] { LIBC_NAMESPACE::localtime_r(nullptr, nullptr); },
+ WITH_SIGNAL(4));
+}
+
+// TODO(zimirza): These tests does not expect the correct output of localtime as
+// per specification. This is due to timezone functions removed from
+// https://github.com/llvm/llvm-project/pull/110363.
+// This will be resolved a new pull request.
+
+TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp) {
+ struct tm input = {.tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 0,
+ .tm_mon = 0,
+ .tm_year = 0,
+ .tm_wday = 0,
+ .tm_yday = 0,
+ .tm_isdst = 0};
+ const time_t timer = 1756595338;
+ struct tm *result = LIBC_NAMESPACE::localtime_r(&timer, &input);
+
+ ASSERT_EQ(125, result->tm_year);
+ ASSERT_EQ(7, result->tm_mon);
+ ASSERT_EQ(30, result->tm_mday);
+ ASSERT_EQ(23, result->tm_hour);
+ ASSERT_EQ(8, result->tm_min);
+ ASSERT_EQ(58, result->tm_sec);
+ ASSERT_EQ(6, result->tm_wday);
+ ASSERT_EQ(241, result->tm_yday);
+ ASSERT_EQ(0, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltimeR, ValidUnixTimestampNegative) {
+ struct tm input = {.tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 0,
+ .tm_mon = 0,
+ .tm_year = 0,
+ .tm_wday = 0,
+ .tm_yday = 0,
+ .tm_isdst = 0};
+ const time_t timer = -1756595338;
+ struct tm *result = LIBC_NAMESPACE::localtime_r(&timer, &input);
+
+ ASSERT_EQ(14, result->tm_year);
+ ASSERT_EQ(4, result->tm_mon);
+ ASSERT_EQ(4, result->tm_mday);
+ ASSERT_EQ(0, result->tm_hour);
+ ASSERT_EQ(51, result->tm_min);
+ ASSERT_EQ(2, result->tm_sec);
+ ASSERT_EQ(1, result->tm_wday);
+ ASSERT_EQ(123, result->tm_yday);
+ ASSERT_EQ(0, result->tm_isdst);
+}
diff --git a/test/src/time/localtime_test.cpp b/test/src/time/localtime_test.cpp
new file mode 100644
index 0000000..144060c
--- /dev/null
+++ b/test/src/time/localtime_test.cpp
@@ -0,0 +1,64 @@
+//===-- Unittests for localtime -------------------------------------------===//
+//
+// 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/localtime.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestamp0) {
+ const time_t timer = 0;
+ struct tm *result = LIBC_NAMESPACE::localtime(&timer);
+
+ ASSERT_EQ(70, result->tm_year);
+ ASSERT_EQ(0, result->tm_mon);
+ ASSERT_EQ(1, result->tm_mday);
+ ASSERT_EQ(0, result->tm_hour);
+ ASSERT_EQ(0, result->tm_min);
+ ASSERT_EQ(0, result->tm_sec);
+ ASSERT_EQ(4, result->tm_wday);
+ ASSERT_EQ(0, result->tm_yday);
+ ASSERT_EQ(0, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltime, NullPtr) {
+ EXPECT_DEATH([] { LIBC_NAMESPACE::localtime(nullptr); }, WITH_SIGNAL(4));
+}
+
+// TODO(zimirza): These tests does not expect the correct output of localtime as
+// per specification. This is due to timezone functions removed from
+// https://github.com/llvm/llvm-project/pull/110363.
+// This will be resolved a new pull request.
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestamp) {
+ const time_t timer = 1756595338;
+ struct tm *result = LIBC_NAMESPACE::localtime(&timer);
+
+ ASSERT_EQ(125, result->tm_year);
+ ASSERT_EQ(7, result->tm_mon);
+ ASSERT_EQ(30, result->tm_mday);
+ ASSERT_EQ(23, result->tm_hour);
+ ASSERT_EQ(8, result->tm_min);
+ ASSERT_EQ(58, result->tm_sec);
+ ASSERT_EQ(6, result->tm_wday);
+ ASSERT_EQ(241, result->tm_yday);
+ ASSERT_EQ(0, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestampNegative) {
+ const time_t timer = -1756595338;
+ struct tm *result = LIBC_NAMESPACE::localtime(&timer);
+
+ ASSERT_EQ(14, result->tm_year);
+ ASSERT_EQ(4, result->tm_mon);
+ ASSERT_EQ(4, result->tm_mday);
+ ASSERT_EQ(0, result->tm_hour);
+ ASSERT_EQ(51, result->tm_min);
+ ASSERT_EQ(2, result->tm_sec);
+ ASSERT_EQ(1, result->tm_wday);
+ ASSERT_EQ(123, result->tm_yday);
+ ASSERT_EQ(0, result->tm_isdst);
+}