File Coverage

src/dt_parse_iso.c
Criterion Covered Total %
statement 278 359 77.4
branch 98 198 49.4
condition n/a
subroutine n/a
pod n/a
total 376 557 67.5


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 3639           count_digits(const unsigned char * const p, size_t i, const size_t len) {
32 3639           const size_t n = i;
33              
34 13062 100         for(; i < len; i++) {
35 12953           const unsigned char c = p[i] - '0';
36 12953 100         if (c > 9)
37 3530           break;
38             }
39 3639           return i - n;
40             }
41              
42             static int
43 3706           parse_number(const unsigned char * const p, size_t i, const size_t len) {
44 3706           int v = 0;
45              
46 3706           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 857           case 4: v += (p[i++] - '0') * 1000;
53 880           case 3: v += (p[i++] - '0') * 100;
54 3690           case 2: v += (p[i++] - '0') * 10;
55 3706           case 1: v += (p[i++] - '0');
56             }
57 3706           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 2           case 2: /* hh */
101 2           h = parse_number(p, 0, 2);
102 2           goto hms;
103 6           case 4: /* hhmm */
104 6           h = parse_number(p, 0, 2);
105 6           m = parse_number(p, 2, 2);
106 6           goto hms;
107 9           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 0           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 6           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 3           case 'Z':
155 3           o = 0;
156 3           n = 1;
157 3           goto zulu;
158 10           case '+':
159 10           sign = 1;
160 10           break;
161 0           case '-':
162 0           sign = -1;
163 0           break;
164 2           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 1           case 2: /* ±hh */
175 1           h = parse_number(p, 1, 2);
176 1           n = 3;
177 1           break;
178 9           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 0           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 13           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 698           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 698           p = (const unsigned char *)str;
216 698 100         if (count_digits(p, 0, len) != 2)
217 1           return 0;
218              
219 697           h = parse_number(p, 0, 2);
220 697           m = s = f = 0;
221 697           n = 2;
222            
223 697 50         if (len < 3 || p[2] != ':')
    100          
224 504           goto hms;
225              
226 193 50         if (count_digits(p, 3, len) != 2)
227 0           return 0;
228              
229 193           m = parse_number(p, 3, 2);
230 193           n = 5;
231              
232 193 50         if (len < 6 || p[5] != ':')
    100          
233 41           goto hms;
234              
235 152 50         if (count_digits(p, 6, len) != 2)
236 0           return 0;
237              
238 152           s = parse_number(p, 6, 2);
239 152           n = 8;
240              
241             /* hh:mm:ss.fffffffff */
242 152 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 53           hms:
250 697 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 697 50         if (sp)
256 697           *sp = h * 3600 + m * 60 + s;
257 697 50         if (fp)
258 697           *fp = f;
259 697           return n;
260             }
261              
262             /*
263             * Z
264             * ±hh
265             * ±hh:mm
266             */
267              
268             size_t
269 633           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 633 50         if (len < 1)
275 0           return 0;
276              
277 633           p = (const unsigned char *)str;
278 633           switch (*p) {
279 585           case 'Z':
280 585           o = 0;
281 585           n = 1;
282 585           goto zulu;
283 37           case '+':
284 37           sign = 1;
285 37           break;
286 11           case '-':
287 11           sign = -1;
288 11           break;
289 0           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 6           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 48           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 633           zulu:
319 633 50         if (op)
320 633           *op = o;
321 633           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 12           case 'z':
356             case 'Z':
357 12           o = 0;
358 12           n = 1;
359 12           goto zulu;
360 9           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 9           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 27           case '+':
383 27           sign = 1;
384 27           break;
385 25           case '-':
386 25           sign = -1;
387 25           break;
388 0           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 4           case 1: /* ±h */
399 4           h = parse_number(p, 1, 1);
400 4           n = 2;
401 4           break;
402 36           case 2: /* ±hh */
403 36           h = parse_number(p, 1, 2);
404 36           n = 3;
405 36           break;
406 12           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 0           default:
412 0           return 0;
413             }
414            
415 40 100         if (len < n + 1 || p[n] != ':')
    50          
416 14           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 52           hm:
425 52 50         if (h > 23 || m > 59)
    50          
426 0           return 0;
427 52           o = sign * (h * 60 + m);
428              
429 66           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 777           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 777           p = (const unsigned char *)str;
450 777           n = count_digits(p, 0, len);
451 777           switch (n) {
452 763           case 4: /* 2012 (year) */
453 763           y = parse_number(p, 0, 4);
454 763           break;
455 4           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 10           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 0           default:
465 0           return 0;
466             }
467              
468 763 50         if (len < 8)
469 0           return 0;
470              
471 763           n = count_digits(p, 5, len);
472 763           switch (p[4]) {
473 759           case '-': /* 2012-359 | 2012-12-24 | 2012-W52-1 | 2012-Q4-85 */
474 759           break;
475             #ifndef DT_PARSE_ISO_STRICT
476 0           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 4           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 0           default:
492 0           return 0;
493             }
494              
495 759           switch (n) {
496 5           case 0: /* 2012-Q4-85 | 2012-W52-1 */
497 5           break;
498 749           case 2: /* 2012-12-24 */
499 749 50         if (p[7] != '-' || count_digits(p, 8, len) != 2)
    50          
500 0           return 0;
501 749           x = parse_number(p, 5, 2);
502 749           d = parse_number(p, 8, 2);
503 749           n = 10;
504 749           goto ymd;
505 5           case 3: /* 2012-359 */
506 5           d = parse_number(p, 5, 3);
507 5           n = 8;
508 5           goto yd;
509 0           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 0           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 5           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 0           default:
535 0           return 0;
536             }
537              
538 9           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 759           ymd:
545 759 50         if (!dt_valid_ymd(y, x, d))
546 0           return 0;
547 759           dt = dt_from_ymd(y, x, d);
548 759           goto finish;
549              
550             #ifndef DT_PARSE_ISO_STRICT
551 0           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 9           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 777           finish:
564             #ifndef DT_PARSE_ISO_YEAR0
565 777 50         if (y < 1)
566 0           return 0;
567             #endif
568 777 50         if (dtp)
569 777           *dtp = dt;
570 777           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