Skip to content

Commit 560e57a

Browse files
committed
[libc] Implement strftime
1 parent 54a4965 commit 560e57a

File tree

11 files changed

+943
-0
lines changed

11 files changed

+943
-0
lines changed

libc/src/stdio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ add_entrypoint_object(
239239

240240
add_subdirectory(printf_core)
241241
add_subdirectory(scanf_core)
242+
add_subdirectory(strftime_core)
242243

243244
add_entrypoint_object(
244245
remove
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
add_header_library(
3+
core_structs
4+
HDRS
5+
core_structs.h
6+
DEPENDS
7+
libc.src.__support.CPP.string_view
8+
libc.src.__support.FPUtil.fp_bits
9+
)
10+
11+
add_header_library(
12+
parser
13+
HDRS
14+
parser.h
15+
DEPENDS
16+
.core_structs
17+
libc.src.__support.arg_list
18+
libc.src.__support.ctype_utils
19+
libc.src.__support.str_to_integer
20+
libc.src.__support.CPP.algorithm
21+
libc.src.__support.CPP.bit
22+
libc.src.__support.CPP.optional
23+
libc.src.__support.CPP.string_view
24+
libc.src.__support.CPP.type_traits
25+
libc.src.__support.common
26+
)
27+
28+
29+
add_object_library(
30+
converter
31+
SRCS
32+
converter.cpp
33+
HDRS
34+
converter.h
35+
DEPENDS
36+
.core_structs
37+
libc.src.stdio.printf_core.writer
38+
libc.src.__support.big_int
39+
libc.src.math.log10
40+
libc.src.__support.common
41+
libc.src.__support.CPP.limits
42+
libc.src.__support.CPP.span
43+
libc.src.__support.CPP.string_view
44+
libc.src.__support.float_to_string
45+
libc.src.__support.FPUtil.fenv_impl
46+
libc.src.__support.FPUtil.fp_bits
47+
libc.src.__support.FPUtil.rounding_mode
48+
libc.src.__support.integer_to_string
49+
libc.src.__support.libc_assert
50+
libc.src.__support.uint128
51+
libc.src.__support.StringUtil.error_to_string
52+
)
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
//===-- Format specifier converter for printf -------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See htto_conv.times://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
10+
#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
11+
12+
#include "src/__support/CPP/string.h"
13+
#include "src/__support/CPP/string_view.h"
14+
#include "src/__support/integer_to_string.h"
15+
#include "src/__support/macros/config.h"
16+
#include "src/math/log10.h"
17+
#include "src/stdio/printf_core/writer.h"
18+
#include "src/stdio/strftime_core/core_structs.h"
19+
#include "src/stdio/strftime_core/time_internal_def.h"
20+
#include <time.h>
21+
22+
namespace LIBC_NAMESPACE_DECL {
23+
namespace strftime_core {
24+
25+
namespace details {
26+
27+
LIBC_INLINE cpp::optional<cpp::string_view>
28+
num_to_strview(uintmax_t num, cpp::span<char> bufref) {
29+
return IntegerToString<uintmax_t>::format_to(bufref, num);
30+
}
31+
32+
template <int width>
33+
LIBC_INLINE int write_num(uintmax_t num, printf_core::Writer *writer) {
34+
cpp::array<char, width> buf;
35+
return writer->write(*num_to_strview(num, buf));
36+
}
37+
38+
template <int width, char padding>
39+
LIBC_INLINE int write_num_with_padding(uintmax_t num,
40+
printf_core::Writer *writer) {
41+
cpp::array<char, width> buf;
42+
auto digits = log10(num) + 1;
43+
auto padding_needed = width - digits;
44+
int char_written = 0;
45+
for (int _ = 0; _ < padding_needed; _++) {
46+
char_written += writer->write(padding);
47+
}
48+
char_written += writer->write(*num_to_strview(num, buf));
49+
return char_written;
50+
}
51+
52+
} // namespace details
53+
54+
/* Nonzero if YEAR is a leap year (every 4 years,
55+
except every 100th isn't, and every 400th is). */
56+
LIBC_INLINE bool is_leap(int year) {
57+
return ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0));
58+
}
59+
60+
LIBC_INLINE int convert_weekday(printf_core::Writer *writer,
61+
const FormatSection &to_conv) {
62+
return writer->write(day_names[to_conv.time->tm_wday]);
63+
}
64+
65+
LIBC_INLINE int convert_zero_padded_day_of_year(printf_core::Writer *writer,
66+
const FormatSection &to_conv) {
67+
return details::write_num_with_padding<3, '0'>(to_conv.time->tm_yday + 1,
68+
writer);
69+
}
70+
71+
LIBC_INLINE int convert_zero_padded_day_of_month(printf_core::Writer *writer,
72+
const FormatSection &to_conv) {
73+
return details::write_num_with_padding<2, '0'>(to_conv.time->tm_mday, writer);
74+
}
75+
76+
LIBC_INLINE int
77+
convert_space_padded_day_of_month(printf_core::Writer *writer,
78+
const FormatSection &to_conv) {
79+
return details::write_num_with_padding<2, ' '>(to_conv.time->tm_mday, writer);
80+
}
81+
82+
LIBC_INLINE int convert_decimal_weekday(printf_core::Writer *writer,
83+
const FormatSection &to_conv) {
84+
return details::write_num<1>(
85+
to_conv.time->tm_wday == 0 ? 7 : to_conv.time->tm_wday, writer);
86+
}
87+
88+
LIBC_INLINE int convert_decimal_weekday_iso(printf_core::Writer *writer,
89+
const FormatSection &to_conv) {
90+
return details::write_num<1>(to_conv.time->tm_wday, writer);
91+
}
92+
93+
LIBC_INLINE int convert_week_number_sunday(printf_core::Writer *writer,
94+
const FormatSection &to_conv) {
95+
int wday = to_conv.time->tm_wday;
96+
int yday = to_conv.time->tm_yday;
97+
int week = (yday - wday + 7) / 7;
98+
return details::write_num_with_padding<2, '0'>(week, writer);
99+
}
100+
101+
LIBC_INLINE int convert_week_number_monday(printf_core::Writer *writer,
102+
const FormatSection &to_conv) {
103+
int wday = (to_conv.time->tm_wday + 6) % 7;
104+
int yday = to_conv.time->tm_yday;
105+
int week = (yday - wday + 7) / 7;
106+
return details::write_num_with_padding<2, '0'>(week, writer);
107+
}
108+
109+
LIBC_INLINE int convert_full_month(printf_core::Writer *writer,
110+
const FormatSection &to_conv) {
111+
return writer->write(month_names[to_conv.time->tm_mon]);
112+
}
113+
114+
LIBC_INLINE int convert_abbreviated_month(printf_core::Writer *writer,
115+
const FormatSection &to_conv) {
116+
return writer->write(abbreviated_month_names[to_conv.time->tm_mon]);
117+
}
118+
119+
LIBC_INLINE int convert_zero_padded_month(printf_core::Writer *writer,
120+
const FormatSection &to_conv) {
121+
return details::write_num_with_padding<2, '0'>(to_conv.time->tm_mon + 1,
122+
writer);
123+
}
124+
125+
LIBC_INLINE int convert_full_year(printf_core::Writer *writer,
126+
const FormatSection &to_conv) {
127+
return details::write_num_with_padding<4, '0'>(
128+
to_conv.time->tm_year + YEAR_BASE, writer);
129+
}
130+
131+
LIBC_INLINE int convert_two_digit_year(printf_core::Writer *writer,
132+
const FormatSection &to_conv) {
133+
return details::write_num_with_padding<2, '0'>(
134+
(to_conv.time->tm_year + YEAR_BASE) % 100, writer);
135+
}
136+
137+
LIBC_INLINE int convert_century(printf_core::Writer *writer,
138+
const FormatSection &to_conv) {
139+
return details::write_num_with_padding<2, '0'>(
140+
(to_conv.time->tm_year + YEAR_BASE) / 100, writer);
141+
}
142+
143+
static int iso_week_days(int yday, int wday) {
144+
/* Add enough to the first operand of % to make it nonnegative. */
145+
int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
146+
return (yday - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7 +
147+
ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
148+
}
149+
150+
LIBC_INLINE int convert_iso_year(printf_core::Writer *writer,
151+
const FormatSection &to_conv) {
152+
int year = to_conv.time->tm_year + YEAR_BASE;
153+
int days = iso_week_days(to_conv.time->tm_yday, to_conv.time->tm_wday);
154+
155+
if (days < 0) {
156+
/* This ISO week belongs to the previous year. */
157+
year--;
158+
days = iso_week_days(to_conv.time->tm_yday + (365 + is_leap(year)),
159+
to_conv.time->tm_wday);
160+
} else {
161+
int d = iso_week_days(to_conv.time->tm_yday - (365 + is_leap(year)),
162+
to_conv.time->tm_wday);
163+
if (0 <= d) {
164+
/* This ISO week belongs to the next year. */
165+
year++;
166+
days = d;
167+
}
168+
}
169+
170+
return details::write_num<4>(year, writer);
171+
}
172+
173+
LIBC_INLINE int convert_hour_24(printf_core::Writer *writer,
174+
const FormatSection &to_conv) {
175+
return details::write_num_with_padding<2, '0'>(to_conv.time->tm_hour, writer);
176+
}
177+
178+
LIBC_INLINE int convert_pm(printf_core::Writer *writer,
179+
const FormatSection &to_conv) {
180+
static const cpp::string_view AM = "AM";
181+
static const cpp::string_view PM = "PM";
182+
return writer->write(to_conv.time->tm_hour >= 12 ? PM : AM);
183+
}
184+
185+
LIBC_INLINE int convert_hour_12(printf_core::Writer *writer,
186+
const FormatSection &to_conv) {
187+
int hour = to_conv.time->tm_hour % 12; // Convert to 12-hour format
188+
if (hour == 0)
189+
hour = 12; // Adjust for midnight
190+
return details::write_num_with_padding<2, '0'>(hour, writer);
191+
}
192+
193+
LIBC_INLINE int convert_minute(printf_core::Writer *writer,
194+
const FormatSection &to_conv) {
195+
return details::write_num_with_padding<2, '0'>(to_conv.time->tm_min, writer);
196+
}
197+
198+
LIBC_INLINE int convert_second(printf_core::Writer *writer,
199+
const FormatSection &to_conv) {
200+
return details::write_num_with_padding<2, '0'>(to_conv.time->tm_sec, writer);
201+
}
202+
203+
int convert(printf_core::Writer *writer, const FormatSection &to_conv) {
204+
if (!to_conv.has_conv)
205+
return writer->write(to_conv.raw_string);
206+
switch (to_conv.conv_name) {
207+
// day of the week
208+
case 'a':
209+
return convert_weekday(writer, to_conv);
210+
case 'w':
211+
return convert_decimal_weekday(writer, to_conv);
212+
case 'u':
213+
return convert_decimal_weekday_iso(writer, to_conv);
214+
// day of the year/ month
215+
case 'j':
216+
return convert_zero_padded_day_of_year(writer, to_conv);
217+
case 'd':
218+
return convert_zero_padded_day_of_month(writer, to_conv);
219+
case 'e':
220+
return convert_space_padded_day_of_month(writer, to_conv);
221+
// week
222+
case 'U':
223+
return convert_week_number_sunday(writer, to_conv);
224+
case 'W':
225+
return convert_week_number_monday(writer, to_conv);
226+
case 'V': // TODO: ISO 8061
227+
// month
228+
case 'B':
229+
return convert_full_month(writer, to_conv);
230+
case 'b':
231+
case 'h':
232+
return convert_abbreviated_month(writer, to_conv);
233+
case 'm':
234+
return convert_zero_padded_month(writer, to_conv);
235+
// year
236+
case 'Y':
237+
return convert_full_year(writer, to_conv);
238+
case 'y':
239+
return convert_two_digit_year(writer, to_conv);
240+
case 'C':
241+
return convert_century(writer, to_conv);
242+
case 'G':
243+
// TODO
244+
return convert_iso_year(writer, to_conv);
245+
// hours
246+
case 'p':
247+
return convert_pm(writer, to_conv);
248+
case 'H':
249+
return convert_hour_24(writer, to_conv);
250+
case 'I':
251+
return convert_hour_12(writer, to_conv);
252+
// minutes
253+
case 'M':
254+
return convert_minute(writer, to_conv);
255+
// seconds
256+
case 'S':
257+
return convert_second(writer, to_conv);
258+
}
259+
return 0;
260+
}
261+
262+
} // namespace strftime_core
263+
} // namespace LIBC_NAMESPACE_DECL
264+
#endif
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===-- Format specifier converter for printf -------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
10+
#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
11+
12+
#include "src/__support/macros/config.h"
13+
#include "src/stdio/printf_core/writer.h"
14+
#include "src/stdio/strftime_core/core_structs.h"
15+
16+
#include <stddef.h>
17+
18+
namespace LIBC_NAMESPACE_DECL {
19+
namespace strftime_core {
20+
21+
// convert will call a conversion function to convert the FormatSection into
22+
// its string representation, and then that will write the result to the
23+
// writer.
24+
int convert(printf_core::Writer *writer, const FormatSection &to_conv);
25+
26+
} // namespace strftime_core
27+
} // namespace LIBC_NAMESPACE_DECL
28+
29+
#endif // LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H

0 commit comments

Comments
 (0)