File Coverage

src/moment_fmt.c
Criterion Covered Total %
statement 220 234 94.0
branch 76 98 77.5
condition n/a
subroutine n/a
pod n/a
total 296 332 89.1


line stmt bran cond sub pod time code
1             #include "moment.h"
2             #include "dt_core.h"
3             #include "dt_accessor.h"
4              
5             typedef enum {
6             PAD_DEFAULT,
7             PAD_NONE,
8             PAD_ZERO,
9             PAD_SPACE,
10             } pad_t;
11              
12             static const char *aDoW[] = {
13             "Mon",
14             "Tue",
15             "Wed",
16             "Thu",
17             "Fri",
18             "Sat",
19             "Sun",
20             };
21              
22             static const char *fDoW[] = {
23             "Monday",
24             "Tuesday",
25             "Wednesday",
26             "Thursday",
27             "Friday",
28             "Saturday",
29             "Sunday",
30             };
31              
32             static const char *aMonth[] = {
33             "Jan",
34             "Feb",
35             "Mar",
36             "Apr",
37             "May",
38             "Jun",
39             "Jul",
40             "Aug",
41             "Sep",
42             "Oct",
43             "Nov",
44             "Dec",
45             };
46              
47             static const char *fMonth[] = {
48             "January",
49             "February",
50             "March",
51             "April",
52             "May",
53             "June",
54             "July",
55             "August",
56             "September",
57             "October",
58             "November",
59             "December",
60             };
61              
62             static const char *Meridiem[] = {
63             "AM",
64             "PM",
65             };
66              
67             /*
68             * The first Sunday of January is the first day of week 1; days in the new
69             * year before this are in week 0.
70             */
71             static int
72 11           dt_week_number_sun(dt_t dt) {
73 11           int sunday = dt_doy(dt) - dt_dow(dt) % 7;
74 11           return (sunday + 6) / 7;
75             }
76              
77             /*
78             * The first Monday of January is the first day of week 1; days in the new
79             * year before this are in week 0.
80             */
81             static int
82 11           dt_week_number_mon(dt_t dt) {
83 11           int monday = dt_doy(dt) - (dt_dow(dt) + 6) % 7;
84 11           return (monday + 6) / 7;
85             }
86              
87             static int
88 27           moment_hour_12(const moment_t *mt) {
89 27           int h = moment_hour(mt) % 12;
90 27 100         if (h == 0)
91 6           h = 12;
92 27           return h;
93             }
94              
95             static const char *
96 8           moment_hour_meridiem(const moment_t *mt) {
97 8           return Meridiem[moment_hour(mt) / 12];
98             }
99              
100             static bool
101 114           supports_padding_flag(const char c) {
102 114 50         switch (c) {
103             case 'C':
104             case 'd':
105             case 'e':
106             case 'g':
107             case 'G':
108             case 'H':
109             case 'I':
110             case 'j':
111             case 'k':
112             case 'l':
113             case 'm':
114             case 'M':
115             case 'S':
116             case 'U':
117             case 'V':
118             case 'W':
119             case 'y':
120             case 'Y':
121 114           return TRUE;
122             }
123 0           return FALSE;
124             }
125              
126             static void
127 293           THX_format_num(pTHX_ SV *dsv, size_t width, pad_t want, pad_t def, unsigned int v) {
128             char buf[20], *p, *e, *d, c;
129             size_t nlen, plen, dlen;
130              
131 293           p = e = buf + sizeof(buf);
132             do {
133 568           *--p = '0' + (v % 10);
134 568 100         } while (v /= 10);
135              
136 293 100         if (want == PAD_DEFAULT)
137 179           want = def;
138              
139 293 100         if (want == PAD_ZERO) c = '0';
140 90 100         else if (want == PAD_SPACE) c = ' ';
141 38           else width = 0;
142              
143 293           nlen = e - p;
144 293 100         plen = (width > nlen) ? width - nlen : 0;
145 293           dlen = nlen + plen;
146 293 50         (void)SvGROW(dsv, SvCUR(dsv) + dlen + 1);
    50          
147 293           d = SvPVX(dsv) + SvCUR(dsv);
148 293 100         if (plen) {
149 74           memset(d, c, plen);
150 74           d += plen;
151             }
152 293           memcpy(d, p, nlen);
153 293           SvCUR_set(dsv, SvCUR(dsv) + dlen);
154 293           *SvEND(dsv) = '\0';
155 293           }
156              
157              
158             #define CHR(n, d) (char)('0' + ((n) / (d)) % 10)
159             static void
160 28           THX_format_f(pTHX_ SV *dsv, const moment_t *mt, int len) {
161             char buf[9];
162             int ns;
163              
164 28 50         if (len > 9) len = 9;
165 28 100         else if (len < 0) len = 0;
166 28           ns = moment_nanosecond(mt);
167 28 100         if (len == 0) {
168 16 100         if ((ns % 1000000) == 0) len = 3;
169 8 50         else if ((ns % 1000) == 0) len = 6;
170 0           else len = 9;
171             }
172 28           switch (len) {
173 1           case 9: buf[8] = CHR(ns, 1);
174 1           case 8: buf[7] = CHR(ns, 10);
175 1           case 7: buf[6] = CHR(ns, 100);
176 13           case 6: buf[5] = CHR(ns, 1000);
177 14           case 5: buf[4] = CHR(ns, 10000);
178 15           case 4: buf[3] = CHR(ns, 100000);
179 28           case 3: buf[2] = CHR(ns, 1000000);
180 28           case 2: buf[1] = CHR(ns, 10000000);
181 28           case 1: buf[0] = CHR(ns, 100000000);
182             }
183 28           sv_catpvn(dsv, buf, len);
184 28           }
185             #undef CHR
186              
187             static void
188 3           THX_format_s(pTHX_ SV *dsv, const moment_t *mt) {
189             char buf[30], *p, *e;
190             int64_t v;
191              
192 3           v = moment_epoch(mt);
193 3           p = e = buf + sizeof(buf);
194 3 50         if (v < 0) {
195             do {
196 0           *--p = '0' - (v % 10);
197 0 0         } while (v /= 10);
198 0           *--p = '-';
199             }
200             else {
201             do {
202 21           *--p = '0' + (v % 10);
203 21 100         } while (v /= 10);
204             }
205 3           sv_catpvn(dsv, p, e - p);
206 3           }
207              
208             static void
209 180           THX_format_z(pTHX_ SV *dsv, const moment_t *mt, int extended) {
210             int offset, sign;
211              
212 180           offset = moment_offset(mt);
213 180 100         if (offset < 0)
214 83           sign = '-', offset = -offset;
215             else
216 97           sign = '+';
217 180 100         if (extended)
218 86           sv_catpvf(dsv, "%c%02d:%02d", sign, offset / 60, offset % 60);
219             else
220 94           sv_catpvf(dsv, "%c%04d", sign, (offset / 60) * 100 + (offset % 60));
221 180           }
222              
223             static void
224 95           THX_format_Z(pTHX_ SV *dsv, const moment_t *mt) {
225             int offset, sign;
226              
227 95           offset = moment_offset(mt);
228 95 100         if (offset == 0)
229 2           sv_catpvn(dsv, "Z", 1);
230             else {
231 93 100         if (offset < 0)
232 42           sign = '-', offset = -offset;
233             else
234 51           sign = '+';
235 93           sv_catpvf(dsv, "%c%02d:%02d", sign, offset / 60, offset % 60);
236             }
237 95           }
238              
239             #define format_num(dsv, width, wanted, def, num) \
240             THX_format_num(aTHX_ dsv, width, wanted, def, num)
241              
242             #define format_f(dsv, mt, len) \
243             THX_format_f(aTHX_ dsv, mt, len)
244              
245             #define format_s(dsv, mt) \
246             THX_format_s(aTHX_ dsv, mt)
247              
248             #define format_z(dsv, mt, extended) \
249             THX_format_z(aTHX_ dsv, mt, extended)
250              
251             #define format_Z(dsv, mt) \
252             THX_format_Z(aTHX_ dsv, mt)
253              
254             SV *
255 634           THX_moment_strftime(pTHX_ const moment_t *mt, const char *s, STRLEN len) {
256             const char *e, *p;
257             char c;
258             SV *dsv;
259             dt_t dt;
260             pad_t pad;
261             int year, month, day, width, zextd;
262              
263 634           dsv = sv_2mortal(newSV(16));
264 634           SvCUR_set(dsv, 0);
265 634           SvPOK_only(dsv);
266              
267 634           dt = moment_local_dt(mt);
268 634           dt_to_ymd(dt, &year, &month, &day);
269              
270 634           e = s + len;
271 1371 100         while (s < e) {
272 737           p = (const char *)memchr(s, '%', e - s);
273 737 50         if (p == NULL || p + 1 == e)
    50          
274 0           p = e;
275 737           sv_catpvn(dsv, s, p - s);
276 737 50         if (p == e)
277 0           break;
278              
279 737           pad = PAD_DEFAULT;
280 737           width = -1;
281 737           zextd = 0;
282 737           s = p + 1;
283              
284             label:
285 960           switch (c = *s++) {
286             case 'a': /* locale's abbreviated day of the week name */
287 10           sv_catpv(dsv, aDoW[dt_dow(dt) - 1]);
288 10           break;
289             case 'A': /* locale's full day of the week name */
290 10           sv_catpv(dsv, fDoW[dt_dow(dt) - 1]);
291 10           break;
292             case 'b': /* locale's abbreviated month name */
293             case 'h':
294 30           sv_catpv(dsv, aMonth[month - 1]);
295 30           break;
296             case 'B': /* locale's full month name */
297 15           sv_catpv(dsv, fMonth[month - 1]);
298 15           break;
299             case 'c': /* locale's date and time (C locale: %a %b %e %H:%M:%S %Y) */
300 10           sv_catpvf(dsv, "%s %s %2d %02d:%02d:%02d %04d",
301 5           aDoW[dt_dow(dt) - 1],
302 5           aMonth[month - 1],
303             day,
304             moment_hour(mt),
305             moment_minute(mt),
306             moment_second(mt),
307             year);
308 5           break;
309             case 'C':
310 11           format_num(dsv, 2, pad, PAD_ZERO, year / 100);
311 11           break;
312             case 'd':
313 17           format_num(dsv, 2, pad, PAD_ZERO, day);
314 17           break;
315             case 'x': /* locale's time representation (C locale: %m/%d/%y) */
316             case 'D':
317 10           sv_catpvf(dsv, "%02d/%02d/%02d", month, day, year % 100);
318 10           break;
319             case 'e':
320 14           format_num(dsv, 2, pad, PAD_SPACE, day);
321 14           break;
322             case 'f': /* extended conversion specification */
323 24 100         if (moment_nanosecond(mt)) {
324 16           sv_catpvn(dsv, ".", 1);
325 16           format_f(dsv, mt, width);
326             }
327 24           break;
328             case 'F':
329 5           sv_catpvf(dsv, "%04d-%02d-%02d", year, month, day);
330 5           break;
331             case 'g':
332 11           format_num(dsv, 2, pad, PAD_ZERO, dt_yow(dt) % 100);
333 11           break;
334             case 'G':
335 17           format_num(dsv, 4, pad, PAD_ZERO, dt_yow(dt));
336 17           break;
337             case 'H':
338 29           format_num(dsv, 2, pad, PAD_ZERO, moment_hour(mt));
339 29           break;
340             case 'I':
341 11           format_num(dsv, 2, pad, PAD_ZERO, moment_hour_12(mt));
342 11           break;
343             case 'j':
344 20           format_num(dsv, 3, pad, PAD_ZERO, dt_doy(dt));
345 20           break;
346             case 'k': /* extended conversion specification */
347 10           format_num(dsv, 2, pad, PAD_SPACE, moment_hour(mt));
348 10           break;
349             case 'l': /* extended conversion specification */
350 11           format_num(dsv, 2, pad, PAD_SPACE, moment_hour_12(mt));
351 11           break;
352             case 'm':
353 17           format_num(dsv, 2, pad, PAD_ZERO, month);
354 17           break;
355             case 'M':
356 29           format_num(dsv, 2, pad, PAD_ZERO, moment_minute(mt));
357 29           break;
358             case 'n':
359 3           sv_catpvn(dsv, "\n", 1);
360 3           break;
361             case 'N': /* extended conversion specification */
362 12           format_f(dsv, mt, width);
363 12           break;
364             case 'p': /* locale's equivalent of either a.m. or p.m (C locale: AM or PM) */
365 3           sv_catpv(dsv, moment_hour_meridiem(mt));
366 3           break;
367             case 'r': /* locale's time in a.m. and p.m. notation (C locale: %I:%M:%S %p) */
368 5           sv_catpvf(dsv, "%02d:%02d:%02d %s",
369             moment_hour_12(mt),
370             moment_minute(mt),
371             moment_second(mt),
372             moment_hour_meridiem(mt));
373 5           break;
374             case 'R':
375 5           sv_catpvf(dsv, "%02d:%02d",
376             moment_hour(mt),
377             moment_minute(mt));
378 5           break;
379             case 's': /* extended conversion specification */
380 3           format_s(dsv, mt);
381 3           break;
382             case 'S':
383 23           format_num(dsv, 2, pad, PAD_ZERO, moment_second(mt));
384 23           break;
385             case 't':
386 3           sv_catpvn(dsv, "\t", 1);
387 3           break;
388             case 'X': /* locale's date representation (C locale: %H:%M:%S) */
389             case 'T':
390 10           sv_catpvf(dsv, "%02d:%02d:%02d",
391             moment_hour(mt),
392             moment_minute(mt),
393             moment_second(mt));
394 10           break;
395             case 'u':
396 11           sv_catpvf(dsv, "%d", dt_dow(dt));
397 11           break;
398             case 'U':
399 11           format_num(dsv, 2, pad, PAD_ZERO, dt_week_number_sun(dt));
400 11           break;
401             case 'V':
402 17           format_num(dsv, 2, pad, PAD_ZERO, dt_woy(dt));
403 17           break;
404             case 'w':
405 5           sv_catpvf(dsv, "%d", dt_dow(dt) % 7);
406 5           break;
407             case 'W':
408 11           format_num(dsv, 2, pad, PAD_ZERO, dt_week_number_mon(dt));
409 11           break;
410             case 'y':
411 11           format_num(dsv, 2, pad, PAD_ZERO, year % 100);
412 11           break;
413             case 'Y':
414 23           format_num(dsv, 4, pad, PAD_ZERO, year);
415 23           break;
416             case 'z':
417 180           format_z(dsv, mt, zextd);
418 180           break;
419             case 'Z':
420 95           format_Z(dsv, mt);
421 95           break;
422             case '%':
423 0           sv_catpvn(dsv, "%", 1);
424 0           break;
425             case ':':
426 86 50         if (s < e && *s == 'z') {
    50          
427 86           zextd = 1;
428 86           goto label;
429             }
430 0           goto unknown;
431             case '_':
432 38 50         if (s < e && supports_padding_flag(*s)) {
    50          
433 38           pad = PAD_SPACE;
434 38           goto label;
435             }
436 0           goto unknown;
437             case '-':
438 38 50         if (s < e && supports_padding_flag(*s)) {
    50          
439 38           pad = PAD_NONE;
440 38           goto label;
441             }
442 0           goto unknown;
443             case '0': case '1': case '2': case '3': case '4':
444             case '5': case '6': case '7': case '8': case '9':
445 61 50         if (s < e && (*s == 'f' || *s == 'N')) {
    100          
    100          
446 23           width = c - '0';
447 23           goto label;
448             }
449 38 50         if (s < e && c == '0' && supports_padding_flag(*s)) {
    50          
    50          
450 38           pad = PAD_ZERO;
451 38           goto label;
452             }
453             /* FALLTROUGH */
454             default:
455             unknown:
456 0           sv_catpvn(dsv, p, s - p);
457 0           break;
458             }
459             }
460 634           return dsv;
461             }
462              
463             SV *
464 4430           THX_moment_to_string(pTHX_ const moment_t *mt, bool reduced) {
465             SV *dsv;
466             dt_t dt;
467             int year, month, day, sec, ns, offset, sign;
468              
469 4430           dsv = sv_2mortal(newSV(16));
470 4430           SvCUR_set(dsv, 0);
471 4430           SvPOK_only(dsv);
472              
473 4430           dt = moment_local_dt(mt);
474 4430           dt_to_ymd(dt, &year, &month, &day);
475              
476 4430           sv_catpvf(dsv, "%04d-%02d-%02dT%02d:%02d",
477             year, month, day, moment_hour(mt), moment_minute(mt));
478              
479 4430           sec = moment_second(mt);
480 4430           ns = moment_nanosecond(mt);
481 4430 100         if (!reduced || sec || ns) {
    100          
    50          
482 4429           sv_catpvf(dsv, ":%02d", sec);
483 4429 100         if (ns) {
484 3027 100         if ((ns % 1000000) == 0) sv_catpvf(dsv, ".%03d", ns / 1000000);
485 1545 100         else if ((ns % 1000) == 0) sv_catpvf(dsv, ".%06d", ns / 1000);
486 1350           else sv_catpvf(dsv, ".%09d", ns);
487             }
488             }
489              
490 4430           offset = moment_offset(mt);
491 4430 100         if (offset == 0)
492 3365           sv_catpvn(dsv, "Z", 1);
493             else {
494 1065 100         if (offset < 0)
495 211           sign = '-', offset = -offset;
496             else
497 854           sign = '+';
498              
499 1065 100         if (reduced && (offset % 60) == 0)
    100          
500 1           sv_catpvf(dsv, "%c%02d", sign, offset / 60);
501             else
502 1064           sv_catpvf(dsv, "%c%02d:%02d", sign, offset / 60, offset % 60);
503             }
504              
505 4430           return dsv;
506             }
507