[libc-commits] [libc] [libc] Implement strftime (PR #111305)

via libc-commits libc-commits at lists.llvm.org
Wed Oct 23 07:32:09 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Tsz Chan (tszhin-swe)

<details>
<summary>Changes</summary>



---

Patch is 61.35 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/111305.diff


24 Files Affected:

- (modified) libc/config/gpu/entrypoints.txt (+2) 
- (modified) libc/newhdrgen/yaml/time.yaml (+20) 
- (modified) libc/spec/stdc.td (+21) 
- (modified) libc/src/time/CMakeLists.txt (+30-4) 
- (added) libc/src/time/strftime.cpp (+31) 
- (added) libc/src/time/strftime.h (+23) 
- (added) libc/src/time/strftime_core/CMakeLists.txt (+57) 
- (added) libc/src/time/strftime_core/composite_converter.h (+124) 
- (added) libc/src/time/strftime_core/converter.cpp (+73) 
- (added) libc/src/time/strftime_core/converter.h (+28) 
- (added) libc/src/time/strftime_core/core_structs.h (+39) 
- (added) libc/src/time/strftime_core/num_converter.h (+208) 
- (added) libc/src/time/strftime_core/parser.h (+106) 
- (added) libc/src/time/strftime_core/str_converter.h (+55) 
- (added) libc/src/time/strftime_core/strftime_main.cpp (+43) 
- (added) libc/src/time/strftime_core/strftime_main.h (+32) 
- (added) libc/src/time/strftime_core/time_internal_def.h (+77) 
- (added) libc/src/time/strftime_l.cpp (+32) 
- (added) libc/src/time/strftime_l.h (+24) 
- (modified) libc/test/src/time/CMakeLists.txt (+12) 
- (added) libc/test/src/time/strftime_core/CMakeLists.txt (+23) 
- (added) libc/test/src/time/strftime_core/converter_test.cpp (+481) 
- (added) libc/test/src/time/strftime_core/parser_test.cpp (+102) 
- (added) libc/test/src/time/strftime_test.cpp (+113) 


``````````diff
diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index b4cfe47f4505fd..af05dc57023f08 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -256,6 +256,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.time.clock
     libc.src.time.clock_gettime
     libc.src.time.nanosleep
+    libc.src.time.strftime
+    libc.src.time.strftime_l
 
     # wchar.h entrypoints
     libc.src.wchar.wctob
diff --git a/libc/newhdrgen/yaml/time.yaml b/libc/newhdrgen/yaml/time.yaml
index 69b40bef3160dd..593b52b2d5a704 100644
--- a/libc/newhdrgen/yaml/time.yaml
+++ b/libc/newhdrgen/yaml/time.yaml
@@ -8,6 +8,7 @@ types:
   - type_name: time_t
   - type_name: clock_t
   - type_name: size_t
+  - type_name: locale_t
 enums: []
 objects: []
 functions:
@@ -96,3 +97,22 @@ functions:
     return_type: time_t
     arguments:
       - type: time_t *
+  - name: strftime
+    standard:
+      - stdc
+    return_type: size_t
+    arguments:
+      - type: char *__restrict
+      - type: size_t
+      - type: const char *__restrict
+      - type: const struct tm *__restrict
+  - name: strftime_l
+    standard:
+      - stdc
+    return_type: size_t
+    arguments:
+      - type: char *__restrict
+      - type: size_t
+      - type: const char *__restrict
+      - type: const struct tm *__restrict
+      - type: locale_t
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 7caf543748151a..f673e0e3bc246a 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -1586,6 +1586,7 @@ def StdC : StandardSpec<"stdc"> {
          StructTimeSpec,
          TimeTType,
          SizeTType,
+         LocaleT,
       ],
       [], // Enumerations
       [
@@ -1651,6 +1652,26 @@ def StdC : StandardSpec<"stdc"> {
               RetValSpec<TimeTType>,
               [ArgSpec<TimeTTypePtr>]
           >,
+          FunctionSpec<
+              "strftime",
+              RetValSpec<SizeTType>,
+              [
+                  ArgSpec<CharRestrictedPtr>,
+                  ArgSpec<SizeTType>,
+                  ArgSpec<ConstCharRestrictedPtr>,
+                  ArgSpec<StructTmPtr>
+              ]
+          FunctionSpec<
+              "strftime_l",
+              RetValSpec<SizeTType>,
+              [
+                  ArgSpec<CharRestrictedPtr>,
+                  ArgSpec<SizeTType>,
+                  ArgSpec<ConstCharRestrictedPtr>,
+                  ArgSpec<StructTmPtr>,
+                  ArgSpec<LocaleT>
+              ]
+          >,
       ]
   >;
 
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index b3318e7ca87fa5..6e6c21bc657386 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -1,6 +1,6 @@
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
-  add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
-endif()
+# if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+#   add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+# endif()
 
 add_object_library(
   time_utils
@@ -99,7 +99,6 @@ add_entrypoint_object(
   HDRS
     mktime.h
   DEPENDS
-    .time_utils
     libc.include.time
     libc.src.errno.errno
 )
@@ -138,3 +137,30 @@ add_entrypoint_object(
   DEPENDS
     .${LIBC_TARGET_OS}.gettimeofday
 )
+
+add_subdirectory(strftime_core)
+
+add_entrypoint_object(
+  strftime
+  SRCS
+    strftime.cpp
+  HDRS
+    strftime.h
+  DEPENDS
+    libc.include.time
+    libc.src.time.strftime_core.strftime_main
+    libc.src.stdio.printf_core.writer
+)
+
+add_entrypoint_object(
+  strftime_l
+  SRCS
+    strftime_l.cpp
+  HDRS
+    strftime_l.h
+  DEPENDS
+    libc.include.time
+    libc.include.locale
+    libc.src.time.strftime_core.strftime_main
+    libc.src.stdio.printf_core.writer
+)
\ No newline at end of file
diff --git a/libc/src/time/strftime.cpp b/libc/src/time/strftime.cpp
new file mode 100644
index 00000000000000..5035001e6fb6b7
--- /dev/null
+++ b/libc/src/time/strftime.cpp
@@ -0,0 +1,31 @@
+//===-- Implementation of strftime 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/strftime.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/errno/libc_errno.h"
+#include "src/time/time_utils.h"
+
+#include "src/stdio/printf_core/writer.h"
+#include "src/time/strftime_core/strftime_main.h"
+namespace LIBC_NAMESPACE_DECL {
+
+size_t strftime(char *__restrict buffer, size_t buffsz,
+                const char *__restrict format, const struct tm *timeptr) {
+
+  printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0),
+                              strftime_core::overflow_write_mock, nullptr);
+  printf_core::Writer writer(&wb);
+  int ret = strftime_core::strftime_main(&writer, format, timeptr);
+  if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
+    wb.buff[wb.buff_cur] = '\0';
+  return ret > 0 ? ret : 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/strftime.h b/libc/src/time/strftime.h
new file mode 100644
index 00000000000000..aa30336d8957d3
--- /dev/null
+++ b/libc/src/time/strftime.h
@@ -0,0 +1,23 @@
+//===-- Implementation header of strftime -----------------------*- 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_STRFTIME_H
+#define LLVM_LIBC_SRC_TIME_STRFTIME_H
+
+#include "src/__support/macros/config.h"
+#include <stddef.h>
+#include <time.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+size_t strftime(char *__restrict, size_t max, const char *__restrict format,
+                const struct tm *timeptr);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_STRFTIME_H
diff --git a/libc/src/time/strftime_core/CMakeLists.txt b/libc/src/time/strftime_core/CMakeLists.txt
new file mode 100644
index 00000000000000..79e2eafc6a98de
--- /dev/null
+++ b/libc/src/time/strftime_core/CMakeLists.txt
@@ -0,0 +1,57 @@
+add_header_library(
+  core_structs
+  HDRS
+    core_structs.h
+  DEPENDS
+    libc.src.__support.CPP.string_view
+    libc.include.time
+)
+
+add_header_library(
+  parser
+  HDRS
+    parser.h
+  DEPENDS
+    .core_structs
+    libc.src.string.string_utils
+    libc.include.time
+
+)
+
+add_object_library(
+  converter
+  SRCS
+    converter.cpp
+  HDRS
+    converter.h
+    num_converter.h
+    str_converter.h
+    composite_converter.h
+  DEPENDS
+    .core_structs
+    libc.src.__support.arg_list
+    libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.printf_main
+    libc.src.__support.big_int
+    libc.src.__support.CPP.string_view
+    libc.src.__support.float_to_string
+    libc.src.__support.integer_to_string
+    libc.src.__support.uint128
+    libc.src.__support.StringUtil.error_to_string
+    libc.include.time
+
+)
+
+add_object_library(
+  strftime_main
+  SRCS
+    strftime_main.cpp
+  HDRS
+    strftime_main.h
+  DEPENDS
+    .core_structs
+    .parser
+    .converter
+    libc.src.stdio.printf_core.writer
+    libc.include.time
+)
\ No newline at end of file
diff --git a/libc/src/time/strftime_core/composite_converter.h b/libc/src/time/strftime_core/composite_converter.h
new file mode 100644
index 00000000000000..cf317988de0757
--- /dev/null
+++ b/libc/src/time/strftime_core/composite_converter.h
@@ -0,0 +1,124 @@
+//===-- Format specifier converter for printf -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See htto_conv.times://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_COMPOSITE_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_COMPOSITE_CONVERTER_H
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/integer_to_string.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/printf_main.h"
+#include "src/stdio/printf_core/writer.h"
+#include "src/time/strftime_core/core_structs.h"
+#include "src/time/strftime_core/time_internal_def.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace strftime_core {
+
+namespace details {
+int snprintf_impl(char *__restrict buffer, size_t buffsz,
+                  const char *__restrict format, ...) {
+  va_list vlist;
+  va_start(vlist, format);
+  internal::ArgList args(vlist);
+  va_end(vlist);
+  printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
+  printf_core::Writer writer(&wb);
+
+  int ret_val = printf_core::printf_main(&writer, format, args);
+  if (buffsz > 0)
+    wb.buff[wb.buff_cur] = '\0';
+  return ret_val;
+}
+} // namespace details
+
+int write_composite(printf_core::Writer *writer, const FormatSection &to_conv) {
+  char buffer[100];
+  auto &time = *to_conv.time;
+
+  switch (to_conv.conv_name) {
+  // Full date and time representation (e.g., equivalent to %a %b %e %T %Y)
+  case 'c': {
+    RET_IF_RESULT_NEGATIVE(details::snprintf_impl(
+        buffer, sizeof(buffer), "%s %s %02d %02d:%02d:%02d %d",
+        safe_abbreviated_day_name(time.tm_wday),
+        safe_abbreviated_month_name(time.tm_mon), time.tm_mday, time.tm_hour,
+        time.tm_min, time.tm_sec, time.tm_year + 1900));
+    break;
+  }
+
+  // Zero-padded day of the month (equivalent to %m/%d/%y)
+  case 'D': {
+    RET_IF_RESULT_NEGATIVE(details::snprintf_impl(
+        buffer, sizeof(buffer), "%02d/%02d/%02d", time.tm_mon + 1, time.tm_mday,
+        (time.tm_year + 1900) % 100));
+    break;
+  }
+
+  // ISO 8601 date representation in YYYY-MM-DD (equivalent to %Y-%m-%d)
+  case 'F': {
+    RET_IF_RESULT_NEGATIVE(details::snprintf_impl(
+        buffer, sizeof(buffer), "%04d-%02d-%02d", time.tm_year + 1900,
+        time.tm_mon + 1, time.tm_mday));
+    break;
+  }
+
+  // 12-hour clock time with seconds and AM/PM (equivalent to %I:%M:%S %p)
+  case 'r': {
+    int hour12 = time.tm_hour % 12;
+    if (hour12 == 0)
+      hour12 = 12;
+    RET_IF_RESULT_NEGATIVE(details::snprintf_impl(
+        buffer, sizeof(buffer), "%02d:%02d:%02d %s", hour12, time.tm_min,
+        time.tm_sec,
+        to_conv.time->tm_hour >= 12 ? default_PM_str : default_AM_str));
+    break;
+  }
+
+  // 24-hour time without seconds (equivalent to %H:%M)
+  case 'R': {
+    RET_IF_RESULT_NEGATIVE(details::snprintf_impl(
+        buffer, sizeof(buffer), "%02d:%02d", time.tm_hour, time.tm_min));
+    break;
+  }
+
+  // Time with seconds (equivalent to %H:%M:%S)
+  case 'T': {
+    RET_IF_RESULT_NEGATIVE(
+        details::snprintf_impl(buffer, sizeof(buffer), "%02d:%02d:%02d",
+                               time.tm_hour, time.tm_min, time.tm_sec));
+    break;
+  }
+
+  // Locale's date representation (often equivalent to %m/%d/%y)
+  case 'x': {
+    RET_IF_RESULT_NEGATIVE(details::snprintf_impl(
+        buffer, sizeof(buffer), "%02d/%02d/%02d", time.tm_mon + 1, time.tm_mday,
+        (time.tm_year + 1900) % 100));
+    break;
+  }
+
+  // Locale's time representation (equivalent to %H:%M:%S)
+  case 'X': {
+    RET_IF_RESULT_NEGATIVE(
+        details::snprintf_impl(buffer, sizeof(buffer), "%02d:%02d:%02d",
+                               time.tm_hour, time.tm_min, time.tm_sec));
+    break;
+  }
+
+  default:
+    return writer->write(to_conv.raw_string);
+  }
+  return writer->write(buffer);
+}
+
+} // namespace strftime_core
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif
diff --git a/libc/src/time/strftime_core/converter.cpp b/libc/src/time/strftime_core/converter.cpp
new file mode 100644
index 00000000000000..a9bbf6ceca63cb
--- /dev/null
+++ b/libc/src/time/strftime_core/converter.cpp
@@ -0,0 +1,73 @@
+//===-- Format specifier converter for printf -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See htto_conv.times://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
+
+#include "composite_converter.h"
+#include "num_converter.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/integer_to_string.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/writer.h"
+#include "src/time/strftime_core/core_structs.h"
+#include "src/time/strftime_core/time_internal_def.h"
+#include "str_converter.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace strftime_core {
+
+int convert(printf_core::Writer *writer, const FormatSection &to_conv) {
+  if (!to_conv.has_conv)
+    return writer->write(to_conv.raw_string);
+  switch (to_conv.conv_name) {
+  case '%':
+    return writer->write("%");
+  case 'C': // Century (C)
+  case 'Y': // Full year (Y)
+  case 'y': // Two-digit year (y)
+  case 'j': // Day of the year (j)
+  case 'm': // Month (m)
+  case 'd': // Day of the month (d)
+  case 'e': // Day of the month (e)
+  case 'H': // 24-hour format (H)
+  case 'I': // 12-hour format (I)
+  case 'M': // Minute (M)
+  case 'S': // Second (S)
+  case 'U': // Week number starting on Sunday (U)
+  case 'W': // Week number starting on Monday (W)
+  case 'V': // ISO week number (V)
+  case 'G': // ISO year (G)
+  case 'w': // Decimal weekday (w)
+  case 'u': // ISO weekday (u)
+    return write_num(writer, to_conv);
+  case 'a': // Abbreviated weekday name (a)
+  case 'A': // Full weekday name (A)
+  case 'b': // Abbreviated month name (b)
+  case 'B': // Full month name (B)
+  case 'p': // AM/PM designation (p)
+  case 'z': // Timezone offset (z)
+  case 'Z': // Timezone name (Z)
+    return write_str(writer, to_conv);
+  case 'c':
+  case 'F':
+  case 'r':
+  case 'R':
+  case 'T':
+  case 'x':
+  case 'X':
+    return write_composite(writer, to_conv);
+  default:
+    return writer->write(to_conv.raw_string);
+  }
+  return 0;
+}
+
+} // namespace strftime_core
+} // namespace LIBC_NAMESPACE_DECL
+#endif
diff --git a/libc/src/time/strftime_core/converter.h b/libc/src/time/strftime_core/converter.h
new file mode 100644
index 00000000000000..8809a8356e5c59
--- /dev/null
+++ b/libc/src/time/strftime_core/converter.h
@@ -0,0 +1,28 @@
+//===-- Format specifier converter for printf -------------------*- 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_STDIO_STRFTIME_CORE_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
+
+#include "src/stdio/printf_core/writer.h"
+#include "src/time/strftime_core/core_structs.h"
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace strftime_core {
+
+// convert will call a conversion function to convert the FormatSection into
+// its string representation, and then that will write the result to the
+// writer.
+int convert(printf_core::Writer *writer, const FormatSection &to_conv);
+
+} // namespace strftime_core
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
diff --git a/libc/src/time/strftime_core/core_structs.h b/libc/src/time/strftime_core/core_structs.h
new file mode 100644
index 00000000000000..5c01638f132da4
--- /dev/null
+++ b/libc/src/time/strftime_core/core_structs.h
@@ -0,0 +1,39 @@
+//===-- Core Structures for printf ------------------------------*- 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_STDIO_STRFTIME_CORE_CORE_STRUCTS_H
+#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CORE_STRUCTS_H
+
+#include "src/__support/CPP/string_view.h"
+#include <time.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace strftime_core {
+
+struct FormatSection {
+  bool has_conv{false};
+  bool isE{false};
+  bool isO{false};
+  cpp::string_view raw_string{};
+  char conv_name;
+  const struct tm *time;
+  int min_width{0};
+  char padding;
+};
+
+#define RET_IF_RESULT_NEGATIVE(func)                                           \
+  {                                                                            \
+    int result = (func);                                                       \
+    if (result < 0)                                                            \
+      return result;                                                           \
+  }
+
+} // namespace strftime_core
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CORE_STRUCTS_H
diff --git a/libc/src/time/strftime_core/num_converter.h b/libc/src/time/strftime_core/num_converter.h
new file mode 100644
index 00000000000000..baad78a281b657
--- /dev/null
+++ b/libc/src/time/strftime_core/num_converter.h
@@ -0,0 +1,208 @@
+//===-- Format specifier converter for printf -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See htto_conv.times://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_NUM_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_NUM_CONVERTER_H
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/integer_to_string.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/writer.h"
+#include "src/time/strftime_core/core_structs.h"
+#include "src/time/strftime_core/time_internal_def.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace strftime_core {
+
+namespace details {
+
+LIBC_INLINE cpp::optional<cpp::string_view>
+num_to_strview(uintmax_t num, cpp::span<char> bufref) {
+  return IntegerToString<uintmax_t>::format_to(bufref, num);
+}
+
+template <typename T> int count_digits(T num) {
+  if (num == 0)
+    return 1;
+  int digits = 0;
+  while (num > 0) {
+    num /= 10;
+    digits++;
+  }
+  return digits;
+}
+
+LIBC_INLINE int write_num_with_padding(int width, char padding, uintmax_t num,
+                                       printf_core::Writer *writer) {
+  cpp::array<char, IntegerToString<uintmax_t>::buffer_size()> buf;
+  int digits = count_digits(num);
+  int padding_needed = width - digits;
+
+  for (int _ = 0; _ < padding_needed; _++) {
+    RET_IF_RESULT_NEGATIVE(writer->write(padding));
+  }
+
+  return writer->write(*num_to_strview(num, buf));
+}
+
+} // namespace details
+
+namespace iso {
+
+/* Nonzero if YEAR is a leap year (every 4 years,
+   except every 100th isn't, and every 400th is).  */
+LIBC_INLINE bool is_leap(int year) {
+  return ((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0));
+}
+
+static int iso_week_days(int yday, int wday) {
+  /* Add enough to the first operand of % to make it nonnegative.  */
+  int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
+  return (yday - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7 +
+          ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
+}
+
+enum class IsoData {
+  GET_DATE,
+  GET_YEAR,
+};
+
+template <IsoData get_date_or_year>
+LIBC_INLINE int convert_iso(const FormatSection &to_conv) {
+  int year = to_conv.time->tm_year + YEAR_BASE;
+  int days = iso_week_days(to_conv.time->tm_yday, to_conv.time->tm_wday);
+
+  if (days < 0) {
+    /* This ISO week belongs to the previous year.  */
+    year--;
+    days = iso_week_days(to_conv.time->tm_yday + (365 + is_leap(year)),
+                       ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/111305


More information about the libc-commits mailing list