File Coverage

src/dt_parse_iso.c
Criterion Covered Total %
statement 235 303 77.5
branch 97 198 48.9
condition n/a
subroutine n/a
pod n/a
total 332 501 66.2


line stmt bran cond sub pod time code
1            
2            
3            

perltidy

4            
 
5             /* * Copyright (c) 2012-2014 Christian Hansen <chansen@cpan.org> * <https://github.com/chansen/c-dt> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <stddef.h> #include "dt_core.h" #include "dt_valid.h" static size_t count_digits(const unsigned char * const p, size_t i, const size_t len) { const size_t n = i; for(; i < len; i++) { const unsigned char c = p[i] - '0'; if (c > 9) break; } return i - n; } static int parse_number(const unsigned char * const p, size_t i, const size_t len) { int v = 0; switch (len) { case 9: v += (p[i++] - '0') * 100000000; case 8: v += (p[i++] - '0') * 10000000; case 7: v += (p[i++] - '0') * 1000000; case 6: v += (p[i++] - '0') * 100000; case 5: v += (p[i++] - '0') * 10000; case 4: v += (p[i++] - '0') * 1000; case 3: v += (p[i++] - '0') * 100; case 2: v += (p[i++] - '0') * 10; case 1: v += (p[i++] - '0'); } return v; } static const int pow_10[10] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, }; /* * fffffffff */ static size_t parse_fraction_digits(const unsigned char *p, size_t i, size_t len, int *fp) { size_t n, ndigits; ndigits = n = count_digits(p, i, len); if (ndigits < 1) return 0; if (ndigits > 9) ndigits = 9; if (fp) *fp = parse_number(p, i, ndigits) * pow_10[9 - ndigits]; return n; } /* * hh * hhmm * hhmmss * hhmmss.fffffffff * hhmmss,fffffffff */ size_t dt_parse_iso_time_basic(const char *str, size_t len, int *sp, int *fp) { const unsigned char *p; int h, m, s, f; size_t n; p = (const unsigned char *)str; n = count_digits(p, 0, len); m = s = f = 0; switch (n) { case 2: /* hh */ h = parse_number(p, 0, 2); goto hms; case 4: /* hhmm */ h = parse_number(p, 0, 2); m = parse_number(p, 2, 2); goto hms; case 6: /* hhmmss */ h = parse_number(p, 0, 2); m = parse_number(p, 2, 2); s = parse_number(p, 4, 2); break; default: return 0; } /* hhmmss.fffffffff */ if (n < len && (p[n] == '.' || p[n] == ',')) { size_t r = parse_fraction_digits(p, ++n, len, &f); if (!r) return 0; n += r; } hms: if (h > 23 || m > 59 || s > 59) { if (!(h == 24 && m == 0 && s == 0 && f == 0)) return 0; } if (sp) *sp = h * 3600 + m * 60 + s; if (fp) *fp = f; return n; } /* * Z * ±hh * ±hhmm */ size_t dt_parse_iso_zone_basic(const char *str, size_t len, int *op) { const unsigned char *p; int o, h, m, sign; size_t n; if (len < 1) return 0; p = (const unsigned char *)str; switch (*p) { case 'Z': o = 0; n = 1; goto zulu; case '+': sign = 1; break; case '-': sign = -1; break; default: return 0; } if (len < 3) return 0; n = count_digits(p, 1, len); m = 0; switch (n) { case 2: /* ±hh */ h = parse_number(p, 1, 2); n = 3; break; case 4: /* ±hhmm */ h = parse_number(p, 1, 2); m = parse_number(p, 3, 2); n = 5; break; default: return 0; } if (h > 23 || m > 59) return 0; o = sign * (h * 60 + m); #ifdef DT_PARSE_ISO_STRICT if (o == 0 && sign < 0) return 0; #endif zulu: if (op) *op = o; return n; } /* * hh * hh:mm * hh:mm:ss * hh:mm:ss.fffffffff * hh:mm:ss,fffffffff */ size_t dt_parse_iso_time_extended(const char *str, size_t len, int *sp, int *fp) { const unsigned char *p; int h, m, s, f; size_t n; p = (const unsigned char *)str; if (count_digits(p, 0, len) != 2) return 0; h = parse_number(p, 0, 2); m = s = f = 0; n = 2; if (len < 3 || p[2] != ':') goto hms; if (count_digits(p, 3, len) != 2) return 0; m = parse_number(p, 3, 2); n = 5; if (len < 6 || p[5] != ':') goto hms; if (count_digits(p, 6, len) != 2) return 0; s = parse_number(p, 6, 2); n = 8; /* hh:mm:ss.fffffffff */ if (n < len && (p[n] == '.' || p[n] == ',')) { size_t r = parse_fraction_digits(p, ++n, len, &f); if (!r) return 0; n += r; } hms: if (h > 23 || m > 59 || s > 59) { if (!(h == 24 && m == 0 && s == 0 && f == 0)) return 0; } if (sp) *sp = h * 3600 + m * 60 + s; if (fp) *fp = f; return n; } /* * Z * ±hh * ±hh:mm */ size_t dt_parse_iso_zone_extended(const char *str, size_t len, int *op) { const unsigned char *p; int o, h, m, sign; size_t n; if (len < 1) return 0; p = (const unsigned char *)str; switch (*p) { case 'Z': o = 0; n = 1; goto zulu; case '+': sign = 1; break; case '-': sign = -1; break; default: return 0; } if (len < 3 || count_digits(p, 1, len) != 2) return 0; h = parse_number(p, 1, 2); m = 0; n = 3; if (len < 4 || p[3] != ':') goto hm; if (count_digits(p, 4, len) != 2) return 0; m = parse_number(p, 4, 2); n = 6; hm: if (h > 23 || m > 59) return 0; o = sign * (h * 60 + m); #ifdef DT_PARSE_ISO_STRICT if (o == 0 && sign < 0) return 0; #endif zulu: if (op) *op = o; return n; } /* * z * Z * GMT * GMT±h * GMT±hhmm * GMT±h:mm * GMT±hh:mm * UTC * UTC±h * UTC±hhmm * UTC±h:mm * UTC±hh:mm * ±h * ±hh * ±hhmm * ±h:mm * ±hh:mm */ size_t dt_parse_iso_zone_lenient(const char *str, size_t len, int *op) { const unsigned char *p; int o, h, m, sign; size_t n; if (len < 1) return 0; p = (const unsigned char *)str; switch (*p) { case 'z': case 'Z': o = 0; n = 1; goto zulu; case 'G': if (len < 3 || p[1] != 'M' || p[2] != 'T') return 0; if (len > 3 && (p[3] == '+' || p[3] == '-')) { if (!(n = dt_parse_iso_zone_lenient(str + 3, len - 3, op))) return 0; return n + 3; } o = 0; n = 3; goto zulu; case 'U': if (len < 3 || p[1] != 'T' || p[2] != 'C') return 0; if (len > 3 && (p[3] == '+' || p[3] == '-')) { if (!(n = dt_parse_iso_zone_lenient(str + 3, len - 3, op))) return 0; return n + 3; } o = 0; n = 3; goto zulu; case '+': sign = 1; break; case '-': sign = -1; break; default: return 0; } if (len < 2) return 0; n = count_digits(p, 1, len); m = 0; switch (n) { case 1: /* ±h */ h = parse_number(p, 1, 1); n = 2; break; case 2: /* ±hh */ h = parse_number(p, 1, 2); n = 3; break; case 4: /* ±hhmm */ h = parse_number(p, 1, 2); m = parse_number(p, 3, 2); n = 5; goto hm; default: return 0; } if (len < n + 1 || p[n] != ':') goto hm; if (count_digits(p, ++n, len) != 2) return 0; m = parse_number(p, n, 2); n += 2; hm: if (h > 23 || m > 59) return 0; o = sign * (h * 60 + m); zulu: if (op) *op = o; return n; } /* * Basic Extended * 20121224 2012-12-24 Calendar date (ISO 8601) * 2012359 2012-359 Ordinal date (ISO 8601) * 2012W521 2012-W52-1 Week date (ISO 8601) * 2012Q485 2012-Q4-85 Quarter date */ size_t dt_parse_iso_date(const char *str, size_t len, dt_t *dtp) { const unsigned char *p; int y, x, d; size_t n; dt_t dt; p = (const unsigned char *)str; n = count_digits(p, 0, len); switch (n) { case 4: /* 2012 (year) */ y = parse_number(p, 0, 4); break; case 7: /* 2012359 (basic ordinal date) */ y = parse_number(p, 0, 4); d = parse_number(p, 4, 3); goto yd; case 8: /* 20121224 (basic calendar date) */ y = parse_number(p, 0, 4); x = parse_number(p, 4, 2); d = parse_number(p, 6, 2); goto ymd; default: return 0; } if (len < 8) return 0; n = count_digits(p, 5, len); switch (p[4]) { case '-': /* 2012-359 | 2012-12-24 | 2012-W52-1 | 2012-Q4-85 */ break; #ifndef DT_PARSE_ISO_STRICT case 'Q': /* 2012Q485 */ if (n != 3) return 0; x = parse_number(p, 5, 1); d = parse_number(p, 6, 2); n = 8; goto yqd; #endif case 'W': /* 2012W521 */ if (n != 3) return 0; x = parse_number(p, 5, 2); d = parse_number(p, 7, 1); n = 8; goto ywd; default: return 0; } switch (n) { case 0: /* 2012-Q4-85 | 2012-W52-1 */ break; case 2: /* 2012-12-24 */ if (p[7] != '-' || count_digits(p, 8, len) != 2) return 0; x = parse_number(p, 5, 2); d = parse_number(p, 8, 2); n = 10; goto ymd; case 3: /* 2012-359 */ d = parse_number(p, 5, 3); n = 8; goto yd; default: return 0; } if (len < 10) return 0; n = count_digits(p, 6, len); switch (p[5]) { #ifndef DT_PARSE_ISO_STRICT case 'Q': /* 2012-Q4-85 */ if (n != 1 || p[7] != '-' || count_digits(p, 8, len) != 2) return 0; x = parse_number(p, 6, 1); d = parse_number(p, 8, 2); n = 10; goto yqd; #endif case 'W': /* 2012-W52-1 */ if (n != 2 || p[8] != '-' || count_digits(p, 9, len) != 1) return 0; x = parse_number(p, 6, 2); d = parse_number(p, 9, 1); n = 10; goto ywd; default: return 0; } yd: if (!dt_valid_yd(y, d)) return 0; dt = dt_from_yd(y, d); goto finish; ymd: if (!dt_valid_ymd(y, x, d)) return 0; dt = dt_from_ymd(y, x, d); goto finish; #ifndef DT_PARSE_ISO_STRICT yqd: if (!dt_valid_yqd(y, x, d)) return 0; dt = dt_from_yqd(y, x, d); goto finish; #endif ywd: if (!dt_valid_ywd(y, x, d)) return 0; dt = dt_from_ywd(y, x, d); finish: #ifndef DT_PARSE_ISO_YEAR0 if (y < 1) return 0; #endif if (dtp) *dtp = dt; return n; } /* * Basic Extended * T12 N/A * T1230 T12:30 * T123045 T12:30:45 * T123045.123456789 T12:30:45.123456789 * T123045,123456789 T12:30:45,123456789 * * The time designator [T] may be omitted. */ size_t dt_parse_iso_time(const char *str, size_t len, int *sod, int *nsec) { size_t n, r; if (len < 2) return 0; if (str[0] == 'T') r = 1, ++str, --len; else r = 0; if (len < 2 || str[2] == ':') n = dt_parse_iso_time_extended(str, len, sod, nsec); else n = dt_parse_iso_time_basic(str, len, sod, nsec); if (!n) return 0; return r + n; } /* * Basic Extended * Z N/A * ±hh N/A * ±hhmm ±hh:mm */ size_t dt_parse_iso_zone(const char *str, size_t len, int *offset) { if (len < 3 || str[3] == ':') return dt_parse_iso_zone_extended(str, len, offset); else return dt_parse_iso_zone_basic(str, len, offset); }