diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index af9a8bc992544..68030f7f1775b 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -203,6 +203,8 @@ set(TARGET_LIBC_ENTRYPOINTS # time.h entrypoints libc.src.time.asctime libc.src.time.asctime_r + libc.src.time.ctime + libc.src.time.ctime_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 6ebe2e4a29025..5894b591072ef 100644 --- a/libc/config/baremetal/riscv/entrypoints.txt +++ b/libc/config/baremetal/riscv/entrypoints.txt @@ -199,6 +199,8 @@ set(TARGET_LIBC_ENTRYPOINTS # time.h entrypoints libc.src.time.asctime libc.src.time.asctime_r + libc.src.time.ctime + libc.src.time.ctime_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 60aa7f5ccb319..64fbe1a250c0b 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -948,6 +948,8 @@ if(LLVM_LIBC_FULL_BUILD) # time.h entrypoints libc.src.time.asctime libc.src.time.asctime_r + libc.src.time.ctime + libc.src.time.ctime_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 9a2746dcb86f8..ff3d821c664c5 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -883,6 +883,8 @@ if(LLVM_LIBC_FULL_BUILD) # time.h entrypoints libc.src.time.asctime libc.src.time.asctime_r + libc.src.time.ctime + libc.src.time.ctime_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 3fd88fc0020e5..2a38db5bcdad8 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1003,6 +1003,8 @@ if(LLVM_LIBC_FULL_BUILD) # time.h entrypoints libc.src.time.asctime libc.src.time.asctime_r + libc.src.time.ctime + libc.src.time.ctime_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 d2344671831c7..69b40bef3160d 100644 --- a/libc/newhdrgen/yaml/time.yaml +++ b/libc/newhdrgen/yaml/time.yaml @@ -24,6 +24,19 @@ functions: arguments: - type: struct tm * - type: char * + - name: ctime + standard: + - stdc + return_type: char * + arguments: + - type: const time_t * + - name: ctime_r + standard: + - stdc + return_type: char * + arguments: + - type: const time_t * + - type: char * - name: clock standard: - stdc diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index a4ae3e1ff7d9c..c7b697d438a89 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -1600,6 +1600,19 @@ def StdC : StandardSpec<"stdc"> { ArgSpec, ] >, + FunctionSpec< + "ctime", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "ctime_r", + RetValSpec, + [ + ArgSpec, + ArgSpec, + ] + >, FunctionSpec< "clock", RetValSpec, diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt index 5680718715974..b3318e7ca87fa 100644 --- a/libc/src/time/CMakeLists.txt +++ b/libc/src/time/CMakeLists.txt @@ -36,6 +36,30 @@ add_entrypoint_object( libc.include.time ) +add_entrypoint_object( + ctime + SRCS + ctime.cpp + HDRS + ctime.h + DEPENDS + .time_utils + libc.hdr.types.time_t + libc.include.time +) + +add_entrypoint_object( + ctime_r + SRCS + ctime_r.cpp + HDRS + ctime_r.h + DEPENDS + .time_utils + libc.hdr.types.time_t + libc.include.time +) + add_entrypoint_object( difftime SRCS diff --git a/libc/src/time/ctime.cpp b/libc/src/time/ctime.cpp new file mode 100644 index 0000000000000..8adae9be73809 --- /dev/null +++ b/libc/src/time/ctime.cpp @@ -0,0 +1,28 @@ +//===-- Implementation of ctime 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 "ctime.h" +#include "src/__support/CPP/limits.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "time_utils.h" + +namespace LIBC_NAMESPACE_DECL { + +using LIBC_NAMESPACE::time_utils::TimeConstants; + +LLVM_LIBC_FUNCTION(char *, ctime, (const time_t *t_ptr)) { + if (t_ptr == nullptr || *t_ptr > cpp::numeric_limits::max()) { + return nullptr; + } + static char buffer[TimeConstants::ASCTIME_BUFFER_SIZE]; + return time_utils::asctime(time_utils::localtime(t_ptr), buffer, + TimeConstants::ASCTIME_MAX_BYTES); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/time/ctime.h b/libc/src/time/ctime.h new file mode 100644 index 0000000000000..7760710776232 --- /dev/null +++ b/libc/src/time/ctime.h @@ -0,0 +1,21 @@ +//===-- Implementation header of ctime --------------------------*- 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_CTIME_H +#define LLVM_LIBC_SRC_TIME_CTIME_H + +#include "hdr/types/time_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +char *ctime(const time_t *t_ptr); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_TIME_CTIME_H diff --git a/libc/src/time/ctime_r.cpp b/libc/src/time/ctime_r.cpp new file mode 100644 index 0000000000000..63d93c4085f38 --- /dev/null +++ b/libc/src/time/ctime_r.cpp @@ -0,0 +1,29 @@ +//===-- Implementation of ctime_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 "ctime_r.h" +#include "src/__support/CPP/limits.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "time_utils.h" + +namespace LIBC_NAMESPACE_DECL { + +using LIBC_NAMESPACE::time_utils::TimeConstants; + +LLVM_LIBC_FUNCTION(char *, ctime_r, (const time_t *t_ptr, char *buffer)) { + if (t_ptr == nullptr || buffer == nullptr || + *t_ptr > cpp::numeric_limits::max()) { + return nullptr; + } + + return time_utils::asctime(time_utils::localtime(t_ptr), buffer, + TimeConstants::ASCTIME_MAX_BYTES); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/time/ctime_r.h b/libc/src/time/ctime_r.h new file mode 100644 index 0000000000000..d45bf7b64d3a6 --- /dev/null +++ b/libc/src/time/ctime_r.h @@ -0,0 +1,21 @@ +//===-- Implementation header of ctime_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_CTIME_R_H +#define LLVM_LIBC_SRC_TIME_CTIME_R_H + +#include "hdr/types/time_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +char *ctime_r(const time_t *t_ptr, char *buffer); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_TIME_CTIME_R_H diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h index 47f55f7d38912..552ea925c1c7d 100644 --- a/libc/src/time/time_utils.h +++ b/libc/src/time/time_utils.h @@ -156,6 +156,13 @@ 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); +} + } // namespace time_utils } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt index 78cfe8f301615..bba01f063fed2 100644 --- a/libc/test/src/time/CMakeLists.txt +++ b/libc/test/src/time/CMakeLists.txt @@ -30,6 +30,42 @@ add_libc_unittest( libc.src.time.asctime_r ) +add_libc_unittest( + ctime_test + SUITE + libc_time_unittests + SRCS + ctime_test.cpp + HDRS + TmHelper.h + TmMatcher.h + CXX_STANDARD + 20 + DEPENDS + libc.include.time + libc.hdr.types.time_t + libc.src.time.ctime + libc.src.time.time_utils +) + +add_libc_unittest( + ctime_r_test + SUITE + libc_time_unittests + SRCS + ctime_r_test.cpp + HDRS + TmHelper.h + TmMatcher.h + CXX_STANDARD + 20 + DEPENDS + libc.include.time + libc.hdr.types.time_t + libc.src.time.ctime_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 new file mode 100644 index 0000000000000..9ce6f75f75484 --- /dev/null +++ b/libc/test/src/time/ctime_r_test.cpp @@ -0,0 +1,58 @@ +//===-- Unittests for ctime_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/ctime_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(LlvmLibcCtimeR, Nullptr) { + char *result; + result = LIBC_NAMESPACE::ctime_r(nullptr, nullptr); + ASSERT_STREQ(nullptr, result); + + char buffer[TimeConstants::ASCTIME_BUFFER_SIZE]; + result = LIBC_NAMESPACE::ctime_r(nullptr, buffer); + ASSERT_STREQ(nullptr, result); + + time_t t; + result = LIBC_NAMESPACE::ctime_r(&t, nullptr); + ASSERT_STREQ(nullptr, result); +} + +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. + t = 0; + result = LIBC_NAMESPACE::ctime_r(&t, buffer); + ASSERT_STREQ("Thu Jan 1 00: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. + t = 2147483647; + result = LIBC_NAMESPACE::ctime_r(&t, buffer); + ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", result); +} + +TEST(LlvmLibcCtimeR, InvalidArgument) { + char buffer[TimeConstants::ASCTIME_BUFFER_SIZE]; + time_t t; + char *result; + t = 2147483648; + result = LIBC_NAMESPACE::ctime_r(&t, buffer); + ASSERT_STREQ(nullptr, result); +} diff --git a/libc/test/src/time/ctime_test.cpp b/libc/test/src/time/ctime_test.cpp new file mode 100644 index 0000000000000..7ec71bb1e4ed1 --- /dev/null +++ b/libc/test/src/time/ctime_test.cpp @@ -0,0 +1,42 @@ +//===-- Unittests for ctime -----------------------------------------------===// +// +// 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/ctime.h" +#include "test/UnitTest/Test.h" +#include "test/src/time/TmHelper.h" + +TEST(LlvmLibcCtime, NULL) { + char *result; + result = LIBC_NAMESPACE::ctime(NULL); + ASSERT_STREQ(NULL, result); +} + +TEST(LlvmLibcCtime, ValidUnixTimestamp0) { + time_t t; + char *result; + t = 0; + result = LIBC_NAMESPACE::ctime(&t); + ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", result); +} + +TEST(LlvmLibcCtime, ValidUnixTimestamp32Int) { + time_t t; + char *result; + t = 2147483647; + result = LIBC_NAMESPACE::ctime(&t); + ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", result); +} + +TEST(LlvmLibcCtime, InvalidArgument) { + time_t t; + char *result; + t = 2147483648; + result = LIBC_NAMESPACE::ctime(&t); + ASSERT_STREQ(NULL, result); +}