File Coverage

src/dt_parse_iso.c
Criterion Covered Total %
statement 235 303 77.5
branch 98 198 49.4
condition n/a
subroutine n/a
pod n/a
total 333 501 66.4


line stmt bran cond sub pod time code
1             /*
2             * Copyright (c) 2012-2014 Christian Hansen
3             *
4             * All rights reserved.
5             *
6             * Redistribution and use in source and binary forms, with or without
7             * modification, are permitted provided that the following conditions are met:
8             *
9             * 1. Redistributions of source code must retain the above copyright notice, this
10             * list of conditions and the following disclaimer.
11             * 2. Redistributions in binary form must reproduce the above copyright notice,
12             * this list of conditions and the following disclaimer in the documentation
13             * and/or other materials provided with the distribution.
14             *
15             * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16             * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17             * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18             * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19             * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20             * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21             * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22             * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23             * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24             * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25             */
26             #include
27             #include "dt_core.h"
28             #include "dt_valid.h"
29              
30             static size_t
31 1579           count_digits(const unsigned char * const p, size_t i, const size_t len) {
32 1579           const size_t n = i;
33              
34 5860 100         for(; i < len; i++) {
35 5751           const unsigned char c = p[i] - '0';
36 5751 100         if (c > 9)
37 1470           break;
38             }
39 1579           return i - n;
40             }
41              
42             static int
43 1646           parse_number(const unsigned char * const p, size_t i, const size_t len) {
44 1646           int v = 0;
45              
46 1646           switch (len) {
47 42           case 9: v += (p[i++] - '0') * 100000000;
48 47           case 8: v += (p[i++] - '0') * 10000000;
49 52           case 7: v += (p[i++] - '0') * 1000000;
50 70           case 6: v += (p[i++] - '0') * 100000;
51 75           case 5: v += (p[i++] - '0') * 10000;
52 346           case 4: v += (p[i++] - '0') * 1000;
53 369           case 3: v += (p[i++] - '0') * 100;
54 1630           case 2: v += (p[i++] - '0') * 10;
55 1646           case 1: v += (p[i++] - '0');
56             }
57 1646           return v;
58             }
59              
60             static const int pow_10[10] = {
61             1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
62             };
63              
64             /*
65             * fffffffff
66             */
67              
68             static size_t
69 102           parse_fraction_digits(const unsigned char *p, size_t i, size_t len, int *fp) {
70             size_t n, ndigits;
71              
72 102           ndigits = n = count_digits(p, i, len);
73 102 50         if (ndigits < 1)
74 0           return 0;
75 102 50         if (ndigits > 9)
76 0           ndigits = 9;
77 102 50         if (fp)
78 102           *fp = parse_number(p, i, ndigits) * pow_10[9 - ndigits];
79 102           return n;
80             }
81              
82             /*
83             * hh
84             * hhmm
85             * hhmmss
86             * hhmmss.fffffffff
87             * hhmmss,fffffffff
88             */
89              
90             size_t
91 17           dt_parse_iso_time_basic(const char *str, size_t len, int *sp, int *fp) {
92             const unsigned char *p;
93             int h, m, s, f;
94             size_t n;
95              
96 17           p = (const unsigned char *)str;
97 17           n = count_digits(p, 0, len);
98 17           m = s = f = 0;
99 17           switch (n) {
100             case 2: /* hh */
101 2           h = parse_number(p, 0, 2);
102 2           goto hms;
103             case 4: /* hhmm */
104 6           h = parse_number(p, 0, 2);
105 6           m = parse_number(p, 2, 2);
106 6           goto hms;
107             case 6: /* hhmmss */
108 9           h = parse_number(p, 0, 2);
109 9           m = parse_number(p, 2, 2);
110 9           s = parse_number(p, 4, 2);
111 9           break;
112             default:
113 0           return 0;
114             }
115              
116             /* hhmmss.fffffffff */
117 9 50         if (n < len && (p[n] == '.' || p[n] == ',')) {
    100          
    50          
118 3           size_t r = parse_fraction_digits(p, ++n, len, &f);
119 3 50         if (!r)
120 0           return 0;
121 3           n += r;
122             }
123              
124             hms:
125 17 50         if (h > 23 || m > 59 || s > 59) {
    50          
    50          
126 0 0         if (!(h == 24 && m == 0 && s == 0 && f == 0))
    0          
    0          
    0          
127 0           return 0;
128             }
129              
130 17 50         if (sp)
131 17           *sp = h * 3600 + m * 60 + s;
132 17 50         if (fp)
133 17           *fp = f;
134 17           return n;
135             }
136              
137             /*
138             * Z
139             * ±hh
140             * ±hhmm
141             */
142              
143             size_t
144 15           dt_parse_iso_zone_basic(const char *str, size_t len, int *op) {
145             const unsigned char *p;
146             int o, h, m, sign;
147             size_t n;
148              
149 15 50         if (len < 1)
150 0           return 0;
151              
152 15           p = (const unsigned char *)str;
153 15           switch (*p) {
154             case 'Z':
155 3           o = 0;
156 3           n = 1;
157 3           goto zulu;
158             case '+':
159 10           sign = 1;
160 10           break;
161             case '-':
162 0           sign = -1;
163 0           break;
164             default:
165 2           return 0;
166             }
167              
168 10 50         if (len < 3)
169 0           return 0;
170              
171 10           n = count_digits(p, 1, len);
172 10           m = 0;
173 10           switch (n) {
174             case 2: /* ±hh */
175 1           h = parse_number(p, 1, 2);
176 1           n = 3;
177 1           break;
178             case 4: /* ±hhmm */
179 9           h = parse_number(p, 1, 2);
180 9           m = parse_number(p, 3, 2);
181 9           n = 5;
182 9           break;
183             default:
184 0           return 0;
185             }
186              
187 10 50         if (h > 23 || m > 59)
    50          
188 0           return 0;
189 10           o = sign * (h * 60 + m);
190             #ifdef DT_PARSE_ISO_STRICT
191             if (o == 0 && sign < 0)
192             return 0;
193             #endif
194              
195             zulu:
196 13 50         if (op)
197 13           *op = o;
198 13           return n;
199             }
200              
201             /*
202             * hh
203             * hh:mm
204             * hh:mm:ss
205             * hh:mm:ss.fffffffff
206             * hh:mm:ss,fffffffff
207             */
208              
209             size_t
210 187           dt_parse_iso_time_extended(const char *str, size_t len, int *sp, int *fp) {
211             const unsigned char *p;
212             int h, m, s, f;
213             size_t n;
214              
215 187           p = (const unsigned char *)str;
216 187 100         if (count_digits(p, 0, len) != 2)
217 1           return 0;
218              
219 186           h = parse_number(p, 0, 2);
220 186           m = s = f = 0;
221 186           n = 2;
222            
223 186 50         if (len < 3 || p[2] != ':')
    100          
224             goto hms;
225              
226 185 50         if (count_digits(p, 3, len) != 2)
227 0           return 0;
228              
229 185           m = parse_number(p, 3, 2);
230 185           n = 5;
231              
232 185 50         if (len < 6 || p[5] != ':')
    100          
233             goto hms;
234              
235 144 50         if (count_digits(p, 6, len) != 2)
236 0           return 0;
237              
238 144           s = parse_number(p, 6, 2);
239 144           n = 8;
240              
241             /* hh:mm:ss.fffffffff */
242 144 50         if (n < len && (p[n] == '.' || p[n] == ',')) {
    100          
    50          
243 99           size_t r = parse_fraction_digits(p, ++n, len, &f);
244 99 50         if (!r)
245 0           return 0;
246 99           n += r;
247             }
248              
249             hms:
250 186 50         if (h > 23 || m > 59 || s > 59) {
    50          
    50          
251 0 0         if (!(h == 24 && m == 0 && s == 0 && f == 0))
    0          
    0          
    0          
252 0           return 0;
253             }
254              
255 186 50         if (sp)
256 186           *sp = h * 3600 + m * 60 + s;
257 186 50         if (fp)
258 186           *fp = f;
259 187           return n;
260             }
261              
262             /*
263             * Z
264             * ±hh
265             * ±hh:mm
266             */
267              
268             size_t
269 122           dt_parse_iso_zone_extended(const char *str, size_t len, int *op) {
270             const unsigned char *p;
271             int o, h, m, sign;
272             size_t n;
273              
274 122 50         if (len < 1)
275 0           return 0;
276              
277 122           p = (const unsigned char *)str;
278 122           switch (*p) {
279             case 'Z':
280 74           o = 0;
281 74           n = 1;
282 74           goto zulu;
283             case '+':
284 37           sign = 1;
285 37           break;
286             case '-':
287 11           sign = -1;
288 11           break;
289             default:
290 0           return 0;
291             }
292              
293 48 50         if (len < 3 || count_digits(p, 1, len) != 2)
    50          
294 0           return 0;
295              
296 48           h = parse_number(p, 1, 2);
297 48           m = 0;
298 48           n = 3;
299              
300 48 100         if (len < 4 || p[3] != ':')
    50          
301             goto hm;
302              
303 42 50         if (count_digits(p, 4, len) != 2)
304 0           return 0;
305              
306 42           m = parse_number(p, 4, 2);
307 42           n = 6;
308              
309             hm:
310 48 50         if (h > 23 || m > 59)
    50          
311 0           return 0;
312 48           o = sign * (h * 60 + m);
313             #ifdef DT_PARSE_ISO_STRICT
314             if (o == 0 && sign < 0)
315             return 0;
316             #endif
317              
318             zulu:
319 122 50         if (op)
320 122           *op = o;
321 122           return n;
322             }
323              
324             /*
325             * z
326             * Z
327             * GMT
328             * GMT±h
329             * GMT±hhmm
330             * GMT±h:mm
331             * GMT±hh:mm
332             * UTC
333             * UTC±h
334             * UTC±hhmm
335             * UTC±h:mm
336             * UTC±hh:mm
337             * ±h
338             * ±hh
339             * ±hhmm
340             * ±h:mm
341             * ±hh:mm
342             */
343              
344             size_t
345 82           dt_parse_iso_zone_lenient(const char *str, size_t len, int *op) {
346             const unsigned char *p;
347             int o, h, m, sign;
348             size_t n;
349              
350 82 50         if (len < 1)
351 0           return 0;
352              
353 82           p = (const unsigned char *)str;
354 82           switch (*p) {
355             case 'z':
356             case 'Z':
357 12           o = 0;
358 12           n = 1;
359 12           goto zulu;
360             case 'G':
361 9 50         if (len < 3 || p[1] != 'M' || p[2] != 'T')
    50          
    50          
362 0           return 0;
363 9 100         if (len > 3 && (p[3] == '+' || p[3] == '-')) {
    100          
    50          
364 8 50         if (!(n = dt_parse_iso_zone_lenient(str + 3, len - 3, op)))
365 0           return 0;
366 8           return n + 3;
367             }
368 1           o = 0;
369 1           n = 3;
370 1           goto zulu;
371             case 'U':
372 9 50         if (len < 3 || p[1] != 'T' || p[2] != 'C')
    50          
    50          
373 0           return 0;
374 9 100         if (len > 3 && (p[3] == '+' || p[3] == '-')) {
    100          
    50          
375 8 50         if (!(n = dt_parse_iso_zone_lenient(str + 3, len - 3, op)))
376 0           return 0;
377 8           return n + 3;
378             }
379 1           o = 0;
380 1           n = 3;
381 1           goto zulu;
382             case '+':
383 27           sign = 1;
384 27           break;
385             case '-':
386 25           sign = -1;
387 25           break;
388             default:
389 0           return 0;
390             }
391              
392 52 50         if (len < 2)
393 0           return 0;
394              
395 52           n = count_digits(p, 1, len);
396 52           m = 0;
397 52           switch (n) {
398             case 1: /* ±h */
399 4           h = parse_number(p, 1, 1);
400 4           n = 2;
401 4           break;
402             case 2: /* ±hh */
403 36           h = parse_number(p, 1, 2);
404 36           n = 3;
405 36           break;
406             case 4: /* ±hhmm */
407 12           h = parse_number(p, 1, 2);
408 12           m = parse_number(p, 3, 2);
409 12           n = 5;
410 12           goto hm;
411             default:
412 0           return 0;
413             }
414            
415 40 100         if (len < n + 1 || p[n] != ':')
    50          
416             goto hm;
417              
418 26 50         if (count_digits(p, ++n, len) != 2)
419 0           return 0;
420              
421 26           m = parse_number(p, n, 2);
422 26           n += 2;
423              
424             hm:
425 52 50         if (h > 23 || m > 59)
    50          
426 0           return 0;
427 52           o = sign * (h * 60 + m);
428              
429             zulu:
430 66 50         if (op)
431 66           *op = o;
432 66           return n;
433             }
434              
435             /*
436             * Basic Extended
437             * 20121224 2012-12-24 Calendar date (ISO 8601)
438             * 2012359 2012-359 Ordinal date (ISO 8601)
439             * 2012W521 2012-W52-1 Week date (ISO 8601)
440             * 2012Q485 2012-Q4-85 Quarter date
441             */
442             size_t
443 266           dt_parse_iso_date(const char *str, size_t len, dt_t *dtp) {
444             const unsigned char *p;
445             int y, x, d;
446             size_t n;
447             dt_t dt;
448              
449 266           p = (const unsigned char *)str;
450 266           n = count_digits(p, 0, len);
451 266           switch (n) {
452             case 4: /* 2012 (year) */
453 252           y = parse_number(p, 0, 4);
454 252           break;
455             case 7: /* 2012359 (basic ordinal date) */
456 4           y = parse_number(p, 0, 4);
457 4           d = parse_number(p, 4, 3);
458 4           goto yd;
459             case 8: /* 20121224 (basic calendar date) */
460 10           y = parse_number(p, 0, 4);
461 10           x = parse_number(p, 4, 2);
462 10           d = parse_number(p, 6, 2);
463 10           goto ymd;
464             default:
465 0           return 0;
466             }
467              
468 252 50         if (len < 8)
469 0           return 0;
470              
471 252           n = count_digits(p, 5, len);
472 252           switch (p[4]) {
473             case '-': /* 2012-359 | 2012-12-24 | 2012-W52-1 | 2012-Q4-85 */
474 248           break;
475             #ifndef DT_PARSE_ISO_STRICT
476             case 'Q': /* 2012Q485 */
477 0 0         if (n != 3)
478 0           return 0;
479 0           x = parse_number(p, 5, 1);
480 0           d = parse_number(p, 6, 2);
481 0           n = 8;
482 0           goto yqd;
483             #endif
484             case 'W': /* 2012W521 */
485 4 50         if (n != 3)
486 0           return 0;
487 4           x = parse_number(p, 5, 2);
488 4           d = parse_number(p, 7, 1);
489 4           n = 8;
490 4           goto ywd;
491             default:
492 0           return 0;
493             }
494              
495 248           switch (n) {
496             case 0: /* 2012-Q4-85 | 2012-W52-1 */
497 5           break;
498             case 2: /* 2012-12-24 */
499 238 50         if (p[7] != '-' || count_digits(p, 8, len) != 2)
    50          
500 0           return 0;
501 238           x = parse_number(p, 5, 2);
502 238           d = parse_number(p, 8, 2);
503 238           n = 10;
504 238           goto ymd;
505             case 3: /* 2012-359 */
506 5           d = parse_number(p, 5, 3);
507 5           n = 8;
508 5           goto yd;
509             default:
510 0           return 0;
511             }
512              
513 5 50         if (len < 10)
514 0           return 0;
515              
516 5           n = count_digits(p, 6, len);
517 5           switch (p[5]) {
518             #ifndef DT_PARSE_ISO_STRICT
519             case 'Q': /* 2012-Q4-85 */
520 0 0         if (n != 1 || p[7] != '-' || count_digits(p, 8, len) != 2)
    0          
    0          
521 0           return 0;
522 0           x = parse_number(p, 6, 1);
523 0           d = parse_number(p, 8, 2);
524 0           n = 10;
525 0           goto yqd;
526             #endif
527             case 'W': /* 2012-W52-1 */
528 5 50         if (n != 2 || p[8] != '-' || count_digits(p, 9, len) != 1)
    50          
    50          
529 0           return 0;
530 5           x = parse_number(p, 6, 2);
531 5           d = parse_number(p, 9, 1);
532 5           n = 10;
533 5           goto ywd;
534             default:
535 0           return 0;
536             }
537              
538             yd:
539 9 50         if (!dt_valid_yd(y, d))
540 0           return 0;
541 9           dt = dt_from_yd(y, d);
542 9           goto finish;
543              
544             ymd:
545 248 50         if (!dt_valid_ymd(y, x, d))
546 0           return 0;
547 248           dt = dt_from_ymd(y, x, d);
548 248           goto finish;
549              
550             #ifndef DT_PARSE_ISO_STRICT
551             yqd:
552 0 0         if (!dt_valid_yqd(y, x, d))
553 0           return 0;
554 0           dt = dt_from_yqd(y, x, d);
555 0           goto finish;
556             #endif
557              
558             ywd:
559 9 50         if (!dt_valid_ywd(y, x, d))
560 0           return 0;
561 9           dt = dt_from_ywd(y, x, d);
562              
563             finish:
564             #ifndef DT_PARSE_ISO_YEAR0
565 266 50         if (y < 1)
566 0           return 0;
567             #endif
568 266 50         if (dtp)
569 266           *dtp = dt;
570 266           return n;
571             }
572              
573             /*
574             * Basic Extended
575             * T12 N/A
576             * T1230 T12:30
577             * T123045 T12:30:45
578             * T123045.123456789 T12:30:45.123456789
579             * T123045,123456789 T12:30:45,123456789
580             *
581             * The time designator [T] may be omitted.
582             */
583              
584             size_t
585 66           dt_parse_iso_time(const char *str, size_t len, int *sod, int *nsec) {
586             size_t n, r;
587              
588 66 50         if (len < 2)
589 0           return 0;
590              
591 66 50         if (str[0] == 'T')
592 0           r = 1, ++str, --len;
593             else
594 66           r = 0;
595              
596 66 50         if (len < 2 || str[2] == ':')
    100          
597 64           n = dt_parse_iso_time_extended(str, len, sod, nsec);
598             else
599 2           n = dt_parse_iso_time_basic(str, len, sod, nsec);
600              
601 66 50         if (!n)
602 0           return 0;
603 66           return r + n;
604             }
605              
606             /*
607             * Basic Extended
608             * Z N/A
609             * ±hh N/A
610             * ±hhmm ±hh:mm
611             */
612              
613             size_t
614 0           dt_parse_iso_zone(const char *str, size_t len, int *offset) {
615 0 0         if (len < 3 || str[3] == ':')
    0          
616 0           return dt_parse_iso_zone_extended(str, len, offset);
617             else
618 0           return dt_parse_iso_zone_basic(str, len, offset);
619             }
620