[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