File Coverage

tstr_time2str.c
Criterion Covered Total %
statement 171 181 94.4
branch 39 46 84.7
condition n/a
subroutine n/a
pod n/a
total 210 227 92.5


line stmt bran cond sub pod time code
1             /*
2             * time2str formatting engine.
3             *
4             * Conversion specifiers:
5             *
6             * %a abbreviated day name (Mon..Sun)
7             * %b abbreviated month name (Jan..Dec)
8             * %d zero-padded day (01-31)
9             * %e space-padded day ( 1-31)
10             * %-d unpadded day (1-31)
11             * %m zero-padded month (01-12)
12             * %y two-digit year (00-99)
13             * %Y four-digit year (0001-9999)
14             * %H zero-padded hour (00-23)
15             * %M zero-padded minute (00-59)
16             * %S zero-padded second (00-60)
17             * %F shorthand for %Y-%m-%d
18             * %T shorthand for %H:%M:%S
19             * %f fractional seconds (dot + digits from nanosecond/precision)
20             * %z basic offset: +HHMM, zero-string for zero
21             * %Z extended offset: +HH:MM, Z for zero
22             * %:z extended offset: +HH:MM, +00:00 for zero
23             * %% literal percent
24             */
25              
26             #define PERL_NO_GET_CONTEXT
27             #include "EXTERN.h"
28             #include "perl.h"
29             #include "XSUB.h"
30              
31             #include
32             #include
33             #include "tstr_time2str.h"
34             #include "tstr_calendar.h"
35              
36             static const char* const kShortDayName[7] = {"Mon", "Tue", "Wed", "Thu",
37             "Fri", "Sat", "Sun"};
38              
39             static const char* const kShortMonthName[12] = {"Jan", "Feb", "Mar", "Apr",
40             "May", "Jun", "Jul", "Aug",
41             "Sep", "Oct", "Nov", "Dec"};
42              
43             static const int kPow10[10] = {
44             1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
45              
46 63           static void write_u40P(pTHX_ SV* dsv, unsigned int v) {
47             char buf[4];
48 63           buf[3] = '0' + v % 10; v /= 10;
49 63           buf[2] = '0' + v % 10; v /= 10;
50 63           buf[1] = '0' + v % 10; v /= 10;
51 63           buf[0] = '0' + v % 10;
52 63           sv_catpvn_nomg(dsv, buf, 4);
53 63           }
54              
55 128           static void write_u20P(pTHX_ SV* dsv, unsigned int v) {
56             char buf[2];
57 128           buf[1] = '0' + v % 10;
58 128           buf[0] = '0' + v / 10;
59 128           sv_catpvn_nomg(dsv, buf, 2);
60 128           }
61              
62 16           static void write_u2SP(pTHX_ SV* dsv, unsigned int v) {
63             char buf[2];
64 16           buf[1] = '0' + v % 10;
65 16 100         buf[0] = (v >= 10) ? '0' + v / 10 : ' ';
66 16           sv_catpvn_nomg(dsv, buf, 2);
67 16           }
68              
69 5           static void write_u2UP(pTHX_ SV* dsv, unsigned int v) {
70             char buf[2];
71 5 100         if (v >= 10) {
72 3           buf[0] = '0' + v / 10;
73 3           buf[1] = '0' + v % 10;
74 3           sv_catpvn_nomg(dsv, buf, 2);
75             } else {
76 2           buf[0] = '0' + v;
77 2           sv_catpvn_nomg(dsv, buf, 1);
78             }
79 5           }
80              
81 78           static void tstr_write_fraction(pTHX_ SV* dsv, int32_t nanosecond, int precision) {
82             char buf[10];
83             int p, j;
84              
85 78 100         if (nanosecond <= 0 && precision < 0)
    100          
86 34           return;
87              
88 48           p = precision;
89 48 100         if (p < 0) {
90 3 50         if ((nanosecond % 1000000) == 0) p = 3;
91 0 0         else if ((nanosecond % 1000) == 0) p = 6;
92 0           else p = 9;
93             }
94              
95 48 100         if (p == 0)
96 4           return;
97              
98 44           buf[0] = '.';
99 230 100         for (j = 0; j < p; j++)
100 186           buf[j + 1] = '0' + (nanosecond / kPow10[8 - j]) % 10;
101 44           sv_catpvn_nomg(dsv, buf, p + 1);
102             }
103              
104 57           static void tstr_write_offset_basic(pTHX_ SV* dsv,
105             int32_t offset,
106             const char* zulu,
107             STRLEN zulu_len) {
108             char buf[5];
109             int sign, h, m;
110              
111 57 100         if (offset == 0 && zulu) {
    100          
112 23           sv_catpvn_nomg(dsv, zulu, zulu_len);
113 23           return;
114             }
115              
116 34 100         if (offset < 0)
117 9           sign = '-', offset = -offset;
118             else
119 25           sign = '+';
120              
121 34           h = offset / 60;
122 34           m = offset % 60;
123              
124 34           buf[0] = (char)sign;
125 34           buf[1] = '0' + h / 10;
126 34           buf[2] = '0' + h % 10;
127 34           buf[3] = '0' + m / 10;
128 34           buf[4] = '0' + m % 10;
129 34           sv_catpvn_nomg(dsv, buf, 5);
130             }
131              
132 15           static void tstr_write_offset_extended_numeric(pTHX_ SV* dsv, int32_t offset) {
133             char buf[6];
134             int sign, h, m;
135              
136 15 100         if (offset < 0)
137 5           sign = '-', offset = -offset;
138             else
139 10           sign = '+';
140              
141 15           h = offset / 60;
142 15           m = offset % 60;
143              
144 15           buf[0] = (char)sign;
145 15           buf[1] = '0' + h / 10;
146 15           buf[2] = '0' + h % 10;
147 15           buf[3] = ':';
148 15           buf[4] = '0' + m / 10;
149 15           buf[5] = '0' + m % 10;
150 15           sv_catpvn_nomg(dsv, buf, 6);
151 15           }
152              
153 50           static void tstr_write_offset_extended(pTHX_ SV* dsv, int32_t offset) {
154 50 100         if (offset == 0)
155 39           sv_catpvn_nomg(dsv, "Z", 1);
156             else
157 11           tstr_write_offset_extended_numeric(aTHX_ dsv, offset);
158 50           }
159              
160 103           static void tstr_write_extended_hms(pTHX_ SV* dsv,
161             unsigned int h,
162             unsigned int m,
163             unsigned int s) {
164             char buf[8];
165 103           buf[0] = '0' + h / 10;
166 103           buf[1] = '0' + h % 10;
167 103           buf[2] = ':';
168 103           buf[3] = '0' + m / 10;
169 103           buf[4] = '0' + m % 10;
170 103           buf[5] = ':';
171 103           buf[6] = '0' + s / 10;
172 103           buf[7] = '0' + s % 10;
173 103           sv_catpvn_nomg(dsv, buf, 8);
174 103           }
175              
176 54           static void tstr_write_extended_ymd(pTHX_ SV* dsv,
177             unsigned int y,
178             unsigned int m,
179             unsigned int d) {
180             char buf[10];
181 54           buf[3] = '0' + y % 10; y /= 10;
182 54           buf[2] = '0' + y % 10; y /= 10;
183 54           buf[1] = '0' + y % 10; y /= 10;
184 54           buf[0] = '0' + y % 10;
185 54           buf[4] = '-';
186 54           buf[5] = '0' + m / 10;
187 54           buf[6] = '0' + m % 10;
188 54           buf[7] = '-';
189 54           buf[8] = '0' + d / 10;
190 54           buf[9] = '0' + d % 10;
191 54           sv_catpvn_nomg(dsv, buf, 10);
192 54           }
193              
194             typedef struct {
195             const char* spec;
196             const char* zulu;
197             STRLEN zulu_len;
198             } tstr_format_info_t;
199              
200             #define TSTR_FMT(s) {s, NULL, 0}
201             #define TSTR_FMT_ZULU(s, zb) {s, zb, sizeof(zb) - 1}
202             #define TSTR_FMT_NONE {NULL, NULL, 0}
203              
204             static const tstr_format_info_t kFormatInfo[TSTR_FORMAT_TYPE_COUNT] = {
205             [TSTR_FORMAT_UNKNOWN] = TSTR_FMT_NONE,
206             [TSTR_FORMAT_ANSIC] = TSTR_FMT("%a %b %e %T %Y"),
207             [TSTR_FORMAT_ASN1GT] = TSTR_FMT_ZULU("%Y%m%d%H%M%S%f%z", "Z"),
208             [TSTR_FORMAT_ASN1UT] = TSTR_FMT_ZULU("%y%m%d%H%M%S%z", "Z"),
209             [TSTR_FORMAT_CLF] = TSTR_FMT("%d/%b/%Y:%T%f %z"),
210             [TSTR_FORMAT_DATETIME] = TSTR_FMT_NONE,
211             [TSTR_FORMAT_ECMASCRIPT] = TSTR_FMT("%a %b %d %Y %T GMT%z"),
212             [TSTR_FORMAT_GITDATE] = TSTR_FMT("%a %b %-d %T %Y %z"),
213             [TSTR_FORMAT_ISO8601] = TSTR_FMT("%FT%T%f%Z"),
214             [TSTR_FORMAT_ISO9075] = TSTR_FMT("%F %T%f %:z"),
215             [TSTR_FORMAT_RFC2616] = TSTR_FMT("%a, %d %b %Y %T GMT"),
216             [TSTR_FORMAT_RFC2822] = TSTR_FMT("%a, %d %b %Y %T %z"),
217             [TSTR_FORMAT_RFC2822FWS] = TSTR_FMT("%a, %d %b %Y %T %z"),
218             [TSTR_FORMAT_RFC3339] = TSTR_FMT("%FT%T%f%Z"),
219             [TSTR_FORMAT_RFC3501] = TSTR_FMT("%d-%b-%Y %T %z"),
220             [TSTR_FORMAT_RFC4287] = TSTR_FMT("%FT%T%f%Z"),
221             [TSTR_FORMAT_RFC5280] = TSTR_FMT_NONE,
222             [TSTR_FORMAT_RFC5545] = TSTR_FMT("%Y%m%dT%H%M%SZ"),
223             [TSTR_FORMAT_RFC9557] = TSTR_FMT("%FT%T%f%Z"),
224             [TSTR_FORMAT_RUBYDATE] = TSTR_FMT("%a %b %d %T %z %Y"),
225             [TSTR_FORMAT_UNIXDATE] = TSTR_FMT_ZULU("%a %b %e %T %z %Y", "UTC"),
226             [TSTR_FORMAT_UNIXSTAMP] = TSTR_FMT_ZULU("%a %b %e %T%f %z %Y", "UTC"),
227             [TSTR_FORMAT_W3CDTF] = TSTR_FMT("%FT%T%f%Z"),
228             };
229              
230             #undef TSTR_FMT
231             #undef TSTR_FMT_ZULU
232             #undef TSTR_FMT_NONE
233              
234 122           static void tstr_time2str_format(pTHX_ SV* dsv,
235             const tstr_datetime_t* dt,
236             int precision,
237             const char* spec,
238             const char* zulu,
239             STRLEN zulu_len) {
240 122           const char* s = spec;
241             const char* p;
242             int dow;
243              
244 768 100         while (*s) {
245 654           p = s;
246 988 100         while (*s && *s != '%')
    100          
247 334           s++;
248              
249 654 100         if (s > p)
250 296           sv_catpvn_nomg(dsv, p, s - p);
251              
252 654 100         if (!*s)
253 8           break;
254              
255 646           s++;
256              
257 646           switch (*s++) {
258 39           case 'a':
259 39           dow = tstr_calendar_rdn_to_dow(dt->rdn);
260 39           sv_catpvn_nomg(dsv, kShortDayName[dow - 1], 3);
261 39           break;
262              
263 49           case 'b':
264 49           sv_catpvn_nomg(dsv, kShortMonthName[dt->month - 1], 3);
265 49           break;
266              
267 47           case 'd':
268 47           write_u20P(aTHX_ dsv, dt->day);
269 47           break;
270              
271 16           case 'e':
272 16           write_u2SP(aTHX_ dsv, dt->day);
273 16           break;
274              
275 5           case '-':
276 5 50         if (*s == 'd') {
277 5           s++;
278 5           write_u2UP(aTHX_ dsv, dt->day);
279             }
280 5           break;
281              
282 19           case 'm':
283 19           write_u20P(aTHX_ dsv, dt->month);
284 19           break;
285              
286 5           case 'y':
287 5           write_u20P(aTHX_ dsv, dt->year % 100);
288 5           break;
289              
290 63           case 'Y':
291 63           write_u40P(aTHX_ dsv, dt->year);
292 63           break;
293              
294 19           case 'H':
295 19           write_u20P(aTHX_ dsv, dt->hour);
296 19           break;
297              
298 19           case 'M':
299 19           write_u20P(aTHX_ dsv, dt->minute);
300 19           break;
301              
302 19           case 'S':
303 19           write_u20P(aTHX_ dsv, dt->second);
304 19           break;
305              
306 54           case 'F':
307 54           tstr_write_extended_ymd(aTHX_ dsv, dt->year, dt->month, dt->day);
308 54           break;
309              
310 103           case 'T':
311 103           tstr_write_extended_hms(aTHX_ dsv, dt->hour, dt->minute, dt->second);
312 103           break;
313              
314 78           case 'f':
315 78           tstr_write_fraction(aTHX_ dsv, dt->nanosecond, precision);
316 78           break;
317              
318 57           case 'z':
319 57           tstr_write_offset_basic(aTHX_ dsv, dt->offset, zulu, zulu_len);
320 57           break;
321              
322 4           case ':':
323 4 50         if (*s == 'z') {
324 4           s++;
325 4           tstr_write_offset_extended_numeric(aTHX_ dsv, dt->offset);
326             }
327 4           break;
328              
329 50           case 'Z':
330 50           tstr_write_offset_extended(aTHX_ dsv, dt->offset);
331 50           break;
332              
333 0           case '%':
334 0           sv_catpvn_nomg(dsv, "%", 1);
335 0           break;
336              
337 0           default:
338 0           sv_catpvn_nomg(dsv, s - 2, 2);
339 0           break;
340             }
341             }
342 122           }
343              
344 122           bool tstr_time2str(pTHX_ SV* dsv,
345             const tstr_datetime_t* dt,
346             int precision,
347             tstr_format_t fmt) {
348             const tstr_format_info_t* info;
349              
350 122 50         if (!tstr_format_is_known(fmt))
351 0           return false;
352              
353 122           info = &kFormatInfo[fmt];
354 122 50         if (!info->spec)
355 0           return false;
356              
357 122           tstr_time2str_format(aTHX_ dsv, dt, precision, info->spec, info->zulu,
358 122           info->zulu_len);
359 122           return true;
360             }