[libc-commits] [libc] [libc] Fix {asc, c, gm, mk}time(_r)? tests and spurios snprintf call (PR #216423)
Pavel Labath via libc-commits
libc-commits at lists.llvm.org
Sun Aug 16 09:21:43 PDT 2026
https://github.com/labath updated https://github.com/llvm/llvm-project/pull/216423
>From 8902c4b795ed497dcaaf405f25d4fc0ce1d2cef4 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Fri, 14 Aug 2026 22:31:26 +0000
Subject: [PATCH] [libc] Fix {asc,c,gm,mk}time(_r)? tests and use strftime in
asctime
time_test_utils was depending on a non-existent library, which caused
these tests to be auto-skipped. Fixing that exposed the fact that some
of the tests don't build (in hermetic mode) due to a snprintf
dependency concealed behind a __builtin_snprintf in asctime.
Instead of calling LIBC_NAMESPACE::snprintf from time_utils (which
violates our policy against internal calls to public entrypoints and
pulls in stdio), this patch addresses the existing TODO by moving
asctime to asctime_utils.h and implementing it via strftime_main.
Assisted by Gemini.
---
libc/src/time/CMakeLists.txt | 26 +++++++++++---
libc/src/time/asctime.cpp | 2 +-
libc/src/time/asctime_r.cpp | 2 +-
libc/src/time/asctime_utils.h | 59 +++++++++++++++++++++++++++++++
libc/src/time/ctime.cpp | 1 +
libc/src/time/ctime_r.cpp | 1 +
libc/src/time/time_utils.h | 30 ----------------
libc/test/src/time/CMakeLists.txt | 8 ++++-
8 files changed, 92 insertions(+), 37 deletions(-)
create mode 100644 libc/src/time/asctime_utils.h
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index 4801e30395d1a..8a4c71968712c 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -38,6 +38,24 @@ add_object_library(
libc.src.__support.uint128
)
+add_subdirectory(strftime_core) #TODO: Move to top
+
+add_header_library(
+ asctime_utils
+ HDRS
+ asctime_utils.h
+ DEPENDS
+ .time_constants
+ .time_utils
+ libc.hdr.errno_macros
+ libc.hdr.types.size_t
+ libc.hdr.types.struct_tm
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.src.__support.printf_core.writer
+ libc.src.time.strftime_core.strftime_main
+)
+
add_entrypoint_object(
asctime
SRCS
@@ -45,7 +63,7 @@ add_entrypoint_object(
HDRS
asctime.h
DEPENDS
- .time_utils
+ .asctime_utils
.time_constants
libc.include.time
libc.hdr.types.struct_tm
@@ -60,7 +78,7 @@ add_entrypoint_object(
HDRS
asctime_r.h
DEPENDS
- .time_utils
+ .asctime_utils
.time_constants
libc.include.time
libc.hdr.types.struct_tm
@@ -75,6 +93,7 @@ add_entrypoint_object(
HDRS
ctime.h
DEPENDS
+ .asctime_utils
.time_utils
.time_constants
libc.hdr.types.time_t
@@ -90,6 +109,7 @@ add_entrypoint_object(
HDRS
ctime_r.h
DEPENDS
+ .asctime_utils
.time_utils
.time_constants
libc.hdr.types.time_t
@@ -183,8 +203,6 @@ add_entrypoint_object(
libc.src.__support.macros.null_check
)
-add_subdirectory(strftime_core) #TODO: Move to top
-
add_entrypoint_object(
strftime
SRCS
diff --git a/libc/src/time/asctime.cpp b/libc/src/time/asctime.cpp
index 8baeeb88b5698..488c6cdbea3fc 100644
--- a/libc/src/time/asctime.cpp
+++ b/libc/src/time/asctime.cpp
@@ -11,8 +11,8 @@
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/null_check.h"
+#include "src/time/asctime_utils.h"
#include "src/time/time_constants.h"
-#include "src/time/time_utils.h"
namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/time/asctime_r.cpp b/libc/src/time/asctime_r.cpp
index 1002e4139adf7..17ead0499e36c 100644
--- a/libc/src/time/asctime_r.cpp
+++ b/libc/src/time/asctime_r.cpp
@@ -11,8 +11,8 @@
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/null_check.h"
+#include "src/time/asctime_utils.h"
#include "src/time/time_constants.h"
-#include "src/time/time_utils.h"
namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/time/asctime_utils.h b/libc/src/time/asctime_utils.h
new file mode 100644
index 0000000000000..f81965a6a2045
--- /dev/null
+++ b/libc/src/time/asctime_utils.h
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Collection of utils for asctime.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_TIME_ASCTIME_UTILS_H
+#define LLVM_LIBC_SRC_TIME_ASCTIME_UTILS_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/struct_tm.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/printf_core/writer.h"
+#include "src/time/strftime_core/strftime_main.h"
+#include "src/time/time_constants.h"
+#include "src/time/time_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace time_utils {
+
+LIBC_INLINE ErrorOr<char *> asctime(const tm *timeptr, char *buffer,
+ size_t buffer_length) {
+ if (timeptr == nullptr || buffer == nullptr)
+ return cpp::unexpected(EINVAL);
+ if (timeptr->tm_wday < 0 ||
+ timeptr->tm_wday > (time_constants::DAYS_PER_WEEK - 1))
+ return cpp::unexpected(EINVAL);
+ if (timeptr->tm_mon < 0 ||
+ timeptr->tm_mon > (time_constants::MONTHS_PER_YEAR - 1))
+ return cpp::unexpected(EINVAL);
+
+ printf_core::DropOverflowBuffer wb(buffer,
+ buffer_length > 0 ? buffer_length - 1 : 0);
+ printf_core::Writer writer(wb);
+
+ auto res = strftime_core::strftime_main(&writer, "%a %b %e %T %Y\n", timeptr);
+ if (!res.has_value())
+ return cpp::unexpected(res.error());
+
+ if (res.value() >= buffer_length)
+ return cpp::unexpected(TIME_OVERFLOW);
+
+ buffer[res.value()] = '\0';
+ return buffer;
+}
+
+} // namespace time_utils
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_ASCTIME_UTILS_H
diff --git a/libc/src/time/ctime.cpp b/libc/src/time/ctime.cpp
index c7efcbffc5123..4d2812f215d9c 100644
--- a/libc/src/time/ctime.cpp
+++ b/libc/src/time/ctime.cpp
@@ -11,6 +11,7 @@
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/null_check.h"
+#include "src/time/asctime_utils.h"
#include "src/time/time_constants.h"
#include "src/time/time_utils.h"
diff --git a/libc/src/time/ctime_r.cpp b/libc/src/time/ctime_r.cpp
index 0c8260b04df2a..5520520f7f52a 100644
--- a/libc/src/time/ctime_r.cpp
+++ b/libc/src/time/ctime_r.cpp
@@ -11,6 +11,7 @@
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/null_check.h"
+#include "src/time/asctime_utils.h"
#include "src/time/time_constants.h"
#include "src/time/time_utils.h"
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 281695b53273f..1a5cbea1f6410 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -54,36 +54,6 @@ constexpr int TIME_OVERFLOW = ERANGE;
/// \return void on success, or error code on failure.
ErrorOr<void> update_from_seconds(time_t total_seconds, tm *tm);
-LIBC_INLINE ErrorOr<char *> asctime(const tm *timeptr, char *buffer,
- size_t bufferLength) {
- if (timeptr == nullptr || buffer == nullptr) {
- return cpp::unexpected(EINVAL);
- }
- if (timeptr->tm_wday < 0 ||
- timeptr->tm_wday > (time_constants::DAYS_PER_WEEK - 1)) {
- return cpp::unexpected(EINVAL);
- }
- if (timeptr->tm_mon < 0 ||
- timeptr->tm_mon > (time_constants::MONTHS_PER_YEAR - 1)) {
- return cpp::unexpected(EINVAL);
- }
-
- // TODO(michaelr): move this to use the strftime machinery
- // equivalent to strftime(buffer, bufferLength, "%a %b %T %Y\n", timeptr)
- int written_size = __builtin_snprintf(
- buffer, bufferLength, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
- time_constants::WEEK_DAY_NAMES[timeptr->tm_wday].data(),
- time_constants::MONTH_NAMES[timeptr->tm_mon].data(), timeptr->tm_mday,
- timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec,
- time_constants::TIME_YEAR_BASE + timeptr->tm_year);
- if (written_size < 0)
- return cpp::unexpected(EINVAL);
- if (static_cast<size_t>(written_size) >= bufferLength) {
- return cpp::unexpected(TIME_OVERFLOW);
- }
- return buffer;
-}
-
LIBC_INLINE ErrorOr<tm *> gmtime_internal(const time_t *timer, tm *result) {
time_t seconds = *timer;
auto status = update_from_seconds(seconds, result);
diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
index 6d7494906e76d..1634fd4bf5fc2 100644
--- a/libc/test/src/time/CMakeLists.txt
+++ b/libc/test/src/time/CMakeLists.txt
@@ -1,5 +1,11 @@
add_custom_target(libc_time_unittests)
+if(LLVM_LIBC_FULL_BUILD)
+ set(libc_test LibcTest.hermetic)
+else()
+ set(libc_test LibcTest.unit)
+endif()
+
add_header_library(
time_test_utils
HDRS
@@ -9,7 +15,7 @@ add_header_library(
libc.hdr.types.struct_tm
libc.src.__support.macros.config
libc.src.time.time_constants
- LibcTest
+ ${libc_test}
)
add_libc_test(
More information about the libc-commits
mailing list