[libc-commits] [libc] [libc] implement localtime (PR #110363)
Зишан Мирза via libc-commits
libc-commits at lists.llvm.org
Tue Oct 8 06:37:53 PDT 2024
https://github.com/zimirza updated https://github.com/llvm/llvm-project/pull/110363
>From 41b22383d62bccc5621435c6b117f7446b0cac99 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sat, 28 Sep 2024 15:50:29 +0200
Subject: [PATCH 01/27] [libc] implement localtime
This is an implementation of localtime.
Closes #107597 and #109892
---
libc/config/baremetal/arm/entrypoints.txt | 2 +
libc/config/baremetal/riscv/entrypoints.txt | 2 +
libc/config/linux/aarch64/entrypoints.txt | 2 +
libc/config/linux/riscv/entrypoints.txt | 2 +
libc/config/linux/x86_64/entrypoints.txt | 2 +
libc/newhdrgen/yaml/time.yaml | 13 +++
libc/spec/stdc.td | 13 +++
libc/src/time/CMakeLists.txt | 24 ++++++
libc/src/time/localtime.cpp | 25 ++++++
libc/src/time/localtime.h | 21 +++++
libc/src/time/localtime_r.cpp | 25 ++++++
libc/src/time/localtime_r.h | 21 +++++
libc/src/time/time_utils.cpp | 45 ++++++++++-
libc/src/time/time_utils.h | 35 ++++++++-
libc/test/src/time/CMakeLists.txt | 36 +++++++++
libc/test/src/time/ctime_r_test.cpp | 4 +-
libc/test/src/time/ctime_test.cpp | 4 +-
libc/test/src/time/localtime_r_test.cpp | 87 +++++++++++++++++++++
libc/test/src/time/localtime_test.cpp | 54 +++++++++++++
19 files changed, 408 insertions(+), 9 deletions(-)
create mode 100644 libc/src/time/localtime.cpp
create mode 100644 libc/src/time/localtime.h
create mode 100644 libc/src/time/localtime_r.cpp
create mode 100644 libc/src/time/localtime_r.h
create mode 100644 libc/test/src/time/localtime_r_test.cpp
create mode 100644 libc/test/src/time/localtime_test.cpp
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 68030f7f1775b5..7577fad2fde869 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -205,6 +205,8 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.time.asctime_r
libc.src.time.ctime
libc.src.time.ctime_r
+ libc.src.time.localtime
+ libc.src.time.localtime_r
libc.src.time.difftime
libc.src.time.gmtime
libc.src.time.gmtime_r
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index 5894b591072ef0..b93aa334296ffb 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -201,6 +201,8 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.time.asctime_r
libc.src.time.ctime
libc.src.time.ctime_r
+ libc.src.time.localtime
+ libc.src.time.localtime_r
libc.src.time.difftime
libc.src.time.gmtime
libc.src.time.gmtime_r
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 64fbe1a250c0ba..d287bb0510a1fb 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -950,6 +950,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.asctime_r
libc.src.time.ctime
libc.src.time.ctime_r
+ libc.src.time.localtime
+ libc.src.time.localtime_r
libc.src.time.clock
libc.src.time.clock_gettime
libc.src.time.difftime
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index ff3d821c664c5b..40a417bcf09b49 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -885,6 +885,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.asctime_r
libc.src.time.ctime
libc.src.time.ctime_r
+ libc.src.time.localtime
+ libc.src.time.localtime_r
libc.src.time.clock
libc.src.time.clock_gettime
libc.src.time.difftime
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index dd658af3bfb674..3b63ce82417f4b 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1005,6 +1005,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.asctime_r
libc.src.time.ctime
libc.src.time.ctime_r
+ libc.src.time.localtime
+ libc.src.time.localtime_r
libc.src.time.clock
libc.src.time.clock_gettime
libc.src.time.difftime
diff --git a/libc/newhdrgen/yaml/time.yaml b/libc/newhdrgen/yaml/time.yaml
index 69b40bef3160dd..7198c19fce2bf0 100644
--- a/libc/newhdrgen/yaml/time.yaml
+++ b/libc/newhdrgen/yaml/time.yaml
@@ -37,6 +37,19 @@ functions:
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/libc/spec/stdc.td b/libc/spec/stdc.td
index 7caf543748151a..9fd8d175f36be3 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -1615,6 +1615,19 @@ def StdC : StandardSpec<"stdc"> {
ArgSpec<CharPtr>,
]
>,
+ FunctionSpec<
+ "localtime",
+ RetValSpec<StructTmPtr>,
+ [ArgSpec<TimeTTypePtr>]
+ >,
+ FunctionSpec<
+ "localtime_r",
+ RetValSpec<StructTmPtr>,
+ [
+ ArgSpec<TimeTTypePtr>,
+ ArgSpec<StructTmPtr>,
+ ]
+ >,
FunctionSpec<
"clock",
RetValSpec<ClockT>,
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index b3318e7ca87fa5..de2e128dd310c4 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -60,6 +60,30 @@ add_entrypoint_object(
libc.include.time
)
+add_entrypoint_object(
+ localtime
+ SRCS
+ localtime.cpp
+ HDRS
+ localtime.h
+ DEPENDS
+ .time_utils
+ libc.hdr.types.time_t
+ libc.include.time
+)
+
+add_entrypoint_object(
+ localtime_r
+ SRCS
+ localtime_r.cpp
+ HDRS
+ localtime_r.h
+ DEPENDS
+ .time_utils
+ libc.hdr.types.time_t
+ libc.include.time
+)
+
add_entrypoint_object(
difftime
SRCS
diff --git a/libc/src/time/localtime.cpp b/libc/src/time/localtime.cpp
new file mode 100644
index 00000000000000..c08af2d8b26b78
--- /dev/null
+++ b/libc/src/time/localtime.cpp
@@ -0,0 +1,25 @@
+//===-- Implementation of 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 "src/__support/CPP/limits.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/time/time_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(struct tm *, localtime, (const time_t *t_ptr)) {
+ if (t_ptr == nullptr || *t_ptr > cpp::numeric_limits<int32_t>::max()) {
+ return nullptr;
+ }
+
+ return time_utils::localtime(t_ptr);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/localtime.h b/libc/src/time/localtime.h
new file mode 100644
index 00000000000000..648c8b755ddee9
--- /dev/null
+++ b/libc/src/time/localtime.h
@@ -0,0 +1,21 @@
+//===-- 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 "src/__support/macros/config.h"
+#include <time.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct tm *localtime(const time_t *t_ptr);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_LOCALTIME_H
diff --git a/libc/src/time/localtime_r.cpp b/libc/src/time/localtime_r.cpp
new file mode 100644
index 00000000000000..6ff3fb3d4faa8c
--- /dev/null
+++ b/libc/src/time/localtime_r.cpp
@@ -0,0 +1,25 @@
+//===-- 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 "src/__support/CPP/limits.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/time/time_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(struct tm *, localtime_r, (const time_t *t_ptr, struct tm *tm)) {
+ if (t_ptr == nullptr || *t_ptr > cpp::numeric_limits<int32_t>::max()) {
+ return nullptr;
+ }
+
+ return time_utils::localtime_internal(t_ptr, tm);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/localtime_r.h b/libc/src/time/localtime_r.h
new file mode 100644
index 00000000000000..d98b52180fa789
--- /dev/null
+++ b/libc/src/time/localtime_r.h
@@ -0,0 +1,21 @@
+//===-- 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 "src/__support/macros/config.h"
+#include <time.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct tm *localtime_r(const time_t *t_ptr, struct tm *tm);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_LOCALTIME_R_H
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 509cad8146df87..d00cc139471c8f 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include <stdio.h>
#include "src/time/time_utils.h"
#include "src/__support/CPP/limits.h" // INT_MIN, INT_MAX
#include "src/__support/common.h"
@@ -129,6 +130,25 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
if (years > INT_MAX || years < INT_MIN)
return time_utils::out_of_range();
+ FILE *fp;
+ fp = fopen("/etc/timezone", "rb");
+ if (fp == NULL) {
+ return time_utils::out_of_range();
+ }
+
+ char timezone[128];
+ if (fgets(timezone, sizeof(timezone), fp) == NULL) {
+ return time_utils::out_of_range();
+ }
+
+ int offset;
+ if (internal::same_string(timezone, "UTC") == 0) {
+ offset = 0;
+ }
+ if (internal::same_string(timezone, "Europe/Berlin") == 0) {
+ offset = 2;
+ }
+
// 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 = static_cast<int>(years + 2000 - TimeConstants::TIME_YEAR_BASE);
@@ -144,11 +164,32 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
TimeConstants::SECONDS_PER_MIN);
tm->tm_sec =
static_cast<int>(remainingSeconds % TimeConstants::SECONDS_PER_MIN);
- // TODO(rtenneti): Need to handle timezone and update of tm_isdst.
- tm->tm_isdst = 0;
+
+ if (offset == 0) {
+ tm->tm_isdst = 1;
+ } else {
+ tm->tm_isdst = 0;
+ tm->tm_hour += offset;
+ }
+
+ fclose(fp);
return 0;
}
+int calculate_dst(struct tm *tm) {
+ int sunday = tm->tm_mday - tm->tm_wday;
+
+ if (tm->tm_mon < 3 || tm->tm_mon > 11) {
+ return 0;
+ } else if (tm->tm_mon > 3 && tm->tm_mon < 11) {
+ return 1;
+ } else if (tm->tm_mon == 3) {
+ return sunday >= 8;
+ }
+
+ return sunday <= 0;
+}
+
} // namespace time_utils
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 552ea925c1c7dc..97374ca5b17289 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -15,6 +15,7 @@
#include "src/__support/macros/config.h"
#include "src/errno/libc_errno.h"
#include "src/time/mktime.h"
+#include "src/__support/CPP/limits.h"
#include <stdint.h>
@@ -85,6 +86,7 @@ struct TimeConstants {
// 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 update_from_seconds(int64_t total_seconds, struct tm *tm);
+extern int calculate_dst(struct tm *tm);
// TODO(michaelrj): move these functions to use ErrorOr instead of setting
// errno. They always accompany a specific return value so we only need the one
@@ -156,11 +158,38 @@ LIBC_INLINE struct tm *gmtime_internal(const time_t *timer, struct tm *result) {
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 struct tm *localtime(const time_t *t_ptr) {
static struct tm result;
- return time_utils::gmtime_internal(t_ptr, &result);
+ int64_t time = *t_ptr;
+
+ // Update the tm structure's year, month, day, etc. from seconds.
+ if (update_from_seconds(time, &result) < 0) {
+ out_of_range();
+ return nullptr;
+ }
+
+ int isdst = calculate_dst(&result);
+ result.tm_hour += isdst;
+ result.tm_isdst = isdst;
+
+ return &result;
+}
+
+LIBC_INLINE struct tm *localtime_internal(const time_t *t_ptr, struct tm *result) {
+ //time_t time = *t;
+ int64_t t = *t_ptr;
+
+ // Update the tm structure's year, month, day, etc. from seconds.
+ if (update_from_seconds(t, result) < 0) {
+ out_of_range();
+ return nullptr;
+ }
+
+ int isdst = calculate_dst(result);
+ result->tm_hour += isdst;
+ result->tm_isdst = isdst;
+
+ return result;
}
} // namespace time_utils
diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
index bba01f063fed27..e4f8944f6f3c62 100644
--- a/libc/test/src/time/CMakeLists.txt
+++ b/libc/test/src/time/CMakeLists.txt
@@ -66,6 +66,42 @@ add_libc_unittest(
libc.src.time.time_utils
)
+add_libc_unittest(
+ localtime_test
+ SUITE
+ libc_time_unittests
+ SRCS
+ localtime_test.cpp
+ HDRS
+ TmHelper.h
+ TmMatcher.h
+ CXX_STANDARD
+ 20
+ DEPENDS
+ libc.include.time
+ libc.hdr.types.time_t
+ libc.src.time.localtime
+ libc.src.time.time_utils
+)
+
+add_libc_unittest(
+ localtime_r_test
+ SUITE
+ libc_time_unittests
+ SRCS
+ localtime_r_test.cpp
+ HDRS
+ TmHelper.h
+ TmMatcher.h
+ CXX_STANDARD
+ 20
+ DEPENDS
+ libc.include.time
+ libc.hdr.types.time_t
+ libc.src.time.localtime_r
+ libc.src.time.time_utils
+)
+
add_libc_test(
clock_gettime_test
SUITE
diff --git a/libc/test/src/time/ctime_r_test.cpp b/libc/test/src/time/ctime_r_test.cpp
index 9ce6f75f754849..e0aa32e28178dc 100644
--- a/libc/test/src/time/ctime_r_test.cpp
+++ b/libc/test/src/time/ctime_r_test.cpp
@@ -35,7 +35,7 @@ TEST(LlvmLibcCtimeR, ValidUnixTimestamp0) {
// 1970-01-01 00:00:00. Test with a valid buffer size.
t = 0;
result = LIBC_NAMESPACE::ctime_r(&t, buffer);
- ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", result);
+ ASSERT_STREQ("Thu Jan 1 01:00:00 1970\n", result);
}
TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
@@ -45,7 +45,7 @@ TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
// 2038-01-19 03:14:07. Test with a valid buffer size.
t = 2147483647;
result = LIBC_NAMESPACE::ctime_r(&t, buffer);
- ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", result);
+ ASSERT_STREQ("Tue Jan 19 04:14:07 2038\n", result);
}
TEST(LlvmLibcCtimeR, InvalidArgument) {
diff --git a/libc/test/src/time/ctime_test.cpp b/libc/test/src/time/ctime_test.cpp
index 7ec71bb1e4ed1e..554b72d23fd4de 100644
--- a/libc/test/src/time/ctime_test.cpp
+++ b/libc/test/src/time/ctime_test.cpp
@@ -22,7 +22,7 @@ TEST(LlvmLibcCtime, ValidUnixTimestamp0) {
char *result;
t = 0;
result = LIBC_NAMESPACE::ctime(&t);
- ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", result);
+ ASSERT_STREQ("Thu Jan 1 01:00:00 1970\n", result);
}
TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
@@ -30,7 +30,7 @@ TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
char *result;
t = 2147483647;
result = LIBC_NAMESPACE::ctime(&t);
- ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", result);
+ ASSERT_STREQ("Tue Jan 19 04:14:07 2038\n", result);
}
TEST(LlvmLibcCtime, InvalidArgument) {
diff --git a/libc/test/src/time/localtime_r_test.cpp b/libc/test/src/time/localtime_r_test.cpp
new file mode 100644
index 00000000000000..83848b53fc7d54
--- /dev/null
+++ b/libc/test/src/time/localtime_r_test.cpp
@@ -0,0 +1,87 @@
+//===-- 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/errno/libc_errno.h"
+#include "src/time/localtime_r.h"
+#include "test/UnitTest/Test.h"
+#include "test/src/time/TmHelper.h"
+
+TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
+ const time_t t_ptr = 1;
+ static struct tm input = (struct tm) {
+ .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
+ };
+ struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
+ ASSERT_EQ(70, result->tm_year);
+ ASSERT_EQ(0, result->tm_mon);
+ ASSERT_EQ(1, result->tm_mday);
+ ASSERT_EQ(2, result->tm_hour);
+ ASSERT_EQ(0, result->tm_min);
+ ASSERT_EQ(1, result->tm_sec);
+ ASSERT_EQ(4, result->tm_wday);
+ ASSERT_EQ(0, result->tm_yday);
+ ASSERT_EQ(0, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
+ time_t t_ptr = 2147483647;
+ static struct tm input = (struct tm) {
+ .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
+ };
+ struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
+ ASSERT_EQ(138, result->tm_year);
+ ASSERT_EQ(0, result->tm_mon);
+ ASSERT_EQ(19, result->tm_mday);
+ ASSERT_EQ(5, result->tm_hour);
+ ASSERT_EQ(14, result->tm_min);
+ ASSERT_EQ(7, result->tm_sec);
+ ASSERT_EQ(2, result->tm_wday);
+ ASSERT_EQ(18, result->tm_yday);
+ ASSERT_EQ(0, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32IntDst) {
+ time_t t_ptr = 1627225465;
+ static struct tm input = (struct tm) {
+ .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
+ };
+ struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
+ ASSERT_EQ(121, result->tm_year);
+ ASSERT_EQ(6, result->tm_mon);
+ ASSERT_EQ(25, result->tm_mday);
+ ASSERT_EQ(18, result->tm_hour);
+ ASSERT_EQ(4, result->tm_min);
+ ASSERT_EQ(25, result->tm_sec);
+ ASSERT_EQ(0, result->tm_wday);
+ ASSERT_EQ(205, result->tm_yday);
+ ASSERT_EQ(1, result->tm_isdst);
+}
diff --git a/libc/test/src/time/localtime_test.cpp b/libc/test/src/time/localtime_test.cpp
new file mode 100644
index 00000000000000..ba068dbf632d02
--- /dev/null
+++ b/libc/test/src/time/localtime_test.cpp
@@ -0,0 +1,54 @@
+//===-- 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/errno/libc_errno.h"
+#include "src/time/localtime.h"
+#include "test/UnitTest/Test.h"
+#include "test/src/time/TmHelper.h"
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestamp0) {
+ const time_t t_ptr = 0;
+ struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
+ ASSERT_EQ(70, result->tm_year);
+ ASSERT_EQ(0, result->tm_mon);
+ ASSERT_EQ(1, result->tm_mday);
+ ASSERT_EQ(2, 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, ValidUnixTimestamp32Int) {
+ time_t t_ptr = 2147483647;
+ struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
+ ASSERT_EQ(138, result->tm_year);
+ ASSERT_EQ(0, result->tm_mon);
+ ASSERT_EQ(19, result->tm_mday);
+ ASSERT_EQ(5, result->tm_hour);
+ ASSERT_EQ(14, result->tm_min);
+ ASSERT_EQ(7, result->tm_sec);
+ ASSERT_EQ(2, result->tm_wday);
+ ASSERT_EQ(18, result->tm_yday);
+ ASSERT_EQ(0, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestamp32IntDst) {
+ time_t t_ptr = 1627225465;
+ struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
+ ASSERT_EQ(121, result->tm_year);
+ ASSERT_EQ(6, result->tm_mon);
+ ASSERT_EQ(25, result->tm_mday);
+ ASSERT_EQ(18, result->tm_hour);
+ ASSERT_EQ(4, result->tm_min);
+ ASSERT_EQ(25, result->tm_sec);
+ ASSERT_EQ(0, result->tm_wday);
+ ASSERT_EQ(205, result->tm_yday);
+ ASSERT_EQ(1, result->tm_isdst);
+}
>From 4280c136c7fa56643a83b6184be894bfca259588 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sat, 28 Sep 2024 16:02:12 +0200
Subject: [PATCH 02/27] [libc] implement localtime
format code with clang-format
---
libc/src/time/localtime.cpp | 2 +-
libc/src/time/localtime_r.cpp | 5 ++-
libc/src/time/time_utils.cpp | 8 ++--
libc/src/time/time_utils.h | 7 +--
libc/test/src/time/localtime_r_test.cpp | 60 +++++++++++--------------
5 files changed, 39 insertions(+), 43 deletions(-)
diff --git a/libc/src/time/localtime.cpp b/libc/src/time/localtime.cpp
index c08af2d8b26b78..48c8d4a6c6b3f4 100644
--- a/libc/src/time/localtime.cpp
+++ b/libc/src/time/localtime.cpp
@@ -16,7 +16,7 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(struct tm *, localtime, (const time_t *t_ptr)) {
if (t_ptr == nullptr || *t_ptr > cpp::numeric_limits<int32_t>::max()) {
- return nullptr;
+ return nullptr;
}
return time_utils::localtime(t_ptr);
diff --git a/libc/src/time/localtime_r.cpp b/libc/src/time/localtime_r.cpp
index 6ff3fb3d4faa8c..64a107a729132f 100644
--- a/libc/src/time/localtime_r.cpp
+++ b/libc/src/time/localtime_r.cpp
@@ -14,9 +14,10 @@
namespace LIBC_NAMESPACE_DECL {
-LLVM_LIBC_FUNCTION(struct tm *, localtime_r, (const time_t *t_ptr, struct tm *tm)) {
+LLVM_LIBC_FUNCTION(struct tm *, localtime_r,
+ (const time_t *t_ptr, struct tm *tm)) {
if (t_ptr == nullptr || *t_ptr > cpp::numeric_limits<int32_t>::max()) {
- return nullptr;
+ return nullptr;
}
return time_utils::localtime_internal(t_ptr, tm);
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index d00cc139471c8f..9a4aacb8f86f85 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -6,11 +6,11 @@
//
//===----------------------------------------------------------------------===//
-#include <stdio.h>
#include "src/time/time_utils.h"
#include "src/__support/CPP/limits.h" // INT_MIN, INT_MAX
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
+#include <stdio.h>
namespace LIBC_NAMESPACE_DECL {
namespace time_utils {
@@ -143,10 +143,10 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
int offset;
if (internal::same_string(timezone, "UTC") == 0) {
- offset = 0;
+ offset = 0;
}
if (internal::same_string(timezone, "Europe/Berlin") == 0) {
- offset = 2;
+ offset = 2;
}
// All the data (years, month and remaining days) was calculated from
@@ -185,7 +185,7 @@ int calculate_dst(struct tm *tm) {
} else if (tm->tm_mon > 3 && tm->tm_mon < 11) {
return 1;
} else if (tm->tm_mon == 3) {
- return sunday >= 8;
+ return sunday >= 8;
}
return sunday <= 0;
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 97374ca5b17289..87e0e0bae767c8 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -11,11 +11,11 @@
#include <stddef.h> // For size_t.
+#include "src/__support/CPP/limits.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/errno/libc_errno.h"
#include "src/time/mktime.h"
-#include "src/__support/CPP/limits.h"
#include <stdint.h>
@@ -175,8 +175,9 @@ LIBC_INLINE struct tm *localtime(const time_t *t_ptr) {
return &result;
}
-LIBC_INLINE struct tm *localtime_internal(const time_t *t_ptr, struct tm *result) {
- //time_t time = *t;
+LIBC_INLINE struct tm *localtime_internal(const time_t *t_ptr,
+ struct tm *result) {
+ // time_t time = *t;
int64_t t = *t_ptr;
// Update the tm structure's year, month, day, etc. from seconds.
diff --git a/libc/test/src/time/localtime_r_test.cpp b/libc/test/src/time/localtime_r_test.cpp
index 83848b53fc7d54..edc7fed030aa9d 100644
--- a/libc/test/src/time/localtime_r_test.cpp
+++ b/libc/test/src/time/localtime_r_test.cpp
@@ -13,17 +13,15 @@
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
const time_t t_ptr = 1;
- static struct tm input = (struct tm) {
- .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
- };
+ static struct tm input = (struct tm){.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};
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
ASSERT_EQ(70, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
@@ -38,17 +36,15 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
time_t t_ptr = 2147483647;
- static struct tm input = (struct tm) {
- .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
- };
+ static struct tm input = (struct tm){.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};
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
ASSERT_EQ(138, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
@@ -63,17 +59,15 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32IntDst) {
time_t t_ptr = 1627225465;
- static struct tm input = (struct tm) {
- .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
- };
+ static struct tm input = (struct tm){.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};
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
ASSERT_EQ(121, result->tm_year);
ASSERT_EQ(6, result->tm_mon);
>From 15a5bef567cd7535a4ace2dcee0da4bddb97b954 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sat, 28 Sep 2024 16:05:42 +0200
Subject: [PATCH 03/27] [libc] implement localtime
fix: dst
---
libc/src/time/time_utils.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 9a4aacb8f86f85..5cc735734111a7 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -166,9 +166,9 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
static_cast<int>(remainingSeconds % TimeConstants::SECONDS_PER_MIN);
if (offset == 0) {
- tm->tm_isdst = 1;
- } else {
tm->tm_isdst = 0;
+ } else {
+ tm->tm_isdst = 1;
tm->tm_hour += offset;
}
>From 9970dfefba4073a55d5bc73be43c03a5a12d6c7b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sat, 28 Sep 2024 16:16:06 +0200
Subject: [PATCH 04/27] [libc] implement localtime
fix: dst
---
libc/src/time/time_utils.h | 1 -
libc/test/src/time/localtime_r_test.cpp | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 87e0e0bae767c8..0be7f114d370f9 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -187,7 +187,6 @@ LIBC_INLINE struct tm *localtime_internal(const time_t *t_ptr,
}
int isdst = calculate_dst(result);
- result->tm_hour += isdst;
result->tm_isdst = isdst;
return result;
diff --git a/libc/test/src/time/localtime_r_test.cpp b/libc/test/src/time/localtime_r_test.cpp
index edc7fed030aa9d..3c5d3c64e6bd9f 100644
--- a/libc/test/src/time/localtime_r_test.cpp
+++ b/libc/test/src/time/localtime_r_test.cpp
@@ -72,7 +72,7 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32IntDst) {
ASSERT_EQ(121, result->tm_year);
ASSERT_EQ(6, result->tm_mon);
ASSERT_EQ(25, result->tm_mday);
- ASSERT_EQ(18, result->tm_hour);
+ ASSERT_EQ(17, result->tm_hour);
ASSERT_EQ(4, result->tm_min);
ASSERT_EQ(25, result->tm_sec);
ASSERT_EQ(0, result->tm_wday);
>From 6dee10b36c82cc3483e281c717c749a624479f3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sat, 28 Sep 2024 16:20:15 +0200
Subject: [PATCH 05/27] [libc] implement localtime
update test for `localtime_r`
---
libc/test/src/time/localtime_r_test.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/test/src/time/localtime_r_test.cpp b/libc/test/src/time/localtime_r_test.cpp
index 3c5d3c64e6bd9f..805db50751b777 100644
--- a/libc/test/src/time/localtime_r_test.cpp
+++ b/libc/test/src/time/localtime_r_test.cpp
@@ -12,7 +12,7 @@
#include "test/src/time/TmHelper.h"
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
- const time_t t_ptr = 1;
+ const time_t t_ptr = 0;
static struct tm input = (struct tm){.tm_sec = 0,
.tm_min = 0,
.tm_hour = 0,
@@ -28,7 +28,7 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
ASSERT_EQ(1, result->tm_mday);
ASSERT_EQ(2, result->tm_hour);
ASSERT_EQ(0, result->tm_min);
- ASSERT_EQ(1, result->tm_sec);
+ ASSERT_EQ(0, result->tm_sec);
ASSERT_EQ(4, result->tm_wday);
ASSERT_EQ(0, result->tm_yday);
ASSERT_EQ(0, result->tm_isdst);
>From 429d13aa01a6d9efbf23e16cbf31b5d89009dfd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sat, 28 Sep 2024 16:22:35 +0200
Subject: [PATCH 06/27] [libc] implement localtime
remove unnecessary code
---
libc/src/time/time_utils.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 0be7f114d370f9..494ebc0bedf276 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -177,7 +177,6 @@ LIBC_INLINE struct tm *localtime(const time_t *t_ptr) {
LIBC_INLINE struct tm *localtime_internal(const time_t *t_ptr,
struct tm *result) {
- // time_t time = *t;
int64_t t = *t_ptr;
// Update the tm structure's year, month, day, etc. from seconds.
>From 6dc9f4c6b22cde2393742e215d4c725d89907843 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sat, 28 Sep 2024 16:34:52 +0200
Subject: [PATCH 07/27] [libc] implement localtime
fix: dst
---
libc/src/time/time_utils.h | 1 -
libc/test/src/time/localtime_test.cpp | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 494ebc0bedf276..6faf8f37172f6c 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -169,7 +169,6 @@ LIBC_INLINE struct tm *localtime(const time_t *t_ptr) {
}
int isdst = calculate_dst(&result);
- result.tm_hour += isdst;
result.tm_isdst = isdst;
return &result;
diff --git a/libc/test/src/time/localtime_test.cpp b/libc/test/src/time/localtime_test.cpp
index ba068dbf632d02..4143e7065596ff 100644
--- a/libc/test/src/time/localtime_test.cpp
+++ b/libc/test/src/time/localtime_test.cpp
@@ -45,7 +45,7 @@ TEST(LlvmLibcLocaltime, ValidUnixTimestamp32IntDst) {
ASSERT_EQ(121, result->tm_year);
ASSERT_EQ(6, result->tm_mon);
ASSERT_EQ(25, result->tm_mday);
- ASSERT_EQ(18, result->tm_hour);
+ ASSERT_EQ(17, result->tm_hour);
ASSERT_EQ(4, result->tm_min);
ASSERT_EQ(25, result->tm_sec);
ASSERT_EQ(0, result->tm_wday);
>From 054d070f0cf4e11bc649c420200894cb15d49ed7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 16:30:21 +0200
Subject: [PATCH 08/27] localtime_s is for windows only, implement localtime_s
on gnu/linux for compatibility reasons
---
libc/config/baremetal/arm/entrypoints.txt | 1 +
libc/config/baremetal/riscv/entrypoints.txt | 1 +
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/docs/c23.rst | 1 +
libc/docs/date_and_time.rst | 2 +
libc/newhdrgen/yaml/time.yaml | 7 +++
libc/spec/stdc.td | 8 ++++
libc/src/time/CMakeLists.txt | 12 +++++
libc/src/time/localtime.cpp | 8 ++--
libc/src/time/localtime_r.cpp | 9 +---
libc/src/time/localtime_r.h | 2 +-
libc/src/time/localtime_s.cpp | 21 +++++++++
libc/src/time/localtime_s.h | 22 +++++++++
libc/src/time/time_utils.h | 37 +++++++++++++--
libc/test/src/time/CMakeLists.txt | 19 ++++++++
libc/test/src/time/ctime_r_test.cpp | 4 +-
libc/test/src/time/localtime_r_test.cpp | 50 +++++++++-----------
libc/test/src/time/localtime_s_test.cpp | 52 +++++++++++++++++++++
20 files changed, 213 insertions(+), 46 deletions(-)
create mode 100644 libc/src/time/localtime_s.cpp
create mode 100644 libc/src/time/localtime_s.h
create mode 100644 libc/test/src/time/localtime_s_test.cpp
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 7577fad2fde869..0a905d50f8ba25 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -207,6 +207,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.time.ctime_r
libc.src.time.localtime
libc.src.time.localtime_r
+ libc.src.time.localtime_s
libc.src.time.difftime
libc.src.time.gmtime
libc.src.time.gmtime_r
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index b93aa334296ffb..b9ea143c770262 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -203,6 +203,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.time.ctime_r
libc.src.time.localtime
libc.src.time.localtime_r
+ libc.src.time.localtime_s
libc.src.time.difftime
libc.src.time.gmtime
libc.src.time.gmtime_r
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index d287bb0510a1fb..a4b430f1e84ea4 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -952,6 +952,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.ctime_r
libc.src.time.localtime
libc.src.time.localtime_r
+ libc.src.time.localtime_s
libc.src.time.clock
libc.src.time.clock_gettime
libc.src.time.difftime
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 40a417bcf09b49..18f6e6418a7af9 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -887,6 +887,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.ctime_r
libc.src.time.localtime
libc.src.time.localtime_r
+ libc.src.time.localtime_s
libc.src.time.clock
libc.src.time.clock_gettime
libc.src.time.difftime
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 3b63ce82417f4b..5b9b0dea1b31ae 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1007,6 +1007,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.ctime_r
libc.src.time.localtime
libc.src.time.localtime_r
+ libc.src.time.localtime_s
libc.src.time.clock
libc.src.time.clock_gettime
libc.src.time.difftime
diff --git a/libc/docs/c23.rst b/libc/docs/c23.rst
index b9a2424307f84b..9f0515bda5d581 100644
--- a/libc/docs/c23.rst
+++ b/libc/docs/c23.rst
@@ -152,6 +152,7 @@ Additions:
* gmtime_r
* localtime_r
+ * localtime_s
* timegm
* timespec_getres
* strftime conversion specifiers
diff --git a/libc/docs/date_and_time.rst b/libc/docs/date_and_time.rst
index b745a3b416f802..ab7c7c3de68677 100644
--- a/libc/docs/date_and_time.rst
+++ b/libc/docs/date_and_time.rst
@@ -89,6 +89,8 @@ Implementation Status
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
| localtime_r | | | | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
+| localtime_s | | | | | | | | | | | | |
++---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
| mktime | |check| | |check| | | |check| | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
| nanosleep | |check| | |check| | | |check| | | | | | | | | |
diff --git a/libc/newhdrgen/yaml/time.yaml b/libc/newhdrgen/yaml/time.yaml
index 7198c19fce2bf0..b0311b64165472 100644
--- a/libc/newhdrgen/yaml/time.yaml
+++ b/libc/newhdrgen/yaml/time.yaml
@@ -50,6 +50,13 @@ functions:
arguments:
- type: const time_t *
- type: struct tm *
+ - name: localtime_s
+ standard:
+ - stdc
+ return_type: struct tm *
+ arguments:
+ - type: const time_t *
+ - type: struct tm *
- name: clock
standard:
- stdc
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 9fd8d175f36be3..5546a3afcab9f7 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -1628,6 +1628,14 @@ def StdC : StandardSpec<"stdc"> {
ArgSpec<StructTmPtr>,
]
>,
+ FunctionSpec<
+ "localtime_s",
+ RetValSpec<StructTmPtr>,
+ [
+ ArgSpec<TimeTTypePtr>,
+ ArgSpec<StructTmPtr>,
+ ]
+ >,
FunctionSpec<
"clock",
RetValSpec<ClockT>,
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index de2e128dd310c4..57b229d029c92e 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -84,6 +84,18 @@ add_entrypoint_object(
libc.include.time
)
+add_entrypoint_object(
+ localtime_s
+ SRCS
+ localtime_s.cpp
+ HDRS
+ localtime_s.h
+ DEPENDS
+ .time_utils
+ libc.hdr.types.time_t
+ libc.include.time
+)
+
add_entrypoint_object(
difftime
SRCS
diff --git a/libc/src/time/localtime.cpp b/libc/src/time/localtime.cpp
index 48c8d4a6c6b3f4..42ba562e0a561c 100644
--- a/libc/src/time/localtime.cpp
+++ b/libc/src/time/localtime.cpp
@@ -6,16 +6,18 @@
//
//===----------------------------------------------------------------------===//
-#include "src/time/localtime.h"
+#include "localtime.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/time/time_utils.h"
+#include "time_utils.h"
namespace LIBC_NAMESPACE_DECL {
+using LIBC_NAMESPACE::time_utils::TimeConstants;
+
LLVM_LIBC_FUNCTION(struct tm *, localtime, (const time_t *t_ptr)) {
- if (t_ptr == nullptr || *t_ptr > cpp::numeric_limits<int32_t>::max()) {
+ if (t_ptr == nullptr) {
return nullptr;
}
diff --git a/libc/src/time/localtime_r.cpp b/libc/src/time/localtime_r.cpp
index 64a107a729132f..0af5414930327b 100644
--- a/libc/src/time/localtime_r.cpp
+++ b/libc/src/time/localtime_r.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "src/time/localtime_r.h"
-#include "src/__support/CPP/limits.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/time/time_utils.h"
@@ -15,12 +14,8 @@
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(struct tm *, localtime_r,
- (const time_t *t_ptr, struct tm *tm)) {
- if (t_ptr == nullptr || *t_ptr > cpp::numeric_limits<int32_t>::max()) {
- return nullptr;
- }
-
- return time_utils::localtime_internal(t_ptr, tm);
+ (const time_t *t_ptr, struct tm *input)) {
+ return time_utils::localtime_internal(t_ptr, input);
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/localtime_r.h b/libc/src/time/localtime_r.h
index d98b52180fa789..553811d6ffdc2b 100644
--- a/libc/src/time/localtime_r.h
+++ b/libc/src/time/localtime_r.h
@@ -14,7 +14,7 @@
namespace LIBC_NAMESPACE_DECL {
-struct tm *localtime_r(const time_t *t_ptr, struct tm *tm);
+struct tm *localtime_r(const time_t *t_ptr, struct tm *input);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/localtime_s.cpp b/libc/src/time/localtime_s.cpp
new file mode 100644
index 00000000000000..e49ff49810819f
--- /dev/null
+++ b/libc/src/time/localtime_s.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of localtime_s 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_s.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/time/time_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// windows only, implemented in gnu/linux for compatibility reasons
+LLVM_LIBC_FUNCTION(int, localtime_s, (const time_t *t_ptr, struct tm *input)) {
+ return time_utils::localtime_s(t_ptr, input);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/localtime_s.h b/libc/src/time/localtime_s.h
new file mode 100644
index 00000000000000..4f69cb0014d507
--- /dev/null
+++ b/libc/src/time/localtime_s.h
@@ -0,0 +1,22 @@
+//===-- Implementation header of localtime_s --------------------*- 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_S_H
+#define LLVM_LIBC_SRC_TIME_LOCALTIME_S_H
+
+#include "src/__support/macros/config.h"
+#include <time.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+// windows only, implemented in gnu/linux for compatibility reasons
+int localtime_s(const time_t *t_ptr, struct tm *input);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_LOCALTIME_S_H
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 6faf8f37172f6c..78bfa069471231 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -175,19 +175,46 @@ LIBC_INLINE struct tm *localtime(const time_t *t_ptr) {
}
LIBC_INLINE struct tm *localtime_internal(const time_t *t_ptr,
- struct tm *result) {
+ struct tm *input) {
+ static struct tm result;
int64_t t = *t_ptr;
+ result.tm_sec = input->tm_sec;
+ result.tm_min = input->tm_min;
+ result.tm_hour = input->tm_hour;
+ result.tm_mday = input->tm_mday;
+ result.tm_mon = input->tm_mon;
+ result.tm_year = input->tm_year;
+ result.tm_wday = input->tm_wday;
+ result.tm_yday = input->tm_yday;
+ result.tm_isdst = input->tm_isdst;
+
// Update the tm structure's year, month, day, etc. from seconds.
- if (update_from_seconds(t, result) < 0) {
+ if (update_from_seconds(t, &result) < 0) {
out_of_range();
return nullptr;
}
- int isdst = calculate_dst(result);
- result->tm_isdst = isdst;
+ int isdst = calculate_dst(&result);
+ result.tm_isdst = isdst;
- return result;
+ return &result;
+}
+
+// for windows only, implemented on gnu/linux for compatibility reasons
+LIBC_INLINE int localtime_s(const time_t *t_ptr, struct tm *input) {
+ static struct tm *result = localtime_internal(t_ptr, input);
+ time_t t = LIBC_NAMESPACE::mktime(result);
+
+ if (*t_ptr < t) {
+ return -1;
+ }
+
+ if (*t_ptr > t) {
+ return 1;
+ }
+
+ return 0;
}
} // namespace time_utils
diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
index e4f8944f6f3c62..54e078cff83c15 100644
--- a/libc/test/src/time/CMakeLists.txt
+++ b/libc/test/src/time/CMakeLists.txt
@@ -102,6 +102,25 @@ add_libc_unittest(
libc.src.time.time_utils
)
+add_libc_unittest(
+ localtime_s_test
+ SUITE
+ libc_time_unittests
+ SRCS
+ localtime_s_test.cpp
+ HDRS
+ TmHelper.h
+ TmMatcher.h
+ CXX_STANDARD
+ 20
+ DEPENDS
+ libc.include.time
+ libc.hdr.types.time_t
+ libc.src.time.mktime
+ libc.src.time.localtime_s
+ libc.src.time.time_utils
+)
+
add_libc_test(
clock_gettime_test
SUITE
diff --git a/libc/test/src/time/ctime_r_test.cpp b/libc/test/src/time/ctime_r_test.cpp
index e0aa32e28178dc..b03e8cecd2e9df 100644
--- a/libc/test/src/time/ctime_r_test.cpp
+++ b/libc/test/src/time/ctime_r_test.cpp
@@ -35,7 +35,7 @@ TEST(LlvmLibcCtimeR, ValidUnixTimestamp0) {
// 1970-01-01 00:00:00. Test with a valid buffer size.
t = 0;
result = LIBC_NAMESPACE::ctime_r(&t, buffer);
- ASSERT_STREQ("Thu Jan 1 01:00:00 1970\n", result);
+ ASSERT_STREQ("Thu Jan 1 02:00:00 1970\n", result);
}
TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
@@ -45,7 +45,7 @@ TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
// 2038-01-19 03:14:07. Test with a valid buffer size.
t = 2147483647;
result = LIBC_NAMESPACE::ctime_r(&t, buffer);
- ASSERT_STREQ("Tue Jan 19 04:14:07 2038\n", result);
+ ASSERT_STREQ("Tue Jan 19 05:14:07 2038\n", result);
}
TEST(LlvmLibcCtimeR, InvalidArgument) {
diff --git a/libc/test/src/time/localtime_r_test.cpp b/libc/test/src/time/localtime_r_test.cpp
index 805db50751b777..ba46835e81f77a 100644
--- a/libc/test/src/time/localtime_r_test.cpp
+++ b/libc/test/src/time/localtime_r_test.cpp
@@ -6,22 +6,16 @@
//
//===----------------------------------------------------------------------===//
-#include "src/errno/libc_errno.h"
#include "src/time/localtime_r.h"
+#include "src/time/time_utils.h"
#include "test/UnitTest/Test.h"
#include "test/src/time/TmHelper.h"
+using LIBC_NAMESPACE::time_utils::TimeConstants;
+
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
+ struct tm input;
const time_t t_ptr = 0;
- static struct tm input = (struct tm){.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};
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
ASSERT_EQ(70, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
@@ -36,15 +30,15 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
time_t t_ptr = 2147483647;
- static struct tm input = (struct tm){.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};
+ struct tm input = (struct tm){.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};
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
ASSERT_EQ(138, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
@@ -59,15 +53,15 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32IntDst) {
time_t t_ptr = 1627225465;
- static struct tm input = (struct tm){.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};
+ struct tm input = (struct tm){.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};
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
ASSERT_EQ(121, result->tm_year);
ASSERT_EQ(6, result->tm_mon);
diff --git a/libc/test/src/time/localtime_s_test.cpp b/libc/test/src/time/localtime_s_test.cpp
new file mode 100644
index 00000000000000..04d5d79c810792
--- /dev/null
+++ b/libc/test/src/time/localtime_s_test.cpp
@@ -0,0 +1,52 @@
+//===-- Unittests for localtime_s -----------------------------------------===//
+//
+// 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_s.h"
+#include "src/time/mktime.h"
+#include "src/time/time_utils.h"
+#include "test/UnitTest/Test.h"
+#include "test/src/time/TmHelper.h"
+
+using LIBC_NAMESPACE::time_utils::TimeConstants;
+
+TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp0) {
+ struct tm input;
+ const time_t t_ptr = 0;
+ int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
+ ASSERT_EQ(-1, result);
+}
+
+TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32Int) {
+ time_t t_ptr = 2147483647;
+ static struct tm input = (struct tm){.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};
+ int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
+ ASSERT_EQ(1, result);
+}
+
+TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32IntDst) {
+ time_t t_ptr = 1627225465;
+ static struct tm input = (struct tm){.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};
+ int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
+ ASSERT_EQ(1, result);
+}
>From a09d3af60719db6bdebb9eb20c5ce01fe36ab8f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 16:37:58 +0200
Subject: [PATCH 09/27] fix: localtime_s
---
libc/newhdrgen/yaml/time.yaml | 2 +-
libc/spec/stdc.td | 2 +-
libc/src/time/CMakeLists.txt | 1 +
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/libc/newhdrgen/yaml/time.yaml b/libc/newhdrgen/yaml/time.yaml
index b0311b64165472..9c249e21fc8d82 100644
--- a/libc/newhdrgen/yaml/time.yaml
+++ b/libc/newhdrgen/yaml/time.yaml
@@ -53,7 +53,7 @@ functions:
- name: localtime_s
standard:
- stdc
- return_type: struct tm *
+ return_type: int
arguments:
- type: const time_t *
- type: struct tm *
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 5546a3afcab9f7..d6186281b76a3f 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -1630,7 +1630,7 @@ def StdC : StandardSpec<"stdc"> {
>,
FunctionSpec<
"localtime_s",
- RetValSpec<StructTmPtr>,
+ RetValSpec<IntType>,
[
ArgSpec<TimeTTypePtr>,
ArgSpec<StructTmPtr>,
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index 57b229d029c92e..e983ccf6359e93 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -94,6 +94,7 @@ add_entrypoint_object(
.time_utils
libc.hdr.types.time_t
libc.include.time
+ libc.time.mktime
)
add_entrypoint_object(
>From 897c8209c85e30567244703e861376ac2d504cc9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 17:58:41 +0200
Subject: [PATCH 10/27] fix: path for mktime
---
libc/src/time/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index e983ccf6359e93..096d6bcc7dd8ef 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -94,7 +94,7 @@ add_entrypoint_object(
.time_utils
libc.hdr.types.time_t
libc.include.time
- libc.time.mktime
+ libc.src.time.mktime
)
add_entrypoint_object(
>From 31b3f2483f11e68659f043fdae2dd12003bba00d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 19:38:42 +0200
Subject: [PATCH 11/27] fix: localtime_s and dst function
---
libc/src/time/time_utils.cpp | 18 ++++--
libc/src/time/time_utils.h | 45 +++++++--------
libc/test/src/time/localtime_s_test.cpp | 77 ++++++++++++++++++++++---
3 files changed, 103 insertions(+), 37 deletions(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 5cc735734111a7..35fbc02c17e679 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -177,18 +177,24 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
return 0;
}
-int calculate_dst(struct tm *tm) {
- int sunday = tm->tm_mday - tm->tm_wday;
+void set_dst(struct tm *tm) {
+ int dst;
+ int sunday;
+
+ dst = 0;
+ sunday = tm->tm_mday - tm->tm_wday;
if (tm->tm_mon < 3 || tm->tm_mon > 11) {
- return 0;
+ dst = 0;
} else if (tm->tm_mon > 3 && tm->tm_mon < 11) {
- return 1;
+ dst = 1;
} else if (tm->tm_mon == 3) {
- return sunday >= 8;
+ dst = sunday >= 8;
+ } else {
+ dst = sunday <= 0;
}
- return sunday <= 0;
+ tm->tm_isdst = dst;
}
} // namespace time_utils
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 78bfa069471231..51442493e19d87 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -86,7 +86,7 @@ struct TimeConstants {
// 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 update_from_seconds(int64_t total_seconds, struct tm *tm);
-extern int calculate_dst(struct tm *tm);
+extern void set_dst(struct tm *tm);
// TODO(michaelrj): move these functions to use ErrorOr instead of setting
// errno. They always accompany a specific return value so we only need the one
@@ -168,51 +168,48 @@ LIBC_INLINE struct tm *localtime(const time_t *t_ptr) {
return nullptr;
}
- int isdst = calculate_dst(&result);
- result.tm_isdst = isdst;
+ set_dst(&result);
return &result;
}
LIBC_INLINE struct tm *localtime_internal(const time_t *t_ptr,
struct tm *input) {
- static struct tm result;
int64_t t = *t_ptr;
- result.tm_sec = input->tm_sec;
- result.tm_min = input->tm_min;
- result.tm_hour = input->tm_hour;
- result.tm_mday = input->tm_mday;
- result.tm_mon = input->tm_mon;
- result.tm_year = input->tm_year;
- result.tm_wday = input->tm_wday;
- result.tm_yday = input->tm_yday;
- result.tm_isdst = input->tm_isdst;
-
// Update the tm structure's year, month, day, etc. from seconds.
- if (update_from_seconds(t, &result) < 0) {
+ if (update_from_seconds(t, input) < 0) {
out_of_range();
return nullptr;
}
- int isdst = calculate_dst(&result);
- result.tm_isdst = isdst;
+ set_dst(input);
- return &result;
+ return input;
}
// for windows only, implemented on gnu/linux for compatibility reasons
LIBC_INLINE int localtime_s(const time_t *t_ptr, struct tm *input) {
- static struct tm *result = localtime_internal(t_ptr, input);
- time_t t = LIBC_NAMESPACE::mktime(result);
+ if (input == NULL)
+ return -1;
+
+ if ((*t_ptr < 0 || *t_ptr > cpp::numeric_limits<int64_t>::max()) && input != NULL) {
+ // setting values to -1 for compatibility reasons
+ // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-s-localtime32-s-localtime64-s
+ input->tm_sec = -1;
+ input->tm_min = -1;
+ input->tm_hour = -1;
+ input->tm_mday = -1;
+ input->tm_mon = -1;
+ input->tm_year = -1;
+ input->tm_wday = -1;
+ input->tm_yday = -1;
+ input->tm_isdst = -1;
- if (*t_ptr < t) {
return -1;
}
- if (*t_ptr > t) {
- return 1;
- }
+ localtime_internal(t_ptr, input);
return 0;
}
diff --git a/libc/test/src/time/localtime_s_test.cpp b/libc/test/src/time/localtime_s_test.cpp
index 04d5d79c810792..4a9d1d5dba5414 100644
--- a/libc/test/src/time/localtime_s_test.cpp
+++ b/libc/test/src/time/localtime_s_test.cpp
@@ -15,15 +15,33 @@
using LIBC_NAMESPACE::time_utils::TimeConstants;
TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp0) {
- struct tm input;
- const time_t t_ptr = 0;
+ struct tm input = (struct tm){.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};
+ time_t t_ptr = 0;
int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
- ASSERT_EQ(-1, result);
+ ASSERT_EQ(0, result);
+
+ ASSERT_EQ(70, input.tm_year);
+ ASSERT_EQ(0, input.tm_mon);
+ ASSERT_EQ(1, input.tm_mday);
+ ASSERT_EQ(2, input.tm_hour);
+ ASSERT_EQ(0, input.tm_min);
+ ASSERT_EQ(0, input.tm_sec);
+ ASSERT_EQ(4, input.tm_wday);
+ ASSERT_EQ(0, input.tm_yday);
+ ASSERT_EQ(0, input.tm_isdst);
}
TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32Int) {
time_t t_ptr = 2147483647;
- static struct tm input = (struct tm){.tm_sec = 0,
+ struct tm input = (struct tm){.tm_sec = 0,
.tm_min = 0,
.tm_hour = 0,
.tm_mday = 0,
@@ -33,12 +51,22 @@ TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32Int) {
.tm_yday = 0,
.tm_isdst = 0};
int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
- ASSERT_EQ(1, result);
+ ASSERT_EQ(0, result);
+
+ ASSERT_EQ(138, input.tm_year);
+ ASSERT_EQ(0, input.tm_mon);
+ ASSERT_EQ(19, input.tm_mday);
+ ASSERT_EQ(5, input.tm_hour);
+ ASSERT_EQ(14, input.tm_min);
+ ASSERT_EQ(7, input.tm_sec);
+ ASSERT_EQ(2, input.tm_wday);
+ ASSERT_EQ(18, input.tm_yday);
+ ASSERT_EQ(0, input.tm_isdst);
}
TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32IntDst) {
time_t t_ptr = 1627225465;
- static struct tm input = (struct tm){.tm_sec = 0,
+ struct tm input = (struct tm){.tm_sec = 0,
.tm_min = 0,
.tm_hour = 0,
.tm_mday = 0,
@@ -48,5 +76,40 @@ TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32IntDst) {
.tm_yday = 0,
.tm_isdst = 0};
int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
- ASSERT_EQ(1, result);
+ ASSERT_EQ(0, result);
+
+ ASSERT_EQ(121, input.tm_year);
+ ASSERT_EQ(6, input.tm_mon);
+ ASSERT_EQ(25, input.tm_mday);
+ ASSERT_EQ(17, input.tm_hour);
+ ASSERT_EQ(4, input.tm_min);
+ ASSERT_EQ(25, input.tm_sec);
+ ASSERT_EQ(0, input.tm_wday);
+ ASSERT_EQ(205, input.tm_yday);
+ ASSERT_EQ(1, input.tm_isdst);
+}
+
+TEST(LlvmLibcLocaltimeS, InvalidUnixTimestamp) {
+ time_t t_ptr = -1;
+ struct tm input = (struct tm){.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};
+ int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
+ ASSERT_EQ(-1, result);
+
+ ASSERT_EQ(-1, input.tm_year);
+ ASSERT_EQ(-1, input.tm_mon);
+ ASSERT_EQ(-1, input.tm_mday);
+ ASSERT_EQ(-1, input.tm_hour);
+ ASSERT_EQ(-1, input.tm_min);
+ ASSERT_EQ(-1, input.tm_sec);
+ ASSERT_EQ(-1, input.tm_wday);
+ ASSERT_EQ(-1, input.tm_yday);
+ ASSERT_EQ(-1, input.tm_isdst);
}
>From 0dbcfd3a8387dd69d83e2ea5a7701bcb4cf1af74 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 19:42:14 +0200
Subject: [PATCH 12/27] added tests for localtime_r
---
libc/test/src/time/localtime_r_test.cpp | 35 ++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/libc/test/src/time/localtime_r_test.cpp b/libc/test/src/time/localtime_r_test.cpp
index ba46835e81f77a..1a3e4be11e6c36 100644
--- a/libc/test/src/time/localtime_r_test.cpp
+++ b/libc/test/src/time/localtime_r_test.cpp
@@ -15,8 +15,19 @@ using LIBC_NAMESPACE::time_utils::TimeConstants;
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
struct tm input;
- const time_t t_ptr = 0;
+ time_t t_ptr = 0;
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
+
+ ASSERT_EQ(70, input.tm_year);
+ ASSERT_EQ(0, input.tm_mon);
+ ASSERT_EQ(1, input.tm_mday);
+ ASSERT_EQ(2, input.tm_hour);
+ ASSERT_EQ(0, input.tm_min);
+ ASSERT_EQ(0, input.tm_sec);
+ ASSERT_EQ(4, input.tm_wday);
+ ASSERT_EQ(0, input.tm_yday);
+ ASSERT_EQ(0, input.tm_isdst);
+
ASSERT_EQ(70, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
ASSERT_EQ(1, result->tm_mday);
@@ -40,6 +51,17 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
.tm_yday = 0,
.tm_isdst = 0};
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
+
+ ASSERT_EQ(138, input.tm_year);
+ ASSERT_EQ(0, input.tm_mon);
+ ASSERT_EQ(19, input.tm_mday);
+ ASSERT_EQ(5, input.tm_hour);
+ ASSERT_EQ(14, input.tm_min);
+ ASSERT_EQ(7, input.tm_sec);
+ ASSERT_EQ(2, input.tm_wday);
+ ASSERT_EQ(18, input.tm_yday);
+ ASSERT_EQ(0, input.tm_isdst);
+
ASSERT_EQ(138, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
ASSERT_EQ(19, result->tm_mday);
@@ -63,6 +85,17 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32IntDst) {
.tm_yday = 0,
.tm_isdst = 0};
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
+
+ ASSERT_EQ(121, input.tm_year);
+ ASSERT_EQ(6, input.tm_mon);
+ ASSERT_EQ(25, input.tm_mday);
+ ASSERT_EQ(17, input.tm_hour);
+ ASSERT_EQ(4, input.tm_min);
+ ASSERT_EQ(25, input.tm_sec);
+ ASSERT_EQ(0, input.tm_wday);
+ ASSERT_EQ(205, input.tm_yday);
+ ASSERT_EQ(1, input.tm_isdst);
+
ASSERT_EQ(121, result->tm_year);
ASSERT_EQ(6, result->tm_mon);
ASSERT_EQ(25, result->tm_mday);
>From a86b91f93c89228b2b8b9c574df34c0d7795b582 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 19:43:47 +0200
Subject: [PATCH 13/27] format code with clang-format
---
libc/src/time/time_utils.h | 3 +-
libc/test/src/time/localtime_s_test.cpp | 48 ++++++++++++-------------
2 files changed, 26 insertions(+), 25 deletions(-)
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 51442493e19d87..929487a16567c5 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -193,7 +193,8 @@ LIBC_INLINE int localtime_s(const time_t *t_ptr, struct tm *input) {
if (input == NULL)
return -1;
- if ((*t_ptr < 0 || *t_ptr > cpp::numeric_limits<int64_t>::max()) && input != NULL) {
+ if ((*t_ptr < 0 || *t_ptr > cpp::numeric_limits<int64_t>::max()) &&
+ input != NULL) {
// setting values to -1 for compatibility reasons
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-s-localtime32-s-localtime64-s
input->tm_sec = -1;
diff --git a/libc/test/src/time/localtime_s_test.cpp b/libc/test/src/time/localtime_s_test.cpp
index 4a9d1d5dba5414..cf97945a8fe4a0 100644
--- a/libc/test/src/time/localtime_s_test.cpp
+++ b/libc/test/src/time/localtime_s_test.cpp
@@ -42,14 +42,14 @@ TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp0) {
TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32Int) {
time_t t_ptr = 2147483647;
struct tm input = (struct tm){.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};
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 0,
+ .tm_mon = 0,
+ .tm_year = 0,
+ .tm_wday = 0,
+ .tm_yday = 0,
+ .tm_isdst = 0};
int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
ASSERT_EQ(0, result);
@@ -67,14 +67,14 @@ TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32Int) {
TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32IntDst) {
time_t t_ptr = 1627225465;
struct tm input = (struct tm){.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};
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 0,
+ .tm_mon = 0,
+ .tm_year = 0,
+ .tm_wday = 0,
+ .tm_yday = 0,
+ .tm_isdst = 0};
int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
ASSERT_EQ(0, result);
@@ -92,14 +92,14 @@ TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32IntDst) {
TEST(LlvmLibcLocaltimeS, InvalidUnixTimestamp) {
time_t t_ptr = -1;
struct tm input = (struct tm){.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};
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 0,
+ .tm_mon = 0,
+ .tm_year = 0,
+ .tm_wday = 0,
+ .tm_yday = 0,
+ .tm_isdst = 0};
int result = LIBC_NAMESPACE::localtime_s(&t_ptr, &input);
ASSERT_EQ(-1, result);
>From 2da5c6c89473ef2f8f4a1041b48aec47fc4b0a9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 19:59:14 +0200
Subject: [PATCH 14/27] refactor: timezone
---
libc/src/time/time_utils.cpp | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 35fbc02c17e679..90fec7cf730286 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -130,24 +130,22 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
if (years > INT_MAX || years < INT_MIN)
return time_utils::out_of_range();
+ char timezone[128];
+
FILE *fp;
fp = fopen("/etc/timezone", "rb");
if (fp == NULL) {
+ // TODO: implement getting timezone from `TZ` environment variable and
+ // storing the value in `timezone`
+ } else if (fgets(timezone, sizeof(timezone), fp) == NULL)
return time_utils::out_of_range();
- }
-
- char timezone[128];
- if (fgets(timezone, sizeof(timezone), fp) == NULL) {
- return time_utils::out_of_range();
- }
int offset;
- if (internal::same_string(timezone, "UTC") == 0) {
+ // TODO: Add more timezones
+ if (internal::same_string(timezone, "UTC") == 0)
offset = 0;
- }
- if (internal::same_string(timezone, "Europe/Berlin") == 0) {
+ if (internal::same_string(timezone, "Europe/Berlin") == 0)
offset = 2;
- }
// All the data (years, month and remaining days) was calculated from
// March, 2000. Thus adjust the data to be from January, 1900.
@@ -167,9 +165,11 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
if (offset == 0) {
tm->tm_isdst = 0;
- } else {
+ } else if (offset > 0) {
tm->tm_isdst = 1;
tm->tm_hour += offset;
+ } else {
+ tm->tm_isdst = -1;
}
fclose(fp);
>From 5ce3de549fb78807ad6941b83a9c521ea4108a75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 20:04:11 +0200
Subject: [PATCH 15/27] removing mktime as dependency to localtime_s
---
libc/src/time/CMakeLists.txt | 1 -
libc/test/src/time/CMakeLists.txt | 1 -
2 files changed, 2 deletions(-)
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index 096d6bcc7dd8ef..57b229d029c92e 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -94,7 +94,6 @@ add_entrypoint_object(
.time_utils
libc.hdr.types.time_t
libc.include.time
- libc.src.time.mktime
)
add_entrypoint_object(
diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
index 54e078cff83c15..376bdcac836bab 100644
--- a/libc/test/src/time/CMakeLists.txt
+++ b/libc/test/src/time/CMakeLists.txt
@@ -116,7 +116,6 @@ add_libc_unittest(
DEPENDS
libc.include.time
libc.hdr.types.time_t
- libc.src.time.mktime
libc.src.time.localtime_s
libc.src.time.time_utils
)
>From 03e50941780e1ee4095d2596a257a58847c81d3a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 20:11:41 +0200
Subject: [PATCH 16/27] revert ctime tests
---
libc/test/src/time/ctime_test.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/test/src/time/ctime_test.cpp b/libc/test/src/time/ctime_test.cpp
index 554b72d23fd4de..7ec71bb1e4ed1e 100644
--- a/libc/test/src/time/ctime_test.cpp
+++ b/libc/test/src/time/ctime_test.cpp
@@ -22,7 +22,7 @@ TEST(LlvmLibcCtime, ValidUnixTimestamp0) {
char *result;
t = 0;
result = LIBC_NAMESPACE::ctime(&t);
- ASSERT_STREQ("Thu Jan 1 01:00:00 1970\n", result);
+ ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", result);
}
TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
@@ -30,7 +30,7 @@ TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
char *result;
t = 2147483647;
result = LIBC_NAMESPACE::ctime(&t);
- ASSERT_STREQ("Tue Jan 19 04:14:07 2038\n", result);
+ ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", result);
}
TEST(LlvmLibcCtime, InvalidArgument) {
>From 60e07cdf36446d3ce3ee0f7d7cbb7fedafadbea5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 20:12:54 +0200
Subject: [PATCH 17/27] revert ctime_r tests
---
libc/test/src/time/ctime_r_test.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/test/src/time/ctime_r_test.cpp b/libc/test/src/time/ctime_r_test.cpp
index b03e8cecd2e9df..9ce6f75f754849 100644
--- a/libc/test/src/time/ctime_r_test.cpp
+++ b/libc/test/src/time/ctime_r_test.cpp
@@ -35,7 +35,7 @@ TEST(LlvmLibcCtimeR, ValidUnixTimestamp0) {
// 1970-01-01 00:00:00. Test with a valid buffer size.
t = 0;
result = LIBC_NAMESPACE::ctime_r(&t, buffer);
- ASSERT_STREQ("Thu Jan 1 02:00:00 1970\n", result);
+ ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", result);
}
TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
@@ -45,7 +45,7 @@ TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
// 2038-01-19 03:14:07. Test with a valid buffer size.
t = 2147483647;
result = LIBC_NAMESPACE::ctime_r(&t, buffer);
- ASSERT_STREQ("Tue Jan 19 05:14:07 2038\n", result);
+ ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", result);
}
TEST(LlvmLibcCtimeR, InvalidArgument) {
>From a8074d447926614f10e3d91795f5f53a4508caa0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 21:18:10 +0200
Subject: [PATCH 18/27] fix: daylight saving time
---
libc/src/time/time_utils.cpp | 18 +++++++-----------
libc/src/time/time_utils.h | 4 ----
libc/test/src/time/ctime_r_test.cpp | 8 ++++----
libc/test/src/time/ctime_test.cpp | 4 ++--
libc/test/src/time/localtime_r_test.cpp | 8 ++++----
libc/test/src/time/localtime_s_test.cpp | 4 ++--
libc/test/src/time/localtime_test.cpp | 4 ++--
7 files changed, 21 insertions(+), 29 deletions(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 90fec7cf730286..df55090185f3e2 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -140,12 +140,11 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
} else if (fgets(timezone, sizeof(timezone), fp) == NULL)
return time_utils::out_of_range();
- int offset;
+ // UTC = 0
+ int offset = 0;
// TODO: Add more timezones
- if (internal::same_string(timezone, "UTC") == 0)
- offset = 0;
if (internal::same_string(timezone, "Europe/Berlin") == 0)
- offset = 2;
+ offset = 1;
// All the data (years, month and remaining days) was calculated from
// March, 2000. Thus adjust the data to be from January, 1900.
@@ -163,14 +162,11 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
tm->tm_sec =
static_cast<int>(remainingSeconds % TimeConstants::SECONDS_PER_MIN);
- if (offset == 0) {
- tm->tm_isdst = 0;
- } else if (offset > 0) {
- tm->tm_isdst = 1;
- tm->tm_hour += offset;
- } else {
- tm->tm_isdst = -1;
+ set_dst(tm);
+ if (tm->tm_isdst > 0) {
+ tm->tm_hour += 1;
}
+ tm->tm_hour += offset;
fclose(fp);
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 929487a16567c5..aba8c7f2c14920 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -168,8 +168,6 @@ LIBC_INLINE struct tm *localtime(const time_t *t_ptr) {
return nullptr;
}
- set_dst(&result);
-
return &result;
}
@@ -183,8 +181,6 @@ LIBC_INLINE struct tm *localtime_internal(const time_t *t_ptr,
return nullptr;
}
- set_dst(input);
-
return input;
}
diff --git a/libc/test/src/time/ctime_r_test.cpp b/libc/test/src/time/ctime_r_test.cpp
index 9ce6f75f754849..5ca4b5b1ea228d 100644
--- a/libc/test/src/time/ctime_r_test.cpp
+++ b/libc/test/src/time/ctime_r_test.cpp
@@ -32,20 +32,20 @@ TEST(LlvmLibcCtimeR, ValidUnixTimestamp0) {
char buffer[TimeConstants::ASCTIME_BUFFER_SIZE];
time_t t;
char *result;
- // 1970-01-01 00:00:00. Test with a valid buffer size.
+ // 1970-01-01 01:00:00. Test with a valid buffer size.
t = 0;
result = LIBC_NAMESPACE::ctime_r(&t, buffer);
- ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", result);
+ ASSERT_STREQ("Thu Jan 1 01:00:00 1970\n", result);
}
TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
char buffer[TimeConstants::ASCTIME_BUFFER_SIZE];
time_t t;
char *result;
- // 2038-01-19 03:14:07. Test with a valid buffer size.
+ // 2038-01-19 04:14:07. Test with a valid buffer size.
t = 2147483647;
result = LIBC_NAMESPACE::ctime_r(&t, buffer);
- ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", result);
+ ASSERT_STREQ("Tue Jan 19 04:14:07 2038\n", result);
}
TEST(LlvmLibcCtimeR, InvalidArgument) {
diff --git a/libc/test/src/time/ctime_test.cpp b/libc/test/src/time/ctime_test.cpp
index 7ec71bb1e4ed1e..554b72d23fd4de 100644
--- a/libc/test/src/time/ctime_test.cpp
+++ b/libc/test/src/time/ctime_test.cpp
@@ -22,7 +22,7 @@ TEST(LlvmLibcCtime, ValidUnixTimestamp0) {
char *result;
t = 0;
result = LIBC_NAMESPACE::ctime(&t);
- ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", result);
+ ASSERT_STREQ("Thu Jan 1 01:00:00 1970\n", result);
}
TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
@@ -30,7 +30,7 @@ TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) {
char *result;
t = 2147483647;
result = LIBC_NAMESPACE::ctime(&t);
- ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", result);
+ ASSERT_STREQ("Tue Jan 19 04:14:07 2038\n", result);
}
TEST(LlvmLibcCtime, InvalidArgument) {
diff --git a/libc/test/src/time/localtime_r_test.cpp b/libc/test/src/time/localtime_r_test.cpp
index 1a3e4be11e6c36..03b885fa4aa96b 100644
--- a/libc/test/src/time/localtime_r_test.cpp
+++ b/libc/test/src/time/localtime_r_test.cpp
@@ -21,7 +21,7 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
ASSERT_EQ(70, input.tm_year);
ASSERT_EQ(0, input.tm_mon);
ASSERT_EQ(1, input.tm_mday);
- ASSERT_EQ(2, input.tm_hour);
+ ASSERT_EQ(1, input.tm_hour);
ASSERT_EQ(0, input.tm_min);
ASSERT_EQ(0, input.tm_sec);
ASSERT_EQ(4, input.tm_wday);
@@ -31,7 +31,7 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
ASSERT_EQ(70, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
ASSERT_EQ(1, result->tm_mday);
- ASSERT_EQ(2, result->tm_hour);
+ ASSERT_EQ(1, result->tm_hour);
ASSERT_EQ(0, result->tm_min);
ASSERT_EQ(0, result->tm_sec);
ASSERT_EQ(4, result->tm_wday);
@@ -55,7 +55,7 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
ASSERT_EQ(138, input.tm_year);
ASSERT_EQ(0, input.tm_mon);
ASSERT_EQ(19, input.tm_mday);
- ASSERT_EQ(5, input.tm_hour);
+ ASSERT_EQ(4, input.tm_hour);
ASSERT_EQ(14, input.tm_min);
ASSERT_EQ(7, input.tm_sec);
ASSERT_EQ(2, input.tm_wday);
@@ -65,7 +65,7 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
ASSERT_EQ(138, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
ASSERT_EQ(19, result->tm_mday);
- ASSERT_EQ(5, result->tm_hour);
+ ASSERT_EQ(4, result->tm_hour);
ASSERT_EQ(14, result->tm_min);
ASSERT_EQ(7, result->tm_sec);
ASSERT_EQ(2, result->tm_wday);
diff --git a/libc/test/src/time/localtime_s_test.cpp b/libc/test/src/time/localtime_s_test.cpp
index cf97945a8fe4a0..aeb658fe6fd871 100644
--- a/libc/test/src/time/localtime_s_test.cpp
+++ b/libc/test/src/time/localtime_s_test.cpp
@@ -31,7 +31,7 @@ TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp0) {
ASSERT_EQ(70, input.tm_year);
ASSERT_EQ(0, input.tm_mon);
ASSERT_EQ(1, input.tm_mday);
- ASSERT_EQ(2, input.tm_hour);
+ ASSERT_EQ(1, input.tm_hour);
ASSERT_EQ(0, input.tm_min);
ASSERT_EQ(0, input.tm_sec);
ASSERT_EQ(4, input.tm_wday);
@@ -56,7 +56,7 @@ TEST(LlvmLibcLocaltimeS, ValidUnixTimestamp32Int) {
ASSERT_EQ(138, input.tm_year);
ASSERT_EQ(0, input.tm_mon);
ASSERT_EQ(19, input.tm_mday);
- ASSERT_EQ(5, input.tm_hour);
+ ASSERT_EQ(4, input.tm_hour);
ASSERT_EQ(14, input.tm_min);
ASSERT_EQ(7, input.tm_sec);
ASSERT_EQ(2, input.tm_wday);
diff --git a/libc/test/src/time/localtime_test.cpp b/libc/test/src/time/localtime_test.cpp
index 4143e7065596ff..293f0050c5802b 100644
--- a/libc/test/src/time/localtime_test.cpp
+++ b/libc/test/src/time/localtime_test.cpp
@@ -17,7 +17,7 @@ TEST(LlvmLibcLocaltime, ValidUnixTimestamp0) {
ASSERT_EQ(70, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
ASSERT_EQ(1, result->tm_mday);
- ASSERT_EQ(2, result->tm_hour);
+ ASSERT_EQ(1, result->tm_hour);
ASSERT_EQ(0, result->tm_min);
ASSERT_EQ(0, result->tm_sec);
ASSERT_EQ(4, result->tm_wday);
@@ -31,7 +31,7 @@ TEST(LlvmLibcLocaltime, ValidUnixTimestamp32Int) {
ASSERT_EQ(138, result->tm_year);
ASSERT_EQ(0, result->tm_mon);
ASSERT_EQ(19, result->tm_mday);
- ASSERT_EQ(5, result->tm_hour);
+ ASSERT_EQ(4, result->tm_hour);
ASSERT_EQ(14, result->tm_min);
ASSERT_EQ(7, result->tm_sec);
ASSERT_EQ(2, result->tm_wday);
>From b3e7a24d06390939220dea8d65e605d4c5881e5d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 21:31:44 +0200
Subject: [PATCH 19/27] update documentation for libc
---
libc/docs/date_and_time.rst | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/libc/docs/date_and_time.rst b/libc/docs/date_and_time.rst
index ab7c7c3de68677..232faa81155456 100644
--- a/libc/docs/date_and_time.rst
+++ b/libc/docs/date_and_time.rst
@@ -71,9 +71,9 @@ Implementation Status
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
| clock_settime | | | | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
-| ctime | | | | | | | | | | | | |
+| ctime | |check| | | | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
-| ctime_r | | | | | | | | | | | | |
+| ctime_r | |check| | | | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
| difftime | |check| | |check| | | |check| | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
@@ -85,11 +85,11 @@ Implementation Status
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
| gmtime_r | |check| | |check| | | |check| | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
-| localtime | | | | | | | | | | | | |
+| localtime | |check| | | | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
-| localtime_r | | | | | | | | | | | | |
+| localtime_r | |check| | | | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
-| localtime_s | | | | | | | | | | | | |
+| localtime_s | |check| | | | | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
| mktime | |check| | |check| | | |check| | | | | | | | | |
+---------------------+---------+---------+---------+-----------------+---------+---------+---------+---------+---------+---------+---------+---------+
>From f9cb5acdf9eb3f0094089caf40145bf9d3d058bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 22:31:46 +0200
Subject: [PATCH 20/27] refactor: file read
---
libc/src/time/time_utils.cpp | 31 ++++++++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index df55090185f3e2..063d9da4ca408b 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -27,6 +27,26 @@ static int64_t computeRemainingYears(int64_t daysPerYears,
return years;
}
+volatile int lock = 0;
+
+void release_file(FILE *fp) {
+ lock = 0;
+ fclose(fp);
+}
+
+void acquire_file(FILE *fp, char *timezone) {
+ while (1) {
+ if (lock == 0) {
+ lock = 1;
+ break;
+ }
+ }
+
+ if (fgets(timezone, sizeof(timezone), fp) == NULL) {
+ release_file(fp);
+ }
+}
+
// 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.
@@ -132,13 +152,18 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
char timezone[128];
- FILE *fp;
+ FILE *fp = NULL;
fp = fopen("/etc/timezone", "rb");
if (fp == NULL) {
// TODO: implement getting timezone from `TZ` environment variable and
// storing the value in `timezone`
- } else if (fgets(timezone, sizeof(timezone), fp) == NULL)
+ } else {
+ acquire_file(fp, timezone);
+ }
+
+ if (lock == 0) {
return time_utils::out_of_range();
+ }
// UTC = 0
int offset = 0;
@@ -168,7 +193,7 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
}
tm->tm_hour += offset;
- fclose(fp);
+ release_file(fp);
return 0;
}
>From 4e12c6748e7d0b56fd1bc03f1ee7eb842b30401f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 22:35:51 +0200
Subject: [PATCH 21/27] fix: file read
---
libc/src/time/time_utils.cpp | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 063d9da4ca408b..54a1b113ddb783 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -27,17 +27,17 @@ static int64_t computeRemainingYears(int64_t daysPerYears,
return years;
}
-volatile int lock = 0;
+volatile int file_usage = 0;
void release_file(FILE *fp) {
- lock = 0;
+ file_usage = 0;
fclose(fp);
}
void acquire_file(FILE *fp, char *timezone) {
while (1) {
- if (lock == 0) {
- lock = 1;
+ if (file_usage == 0) {
+ file_usage = 1;
break;
}
}
@@ -161,7 +161,8 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
acquire_file(fp, timezone);
}
- if (lock == 0) {
+ if (file_usage == 0) {
+ release_file(fp);
return time_utils::out_of_range();
}
>From ea2120e80ea18e1b1f9c222a22ce8ab78fabf94d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Sun, 6 Oct 2024 22:48:37 +0200
Subject: [PATCH 22/27] fix: timezone char length
---
libc/src/time/time_utils.cpp | 8 ++++----
libc/src/time/time_utils.h | 2 ++
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 54a1b113ddb783..2f33e056b8c1ec 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -34,7 +34,7 @@ void release_file(FILE *fp) {
fclose(fp);
}
-void acquire_file(FILE *fp, char *timezone) {
+void acquire_file(FILE *fp, char *timezone, size_t timezone_size) {
while (1) {
if (file_usage == 0) {
file_usage = 1;
@@ -42,7 +42,7 @@ void acquire_file(FILE *fp, char *timezone) {
}
}
- if (fgets(timezone, sizeof(timezone), fp) == NULL) {
+ if (fgets(timezone, (int)timezone_size, fp) == NULL) {
release_file(fp);
}
}
@@ -150,7 +150,7 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
if (years > INT_MAX || years < INT_MIN)
return time_utils::out_of_range();
- char timezone[128];
+ char timezone[TimeConstants::TIMEZONE_SIZE];
FILE *fp = NULL;
fp = fopen("/etc/timezone", "rb");
@@ -158,7 +158,7 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
// TODO: implement getting timezone from `TZ` environment variable and
// storing the value in `timezone`
} else {
- acquire_file(fp, timezone);
+ acquire_file(fp, timezone, TimeConstants::TIMEZONE_SIZE);
}
if (file_usage == 0) {
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index aba8c7f2c14920..780d5242cfa681 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -81,6 +81,8 @@ struct TimeConstants {
static constexpr int END_OF32_BIT_EPOCH_YEAR = 2038;
static constexpr time_t OUT_OF_RANGE_RETURN_VALUE = -1;
+
+ static constexpr size_t TIMEZONE_SIZE = 128;
};
// Update the "tm" structure's year, month, etc. members from seconds.
>From 1203b85d1ebc3a495d4ac56fb7e6d479cce13597 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Tue, 8 Oct 2024 15:03:49 +0200
Subject: [PATCH 23/27] timezone implementation
---
libc/src/time/CMakeLists.txt | 13 +++
libc/src/time/time_utils.cpp | 45 +++++----
libc/src/time/timezone.cpp | 52 ++++++++++
libc/src/time/timezone.h | 33 +++++++
libc/test/src/time/CMakeLists.txt | 5 +
libc/test/src/time/localtime_r_test.cpp | 24 ++++-
libc/test/src/time/localtime_test.cpp | 121 +++++++++++++++++++++++-
7 files changed, 270 insertions(+), 23 deletions(-)
create mode 100644 libc/src/time/timezone.cpp
create mode 100644 libc/src/time/timezone.h
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index 57b229d029c92e..008832c5963c25 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -14,6 +14,18 @@ add_object_library(
libc.src.errno.errno
)
+add_object_library(
+ timezone
+ SRCS
+ timezone.cpp
+ HDRS
+ timezone.h
+ DEPENDS
+ libc.include.time
+ libc.src.__support.CPP.limits
+ libc.src.errno.errno
+)
+
add_entrypoint_object(
asctime
SRCS
@@ -68,6 +80,7 @@ add_entrypoint_object(
localtime.h
DEPENDS
.time_utils
+ .timezone
libc.hdr.types.time_t
libc.include.time
)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 2f33e056b8c1ec..c5d3c88d5c6260 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -7,10 +7,13 @@
//===----------------------------------------------------------------------===//
#include "src/time/time_utils.h"
+#include "src/time/timezone.h"
#include "src/__support/CPP/limits.h" // INT_MIN, INT_MAX
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
+#include "src/__support/CPP/string_view.h"
#include <stdio.h>
+#include <stdlib.h>
namespace LIBC_NAMESPACE_DECL {
namespace time_utils {
@@ -29,9 +32,10 @@ static int64_t computeRemainingYears(int64_t daysPerYears,
volatile int file_usage = 0;
-void release_file(FILE *fp) {
+void release_file(FILE *fp, char *timezone) {
file_usage = 0;
fclose(fp);
+ free(timezone);
}
void acquire_file(FILE *fp, char *timezone, size_t timezone_size) {
@@ -43,7 +47,7 @@ void acquire_file(FILE *fp, char *timezone, size_t timezone_size) {
}
if (fgets(timezone, (int)timezone_size, fp) == NULL) {
- release_file(fp);
+ release_file(fp, timezone);
}
}
@@ -150,27 +154,25 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
if (years > INT_MAX || years < INT_MIN)
return time_utils::out_of_range();
- char timezone[TimeConstants::TIMEZONE_SIZE];
-
+ char *timezone = (char *)malloc(sizeof(char) * TimeConstants::TIMEZONE_SIZE);
+ timezone = getenv("TZ");
FILE *fp = NULL;
- fp = fopen("/etc/timezone", "rb");
- if (fp == NULL) {
- // TODO: implement getting timezone from `TZ` environment variable and
- // storing the value in `timezone`
- } else {
+ if (timezone == NULL) {
+ timezone = (char *)realloc(timezone, sizeof(char) * TimeConstants::TIMEZONE_SIZE);
+ fp = fopen("/etc/timezone", "rb");
+ if (fp == NULL) {
+ return time_utils::out_of_range();
+ }
+
acquire_file(fp, timezone, TimeConstants::TIMEZONE_SIZE);
}
- if (file_usage == 0) {
- release_file(fp);
+ if (fp != NULL && file_usage == 0) {
+ release_file(fp, timezone);
return time_utils::out_of_range();
}
- // UTC = 0
- int offset = 0;
- // TODO: Add more timezones
- if (internal::same_string(timezone, "Europe/Berlin") == 0)
- offset = 1;
+ int offset = timezone::get_timezone_offset(timezone);
// All the data (years, month and remaining days) was calculated from
// March, 2000. Thus adjust the data to be from January, 1900.
@@ -189,12 +191,17 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
static_cast<int>(remainingSeconds % TimeConstants::SECONDS_PER_MIN);
set_dst(tm);
- if (tm->tm_isdst > 0) {
+ if (tm->tm_isdst > 0 && offset != 0) {
tm->tm_hour += 1;
}
- tm->tm_hour += offset;
- release_file(fp);
+ if (offset != 0) {
+ tm->tm_hour += offset;
+ }
+
+ if (file_usage == 1) {
+ release_file(fp, timezone);
+ }
return 0;
}
diff --git a/libc/src/time/timezone.cpp b/libc/src/time/timezone.cpp
new file mode 100644
index 00000000000000..954293ee56bbf0
--- /dev/null
+++ b/libc/src/time/timezone.cpp
@@ -0,0 +1,52 @@
+//===-- Implementation of timezone functions ------------------------------===//
+//
+// 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/time/timezone.h"
+#include "src/__support/CPP/limits.h" // INT_MIN, INT_MAX
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/CPP/string_view.h"
+
+#define BUF_SIZE 1024
+
+namespace LIBC_NAMESPACE_DECL {
+namespace timezone {
+
+using LIBC_NAMESPACE::time_utils::TimeConstants;
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int get_timezone_offset(char *timezone) {
+ int offset = 0;
+ LIBC_NAMESPACE::cpp::string_view tz(timezone);
+
+ if (tz.starts_with("America")) {
+ if (tz.ends_with("San_Francisco")) {
+ offset = -8;
+ }
+
+ if (tz.starts_with("America/New_York")) {
+ offset = -5;
+ }
+ }
+
+ if (tz.starts_with("Europe")) {
+ offset = 1;
+
+ if (tz.ends_with("Moscow")) {
+ offset = 2;
+ }
+ }
+
+ return offset;
+}
+
+} // namespace timezone
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/timezone.h b/libc/src/time/timezone.h
new file mode 100644
index 00000000000000..87f45bdc5d6c2e
--- /dev/null
+++ b/libc/src/time/timezone.h
@@ -0,0 +1,33 @@
+//===-- Implementation of timezone functions ------------------------------===//
+//
+// 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_TIMEZONE_H
+#define LLVM_LIBC_SRC_TIME_TIMEZONE_H
+
+#include <stddef.h> // For size_t.
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/errno/libc_errno.h"
+#include "src/time/mktime.h"
+
+#include <stdint.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace timezone {
+
+#define TZ_HEADER "TZif"
+
+extern int get_timezone_offset(char *timezone);
+
+} // namespace timezone
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_TIMEZONE_H
diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
index 376bdcac836bab..1c04295613ab4a 100644
--- a/libc/test/src/time/CMakeLists.txt
+++ b/libc/test/src/time/CMakeLists.txt
@@ -46,6 +46,7 @@ add_libc_unittest(
libc.hdr.types.time_t
libc.src.time.ctime
libc.src.time.time_utils
+ libc.src.time.timezone
)
add_libc_unittest(
@@ -64,6 +65,7 @@ add_libc_unittest(
libc.hdr.types.time_t
libc.src.time.ctime_r
libc.src.time.time_utils
+ libc.src.time.timezone
)
add_libc_unittest(
@@ -82,6 +84,7 @@ add_libc_unittest(
libc.hdr.types.time_t
libc.src.time.localtime
libc.src.time.time_utils
+ libc.src.time.timezone
)
add_libc_unittest(
@@ -100,6 +103,7 @@ add_libc_unittest(
libc.hdr.types.time_t
libc.src.time.localtime_r
libc.src.time.time_utils
+ libc.src.time.timezone
)
add_libc_unittest(
@@ -118,6 +122,7 @@ add_libc_unittest(
libc.hdr.types.time_t
libc.src.time.localtime_s
libc.src.time.time_utils
+ libc.src.time.timezone
)
add_libc_test(
diff --git a/libc/test/src/time/localtime_r_test.cpp b/libc/test/src/time/localtime_r_test.cpp
index 03b885fa4aa96b..18b2ef84992226 100644
--- a/libc/test/src/time/localtime_r_test.cpp
+++ b/libc/test/src/time/localtime_r_test.cpp
@@ -9,11 +9,27 @@
#include "src/time/localtime_r.h"
#include "src/time/time_utils.h"
#include "test/UnitTest/Test.h"
-#include "test/src/time/TmHelper.h"
-using LIBC_NAMESPACE::time_utils::TimeConstants;
+#include <string.h>
+
+extern char **environ;
+
+void set_env_var(const char* env) {
+ int i = 0;
+ if (environ[i] != NULL) {
+ i++;
+ }
+
+ environ[i] = (char*)malloc(strlen(env)+1);
+ if (environ[i] != nullptr) {
+ memcpy(environ[i], env, strlen(env)+1);
+ environ[i+1] = nullptr;
+ }
+}
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
+ set_env_var("TZ=Europe/Berlin");
+
struct tm input;
time_t t_ptr = 0;
struct tm *result = LIBC_NAMESPACE::localtime_r(&t_ptr, &input);
@@ -40,6 +56,8 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp0) {
}
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
+ set_env_var("TZ=Europe/Berlin");
+
time_t t_ptr = 2147483647;
struct tm input = (struct tm){.tm_sec = 0,
.tm_min = 0,
@@ -74,6 +92,8 @@ TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32Int) {
}
TEST(LlvmLibcLocaltimeR, ValidUnixTimestamp32IntDst) {
+ set_env_var("TZ=Europe/Berlin");
+
time_t t_ptr = 1627225465;
struct tm input = (struct tm){.tm_sec = 0,
.tm_min = 0,
diff --git a/libc/test/src/time/localtime_test.cpp b/libc/test/src/time/localtime_test.cpp
index 293f0050c5802b..cf373ced8b7fde 100644
--- a/libc/test/src/time/localtime_test.cpp
+++ b/libc/test/src/time/localtime_test.cpp
@@ -6,12 +6,29 @@
//
//===----------------------------------------------------------------------===//
-#include "src/errno/libc_errno.h"
#include "src/time/localtime.h"
#include "test/UnitTest/Test.h"
-#include "test/src/time/TmHelper.h"
+
+#include <string.h>
+
+extern char **environ;
+
+void set_env_var(const char* env) {
+ int i = 0;
+ if (environ[i] != NULL) {
+ i++;
+ }
+
+ environ[i] = (char*)malloc(strlen(env)+1);
+ if (environ[i] != nullptr) {
+ memcpy(environ[i], env, strlen(env)+1);
+ environ[i+1] = nullptr;
+ }
+}
TEST(LlvmLibcLocaltime, ValidUnixTimestamp0) {
+ set_env_var("TZ=Europe/Berlin");
+
const time_t t_ptr = 0;
struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
ASSERT_EQ(70, result->tm_year);
@@ -26,6 +43,8 @@ TEST(LlvmLibcLocaltime, ValidUnixTimestamp0) {
}
TEST(LlvmLibcLocaltime, ValidUnixTimestamp32Int) {
+ set_env_var("TZ=Europe/Berlin");
+
time_t t_ptr = 2147483647;
struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
ASSERT_EQ(138, result->tm_year);
@@ -40,6 +59,8 @@ TEST(LlvmLibcLocaltime, ValidUnixTimestamp32Int) {
}
TEST(LlvmLibcLocaltime, ValidUnixTimestamp32IntDst) {
+ set_env_var("TZ=Europe/Berlin");
+
time_t t_ptr = 1627225465;
struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
ASSERT_EQ(121, result->tm_year);
@@ -52,3 +73,99 @@ TEST(LlvmLibcLocaltime, ValidUnixTimestamp32IntDst) {
ASSERT_EQ(205, result->tm_yday);
ASSERT_EQ(1, result->tm_isdst);
}
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestampTzEnvironmentVariableUsaPst) {
+ set_env_var("TZ=America/San_Francisco");
+
+ time_t t_ptr = 1627225465;
+ struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
+ ASSERT_EQ(121, result->tm_year);
+ ASSERT_EQ(6, result->tm_mon);
+ ASSERT_EQ(25, result->tm_mday);
+ ASSERT_EQ(8, result->tm_hour);
+ ASSERT_EQ(4, result->tm_min);
+ ASSERT_EQ(25, result->tm_sec);
+ ASSERT_EQ(0, result->tm_wday);
+ ASSERT_EQ(205, result->tm_yday);
+ ASSERT_EQ(1, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestampTzEnvironmentVariableUsaEst) {
+ set_env_var("TZ=America/New_York");
+
+ time_t t_ptr = 1627225465;
+ struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
+ ASSERT_EQ(121, result->tm_year);
+ ASSERT_EQ(6, result->tm_mon);
+ ASSERT_EQ(25, result->tm_mday);
+ ASSERT_EQ(11, result->tm_hour);
+ ASSERT_EQ(4, result->tm_min);
+ ASSERT_EQ(25, result->tm_sec);
+ ASSERT_EQ(0, result->tm_wday);
+ ASSERT_EQ(205, result->tm_yday);
+ ASSERT_EQ(1, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestampTzEnvironmentVariableUTC) {
+ set_env_var("TZ=UTC");
+
+ time_t t_ptr = 1627225465;
+ struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
+ ASSERT_EQ(121, result->tm_year);
+ ASSERT_EQ(6, result->tm_mon);
+ ASSERT_EQ(25, result->tm_mday);
+ ASSERT_EQ(15, result->tm_hour);
+ ASSERT_EQ(4, result->tm_min);
+ ASSERT_EQ(25, result->tm_sec);
+ ASSERT_EQ(0, result->tm_wday);
+ ASSERT_EQ(205, result->tm_yday);
+ ASSERT_EQ(1, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestampTzEnvironmentVariableGMT) {
+ set_env_var("TZ=GMT");
+
+ time_t t_ptr = 1627225465;
+ struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
+ ASSERT_EQ(121, result->tm_year);
+ ASSERT_EQ(6, result->tm_mon);
+ ASSERT_EQ(25, result->tm_mday);
+ ASSERT_EQ(15, result->tm_hour);
+ ASSERT_EQ(4, result->tm_min);
+ ASSERT_EQ(25, result->tm_sec);
+ ASSERT_EQ(0, result->tm_wday);
+ ASSERT_EQ(205, result->tm_yday);
+ ASSERT_EQ(1, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestampTzEnvironmentVariableEuropeBerlin) {
+ set_env_var("TZ=Europe/Berlin");
+
+ time_t t_ptr = 1627225465;
+ struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
+ ASSERT_EQ(121, result->tm_year);
+ ASSERT_EQ(6, result->tm_mon);
+ ASSERT_EQ(25, result->tm_mday);
+ ASSERT_EQ(17, result->tm_hour);
+ ASSERT_EQ(4, result->tm_min);
+ ASSERT_EQ(25, result->tm_sec);
+ ASSERT_EQ(0, result->tm_wday);
+ ASSERT_EQ(205, result->tm_yday);
+ ASSERT_EQ(1, result->tm_isdst);
+}
+
+TEST(LlvmLibcLocaltime, ValidUnixTimestampTzEnvironmentVariableRussiaMoscow) {
+ set_env_var("TZ=Europe/Moscow");
+
+ time_t t_ptr = 1627225465;
+ struct tm *result = LIBC_NAMESPACE::localtime(&t_ptr);
+ ASSERT_EQ(121, result->tm_year);
+ ASSERT_EQ(6, result->tm_mon);
+ ASSERT_EQ(25, result->tm_mday);
+ ASSERT_EQ(18, result->tm_hour);
+ ASSERT_EQ(4, result->tm_min);
+ ASSERT_EQ(25, result->tm_sec);
+ ASSERT_EQ(0, result->tm_wday);
+ ASSERT_EQ(205, result->tm_yday);
+ ASSERT_EQ(1, result->tm_isdst);
+}
>From 47a35891cacb2104dda756f5d434c5867a13b362 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Tue, 8 Oct 2024 15:04:22 +0200
Subject: [PATCH 24/27] format code with clang-format
---
libc/src/time/time_utils.cpp | 7 ++++---
libc/src/time/timezone.cpp | 4 ++--
libc/src/time/timezone.h | 2 +-
libc/test/src/time/localtime_r_test.cpp | 10 +++++-----
libc/test/src/time/localtime_test.cpp | 10 +++++-----
5 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index c5d3c88d5c6260..31034a8fb66428 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -7,11 +7,11 @@
//===----------------------------------------------------------------------===//
#include "src/time/time_utils.h"
-#include "src/time/timezone.h"
#include "src/__support/CPP/limits.h" // INT_MIN, INT_MAX
+#include "src/__support/CPP/string_view.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/__support/CPP/string_view.h"
+#include "src/time/timezone.h"
#include <stdio.h>
#include <stdlib.h>
@@ -158,7 +158,8 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
timezone = getenv("TZ");
FILE *fp = NULL;
if (timezone == NULL) {
- timezone = (char *)realloc(timezone, sizeof(char) * TimeConstants::TIMEZONE_SIZE);
+ timezone =
+ (char *)realloc(timezone, sizeof(char) * TimeConstants::TIMEZONE_SIZE);
fp = fopen("/etc/timezone", "rb");
if (fp == NULL) {
return time_utils::out_of_range();
diff --git a/libc/src/time/timezone.cpp b/libc/src/time/timezone.cpp
index 954293ee56bbf0..8fd3cb86425fac 100644
--- a/libc/src/time/timezone.cpp
+++ b/libc/src/time/timezone.cpp
@@ -6,12 +6,12 @@
//
//===----------------------------------------------------------------------===//
-#include "src/time/time_utils.h"
#include "src/time/timezone.h"
#include "src/__support/CPP/limits.h" // INT_MIN, INT_MAX
+#include "src/__support/CPP/string_view.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/__support/CPP/string_view.h"
+#include "src/time/time_utils.h"
#define BUF_SIZE 1024
diff --git a/libc/src/time/timezone.h b/libc/src/time/timezone.h
index 87f45bdc5d6c2e..26bf52c6cb9286 100644
--- a/libc/src/time/timezone.h
+++ b/libc/src/time/timezone.h
@@ -12,9 +12,9 @@
#include <stddef.h> // For size_t.
#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/string_view.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/__support/CPP/string_view.h"
#include "src/errno/libc_errno.h"
#include "src/time/mktime.h"
diff --git a/libc/test/src/time/localtime_r_test.cpp b/libc/test/src/time/localtime_r_test.cpp
index 18b2ef84992226..2205c59ab7e527 100644
--- a/libc/test/src/time/localtime_r_test.cpp
+++ b/libc/test/src/time/localtime_r_test.cpp
@@ -14,16 +14,16 @@
extern char **environ;
-void set_env_var(const char* env) {
+void set_env_var(const char *env) {
int i = 0;
if (environ[i] != NULL) {
i++;
}
- environ[i] = (char*)malloc(strlen(env)+1);
- if (environ[i] != nullptr) {
- memcpy(environ[i], env, strlen(env)+1);
- environ[i+1] = nullptr;
+ environ[i] = (char *)malloc(strlen(env) + 1);
+ if (environ[i] != NULL) {
+ memcpy(environ[i], env, strlen(env) + 1);
+ environ[i + 1] = NULL;
}
}
diff --git a/libc/test/src/time/localtime_test.cpp b/libc/test/src/time/localtime_test.cpp
index cf373ced8b7fde..b360734ef7379c 100644
--- a/libc/test/src/time/localtime_test.cpp
+++ b/libc/test/src/time/localtime_test.cpp
@@ -13,16 +13,16 @@
extern char **environ;
-void set_env_var(const char* env) {
+void set_env_var(const char *env) {
int i = 0;
if (environ[i] != NULL) {
i++;
}
- environ[i] = (char*)malloc(strlen(env)+1);
- if (environ[i] != nullptr) {
- memcpy(environ[i], env, strlen(env)+1);
- environ[i+1] = nullptr;
+ environ[i] = (char *)malloc(strlen(env) + 1);
+ if (environ[i] != NULL) {
+ memcpy(environ[i], env, strlen(env) + 1);
+ environ[i + 1] = NULL;
}
}
>From 6e9d078c7fd3ab4638995b455fb69b7d8cac1dff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Tue, 8 Oct 2024 15:25:38 +0200
Subject: [PATCH 25/27] use stack allocation of memory instead of heap
allocation
---
libc/src/time/time_utils.cpp | 12 +++++++-----
libc/src/time/timezone.cpp | 2 +-
libc/src/time/timezone.h | 2 --
libc/test/src/time/localtime_test.cpp | 2 +-
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 31034a8fb66428..beab005f29891c 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -14,6 +14,7 @@
#include "src/time/timezone.h"
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
namespace LIBC_NAMESPACE_DECL {
namespace time_utils {
@@ -154,12 +155,13 @@ int64_t update_from_seconds(int64_t total_seconds, struct tm *tm) {
if (years > INT_MAX || years < INT_MIN)
return time_utils::out_of_range();
- char *timezone = (char *)malloc(sizeof(char) * TimeConstants::TIMEZONE_SIZE);
- timezone = getenv("TZ");
+ char timezone[TimeConstants::TIMEZONE_SIZE];
+ char *env_tz = getenv("TZ");
FILE *fp = NULL;
- if (timezone == NULL) {
- timezone =
- (char *)realloc(timezone, sizeof(char) * TimeConstants::TIMEZONE_SIZE);
+ if (env_tz) {
+ strncpy(timezone, env_tz, sizeof(timezone));
+ timezone[sizeof(timezone) - 1] = '\0';
+ } else {
fp = fopen("/etc/timezone", "rb");
if (fp == NULL) {
return time_utils::out_of_range();
diff --git a/libc/src/time/timezone.cpp b/libc/src/time/timezone.cpp
index 8fd3cb86425fac..3434fe363b296e 100644
--- a/libc/src/time/timezone.cpp
+++ b/libc/src/time/timezone.cpp
@@ -32,7 +32,7 @@ int get_timezone_offset(char *timezone) {
offset = -8;
}
- if (tz.starts_with("America/New_York")) {
+ if (tz.ends_with("New_York")) {
offset = -5;
}
}
diff --git a/libc/src/time/timezone.h b/libc/src/time/timezone.h
index 26bf52c6cb9286..9155551ee8887b 100644
--- a/libc/src/time/timezone.h
+++ b/libc/src/time/timezone.h
@@ -23,8 +23,6 @@
namespace LIBC_NAMESPACE_DECL {
namespace timezone {
-#define TZ_HEADER "TZif"
-
extern int get_timezone_offset(char *timezone);
} // namespace timezone
diff --git a/libc/test/src/time/localtime_test.cpp b/libc/test/src/time/localtime_test.cpp
index b360734ef7379c..ef59c8b867db1e 100644
--- a/libc/test/src/time/localtime_test.cpp
+++ b/libc/test/src/time/localtime_test.cpp
@@ -154,7 +154,7 @@ TEST(LlvmLibcLocaltime, ValidUnixTimestampTzEnvironmentVariableEuropeBerlin) {
ASSERT_EQ(1, result->tm_isdst);
}
-TEST(LlvmLibcLocaltime, ValidUnixTimestampTzEnvironmentVariableRussiaMoscow) {
+TEST(LlvmLibcLocaltime, ValidUnixTimestampTzEnvironmentVariableEuropeMoscow) {
set_env_var("TZ=Europe/Moscow");
time_t t_ptr = 1627225465;
>From a810a4706838dcae6f2cee5e1e9c34f559df6278 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Tue, 8 Oct 2024 15:29:11 +0200
Subject: [PATCH 26/27] remove `free` function because stack allocation of
memory is now used
---
libc/src/time/time_utils.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index beab005f29891c..1c6d49a69ebc83 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -36,7 +36,6 @@ volatile int file_usage = 0;
void release_file(FILE *fp, char *timezone) {
file_usage = 0;
fclose(fp);
- free(timezone);
}
void acquire_file(FILE *fp, char *timezone, size_t timezone_size) {
>From 15aebcec9423f0b036c0b13b2cde094d1091ea77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=97=D0=B8=D1=88=D0=B0=D0=BD=20=D0=9C=D0=B8=D1=80=D0=B7?=
=?UTF-8?q?=D0=B0?= <zmirza at tutanota.de>
Date: Tue, 8 Oct 2024 15:37:50 +0200
Subject: [PATCH 27/27] added more timezones
---
libc/src/time/timezone.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/libc/src/time/timezone.cpp b/libc/src/time/timezone.cpp
index 3434fe363b296e..159027379dcf50 100644
--- a/libc/src/time/timezone.cpp
+++ b/libc/src/time/timezone.cpp
@@ -32,6 +32,10 @@ int get_timezone_offset(char *timezone) {
offset = -8;
}
+ if (tz.ends_with("Chicago")) {
+ offset = -4;
+ }
+
if (tz.ends_with("New_York")) {
offset = -5;
}
@@ -40,6 +44,10 @@ int get_timezone_offset(char *timezone) {
if (tz.starts_with("Europe")) {
offset = 1;
+ if (tz.ends_with("Lisbon")) {
+ offset = 0;
+ }
+
if (tz.ends_with("Moscow")) {
offset = 2;
}
More information about the libc-commits
mailing list