• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- Composite converter for strftime ------------------------*- 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_COMPOSITE_CONVERTER_H
10 #define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_COMPOSITE_CONVERTER_H
11 
12 #include "hdr/types/struct_tm.h"
13 #include "src/__support/CPP/string_view.h"
14 #include "src/__support/macros/config.h"
15 #include "src/stdio/printf_core/writer.h"
16 #include "src/time/strftime_core/core_structs.h"
17 #include "src/time/strftime_core/num_converter.h"
18 #include "src/time/strftime_core/str_converter.h"
19 #include "src/time/time_constants.h"
20 #include "src/time/time_utils.h"
21 
22 namespace LIBC_NAMESPACE_DECL {
23 namespace strftime_core {
24 
25 LIBC_INLINE IntFormatSection
26 get_specific_int_format(const tm *timeptr, const FormatSection &base_to_conv,
27                         char new_conv_name, int TRAILING_CONV_LEN = -1) {
28   // a negative padding will be treated as the default
29   const int NEW_MIN_WIDTH =
30       TRAILING_CONV_LEN > 0 ? base_to_conv.min_width - TRAILING_CONV_LEN : 0;
31   FormatSection new_conv = base_to_conv;
32   new_conv.conv_name = new_conv_name;
33   new_conv.min_width = NEW_MIN_WIDTH;
34 
35   IntFormatSection result = get_int_format(new_conv, timeptr);
36 
37   // If the user set the padding, but it's below the width of the trailing
38   // conversions, then there should be no padding.
39   if (base_to_conv.min_width > 0 && NEW_MIN_WIDTH < 0)
40     result.pad_to_len = 0;
41 
42   return result;
43 }
44 
45 template <printf_core::WriteMode write_mode>
convert_date_us(printf_core::Writer<write_mode> * writer,const FormatSection & to_conv,const tm * timeptr)46 LIBC_INLINE int convert_date_us(printf_core::Writer<write_mode> *writer,
47                                 const FormatSection &to_conv,
48                                 const tm *timeptr) {
49   // format is %m/%d/%y (month/day/year)
50   // we only pad the first conversion, and we assume all the other values are in
51   // their valid ranges.
52   constexpr int TRAILING_CONV_LEN = 1 + 2 + 1 + 2; // sizeof("/01/02")
53   IntFormatSection year_conv;
54   IntFormatSection mon_conv;
55   IntFormatSection mday_conv;
56 
57   mon_conv = get_specific_int_format(timeptr, to_conv, 'm', TRAILING_CONV_LEN);
58   mday_conv = get_specific_int_format(timeptr, to_conv, 'd');
59   year_conv = get_specific_int_format(timeptr, to_conv, 'y');
60 
61   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, mon_conv));
62   RET_IF_RESULT_NEGATIVE(writer->write('/'));
63   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, mday_conv));
64   RET_IF_RESULT_NEGATIVE(writer->write('/'));
65   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, year_conv));
66 
67   return WRITE_OK;
68 }
69 
70 template <printf_core::WriteMode write_mode>
convert_date_iso(printf_core::Writer<write_mode> * writer,const FormatSection & to_conv,const tm * timeptr)71 LIBC_INLINE int convert_date_iso(printf_core::Writer<write_mode> *writer,
72                                  const FormatSection &to_conv,
73                                  const tm *timeptr) {
74   // format is "%Y-%m-%d" (year-month-day)
75   // we only pad the first conversion, and we assume all the other values are in
76   // their valid ranges.
77   constexpr int TRAILING_CONV_LEN = 1 + 2 + 1 + 2; // sizeof("-01-02")
78   IntFormatSection year_conv;
79   IntFormatSection mon_conv;
80   IntFormatSection mday_conv;
81 
82   year_conv = get_specific_int_format(timeptr, to_conv, 'Y', TRAILING_CONV_LEN);
83   mon_conv = get_specific_int_format(timeptr, to_conv, 'm');
84   mday_conv = get_specific_int_format(timeptr, to_conv, 'd');
85 
86   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, year_conv));
87   RET_IF_RESULT_NEGATIVE(writer->write('-'));
88   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, mon_conv));
89   RET_IF_RESULT_NEGATIVE(writer->write('-'));
90   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, mday_conv));
91 
92   return WRITE_OK;
93 }
94 
95 template <printf_core::WriteMode write_mode>
convert_time_am_pm(printf_core::Writer<write_mode> * writer,const FormatSection & to_conv,const tm * timeptr)96 LIBC_INLINE int convert_time_am_pm(printf_core::Writer<write_mode> *writer,
97                                    const FormatSection &to_conv,
98                                    const tm *timeptr) {
99   // format is "%I:%M:%S %p" (hour:minute:second AM/PM)
100   // we only pad the first conversion, and we assume all the other values are in
101   // their valid ranges.
102   constexpr int TRAILING_CONV_LEN =
103       1 + 2 + 1 + 2 + 1 + 2; // sizeof(":01:02 AM")
104   IntFormatSection hour_conv;
105   IntFormatSection min_conv;
106   IntFormatSection sec_conv;
107 
108   const time_utils::TMReader time_reader(timeptr);
109 
110   hour_conv = get_specific_int_format(timeptr, to_conv, 'I', TRAILING_CONV_LEN);
111   min_conv = get_specific_int_format(timeptr, to_conv, 'M');
112   sec_conv = get_specific_int_format(timeptr, to_conv, 'S');
113 
114   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, hour_conv));
115   RET_IF_RESULT_NEGATIVE(writer->write(':'));
116   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, min_conv));
117   RET_IF_RESULT_NEGATIVE(writer->write(':'));
118   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, sec_conv));
119   RET_IF_RESULT_NEGATIVE(writer->write(' '));
120   RET_IF_RESULT_NEGATIVE(writer->write(time_reader.get_am_pm()));
121 
122   return WRITE_OK;
123 }
124 
125 template <printf_core::WriteMode write_mode>
convert_time_minute(printf_core::Writer<write_mode> * writer,const FormatSection & to_conv,const tm * timeptr)126 LIBC_INLINE int convert_time_minute(printf_core::Writer<write_mode> *writer,
127                                     const FormatSection &to_conv,
128                                     const tm *timeptr) {
129   // format is "%H:%M" (hour:minute)
130   // we only pad the first conversion, and we assume all the other values are in
131   // their valid ranges.
132   constexpr int TRAILING_CONV_LEN = 1 + 2; // sizeof(":01")
133   IntFormatSection hour_conv;
134   IntFormatSection min_conv;
135 
136   hour_conv = get_specific_int_format(timeptr, to_conv, 'H', TRAILING_CONV_LEN);
137   min_conv = get_specific_int_format(timeptr, to_conv, 'M');
138 
139   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, hour_conv));
140   RET_IF_RESULT_NEGATIVE(writer->write(':'));
141   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, min_conv));
142 
143   return WRITE_OK;
144 }
145 
146 template <printf_core::WriteMode write_mode>
convert_time_second(printf_core::Writer<write_mode> * writer,const FormatSection & to_conv,const tm * timeptr)147 LIBC_INLINE int convert_time_second(printf_core::Writer<write_mode> *writer,
148                                     const FormatSection &to_conv,
149                                     const tm *timeptr) {
150   // format is "%H:%M:%S" (hour:minute:second)
151   // we only pad the first conversion, and we assume all the other values are in
152   // their valid ranges.
153   constexpr int TRAILING_CONV_LEN = 1 + 2 + 1 + 2; // sizeof(":01:02")
154   IntFormatSection hour_conv;
155   IntFormatSection min_conv;
156   IntFormatSection sec_conv;
157 
158   hour_conv = get_specific_int_format(timeptr, to_conv, 'H', TRAILING_CONV_LEN);
159   min_conv = get_specific_int_format(timeptr, to_conv, 'M');
160   sec_conv = get_specific_int_format(timeptr, to_conv, 'S');
161 
162   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, hour_conv));
163   RET_IF_RESULT_NEGATIVE(writer->write(':'));
164   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, min_conv));
165   RET_IF_RESULT_NEGATIVE(writer->write(':'));
166   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, sec_conv));
167 
168   return WRITE_OK;
169 }
170 
171 template <printf_core::WriteMode write_mode>
convert_full_date_time(printf_core::Writer<write_mode> * writer,const FormatSection & to_conv,const tm * timeptr)172 LIBC_INLINE int convert_full_date_time(printf_core::Writer<write_mode> *writer,
173                                        const FormatSection &to_conv,
174                                        const tm *timeptr) {
175   const time_utils::TMReader time_reader(timeptr);
176   // format is "%a %b %e %T %Y" (weekday month mday [time] year)
177   // we only pad the first conversion, and we assume all the other values are in
178   // their valid ranges.
179   // sizeof("Sun Jan 12 03:45:06 2025")
180   constexpr int FULL_CONV_LEN = 3 + 1 + 3 + 1 + 2 + 1 + 8 + 1 + 4;
181   // use the full conv len because this isn't being passed to a proper converter
182   // that will handle the width of the leading conversion. Instead it has to be
183   // handled below.
184   const int requested_padding = to_conv.min_width - FULL_CONV_LEN;
185 
186   cpp::string_view wday_str = unwrap_opt(time_reader.get_weekday_short_name());
187   cpp::string_view month_str = unwrap_opt(time_reader.get_month_short_name());
188   IntFormatSection mday_conv;
189   IntFormatSection year_conv;
190 
191   mday_conv = get_specific_int_format(timeptr, to_conv, 'e');
192   year_conv = get_specific_int_format(timeptr, to_conv, 'Y');
193 
194   FormatSection raw_time_conv = to_conv;
195   raw_time_conv.conv_name = 'T';
196   raw_time_conv.min_width = 0;
197 
198   if (requested_padding > 0)
199     RET_IF_RESULT_NEGATIVE(writer->write(' ', requested_padding));
200   RET_IF_RESULT_NEGATIVE(writer->write(wday_str));
201   RET_IF_RESULT_NEGATIVE(writer->write(' '));
202   RET_IF_RESULT_NEGATIVE(writer->write(month_str));
203   RET_IF_RESULT_NEGATIVE(writer->write(' '));
204   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, mday_conv));
205   RET_IF_RESULT_NEGATIVE(writer->write(' '));
206   RET_IF_RESULT_NEGATIVE(convert_time_second(writer, raw_time_conv, timeptr));
207   RET_IF_RESULT_NEGATIVE(writer->write(' '));
208   RET_IF_RESULT_NEGATIVE(write_padded_int(writer, year_conv));
209 
210   return WRITE_OK;
211 }
212 
213 template <printf_core::WriteMode write_mode>
convert_composite(printf_core::Writer<write_mode> * writer,const FormatSection & to_conv,const tm * timeptr)214 LIBC_INLINE int convert_composite(printf_core::Writer<write_mode> *writer,
215                                   const FormatSection &to_conv,
216                                   const tm *timeptr) {
217   switch (to_conv.conv_name) {
218   case 'c': // locale specified date and time
219             // in default locale Equivalent to %a %b %e %T %Y.
220     return convert_full_date_time(writer, to_conv, timeptr);
221   case 'D': // %m/%d/%y (month/day/year)
222     return convert_date_us(writer, to_conv, timeptr);
223   case 'F': // %Y-%m-%d (year-month-day)
224     return convert_date_iso(writer, to_conv, timeptr);
225   case 'r': // %I:%M:%S %p (hour:minute:second AM/PM)
226     return convert_time_am_pm(writer, to_conv, timeptr);
227   case 'R': // %H:%M (hour:minute)
228     return convert_time_minute(writer, to_conv, timeptr);
229   case 'T': // %H:%M:%S (hour:minute:second)
230     return convert_time_second(writer, to_conv, timeptr);
231   case 'x': // locale specified date
232             // in default locale Equivalent to %m/%d/%y. (same as %D)
233     return convert_date_us(writer, to_conv, timeptr);
234   case 'X': // locale specified time
235             // in default locale Equivalent to %T.
236     return convert_time_second(writer, to_conv, timeptr);
237   default:
238     __builtin_trap(); // this should be unreachable, but trap if you hit it.
239   }
240 }
241 } // namespace strftime_core
242 } // namespace LIBC_NAMESPACE_DECL
243 
244 #endif // LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_COMPOSITE_CONVERTER_H
245