Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 78 additions & 14 deletions dt_parse_iso.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Comment thread
ligurio marked this conversation as resolved.
*/
#include <stddef.h>
#include <stdlib.h>
#include "dt_core.h"
#include "dt_valid.h"

Expand Down Expand Up @@ -62,7 +63,8 @@ static const int pow_10[10] = {
};

/*
* fffffffff
* Read all digits, but convert up to 9 only.
* fffffffff[f...]
*/

static size_t
Expand All @@ -79,49 +81,87 @@ parse_fraction_digits(const unsigned char *p, size_t i, size_t len, int *fp) {
return n;
}

static void
Comment thread
ligurio marked this conversation as resolved.
handle_frac_case(int frac_case, int *fp, int *mp, int *sp)
{
double w = 60.0 * *fp / 1E9; /* .hh or .mm */
switch (frac_case) {
case 1:
*mp = (int) w;
w = 60.0 * (w - *mp); /* .hh -> .mm */
/* fallthrough */
case 2:
*sp = (int) w;
/* We prefer to truncate hour or minute fractions
* to seconds precision for simplicity:
* no rounding = no overflow to mins/hours/etc.
* If somebody want a second fraction,
* they must use explicit representation s,f.
*/
*fp = 0; /* *fp = (int) ((w - *sp) * 1E9); */
break;
default:
abort();
}
}

/*
* The representation of time format is defined in ISO 8601-1:2019, 5.3.1.
* Representations with decimal fraction is defined in 5.3.1.4.
* More than 9 fraction digits are truncated by the routine.
* hh
* hh.fffffffff[f...]
* hh,fffffffff[f...]
* hhmm
* hhmm.fffffffff[f...]
* hhmm,fffffffff[f...]
* hhmmss
* hhmmss.fffffffff
* hhmmss,fffffffff
* hhmmss.fffffffff[f...]
* hhmmss,fffffffff[f...]
*/

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;
int frac_case;

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;
frac_case = 1;
break;
case 4: /* hhmm */
h = parse_number(p, 0, 2);
m = parse_number(p, 2, 2);
goto hms;
frac_case = 2;
break;
case 6: /* hhmmss */
h = parse_number(p, 0, 2);
m = parse_number(p, 2, 2);
s = parse_number(p, 4, 2);
frac_case = 0;
break;
default:
return 0;
}

/* hhmmss.fffffffff */
/* .fffffffff[f...] */
if (n < len && (p[n] == '.' || p[n] == ',')) {
size_t r = parse_fraction_digits(p, ++n, len, &f);
++n;
size_t r = parse_fraction_digits(p, n, len, &f);
if (!r)
return 0;
n += r;

if (frac_case > 0)
handle_frac_case(frac_case, &f, &m, &s);
}

hms:
if (h > 23 || m > 59 || s > 59) {
if (!(h == 24 && m == 0 && s == 0 && f == 0))
return 0;
Expand Down Expand Up @@ -199,18 +239,26 @@ dt_parse_iso_zone_basic(const char *str, size_t len, int *op) {
}

/*
* The representation of time format is defined in ISO 8601-1:2019, 5.3.1.
* Representations with decimal fraction is defined in 5.3.1.4.
* More than 9 fraction digits are truncated by the routine.
* hh
* hh.fffffffff[f...]
* hh,fffffffff[f...]
* hh:mm
* hh:mm.fffffffff[f...]
* hh:mm,fffffffff[f...]
* hh:mm:ss
* hh:mm:ss.fffffffff
* hh:mm:ss,fffffffff
* hh:mm:ss.fffffffff[f...]
* hh:mm:ss,fffffffff[f...]
*/

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;
int frac_case;

p = (const unsigned char *)str;
if (count_digits(p, 0, len) != 2)
Expand All @@ -219,31 +267,43 @@ dt_parse_iso_time_extended(const char *str, size_t len, int *sp, int *fp) {
h = parse_number(p, 0, 2);
m = s = f = 0;
n = 2;
frac_case = 1;

if (len < 3 || p[2] != ':')
if (len < 3 || (p[2] != ':' && p[2] != ',' && p[2] != '.'))
goto hms;
else if (p[2] != ':')
goto parse_frac;

if (count_digits(p, 3, len) != 2)
return 0;

m = parse_number(p, 3, 2);
n = 5;
frac_case = 2;

if (len < 6 || p[5] != ':')
if (len < 6 || (p[5] != ':' && p[5] != ',' && p[5] != '.'))
goto hms;
else if (p[5] != ':')
goto parse_frac;

if (count_digits(p, 6, len) != 2)
return 0;

s = parse_number(p, 6, 2);
n = 8;
frac_case = 0;

/* hh:mm:ss.fffffffff */
parse_frac:
/* .fffffffff[f...] */
if (n < len && (p[n] == '.' || p[n] == ',')) {
size_t r = parse_fraction_digits(p, ++n, len, &f);
++n;
size_t r = parse_fraction_digits(p, n, len, &f);
if (!r)
return 0;
n += r;

if (frac_case > 0)
handle_frac_case(frac_case, &f, &m, &s);
}

hms:
Expand Down Expand Up @@ -665,7 +725,11 @@ dt_parse_iso_date(const char *str, size_t len, dt_t *dtp) {
/*
* Basic Extended
* T12 N/A
* T12.123456789 N/A
* T12,123456789 N/A
* T1230 T12:30
* T1230.123456789 T12:30.123456789
* T1230,123456789 T12:30,123456789
* T123045 T12:30:45
* T123045.123456789 T12:30:45.123456789
* T123045,123456789 T12:30:45,123456789
Expand Down
42 changes: 42 additions & 0 deletions t/parse_iso_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,48 @@ const struct good_t {
{ 0, 0, "T000000.0000000", 15 },
{ 0, 0, "T000000.00000000", 16 },
{ 0, 0, "T000000.000000000", 17 },
{ 0, 0, "T00.000000000", 13 },
{ 1800, 0, "T00.5", 5 },
{ 900, 0, "T00.25", 6 },
{ 450, 0, "T00.125", 7 },
{ 1199, 0, "T00.333333333", 13 },
{ 1199, 0, "T00.333333333999", 16 },
{ 3599, 0, "T00.999999999", 13 },
{ 0, 0, "T00,000000000", 13 },
{ 1800, 0, "T00,5", 5 },
{ 900, 0, "T00,25", 6 },
{ 450, 0, "T00,125", 7 },
{ 1199, 0, "T00,333333333", 13 },
{ 1199, 0, "T00,333333333999", 16 },
{ 3599, 0, "T00,999999999", 13 },
{ 0, 0, "T00:00.000000000", 16 },
{ 30, 0, "T00:00.5", 8 },
{ 15, 0, "T00:00.25", 9 },
{ 7, 0, "T00:00.125", 10 },
{ 19, 0, "T00:00.333333333", 16 },
{ 19, 0, "T00:00.333333333999", 19 },
{ 59, 0, "T00:00.999999999", 16 },
{ 0, 0, "T00:00,000000000", 16 },
{ 30, 0, "T00:00,5", 8 },
{ 15, 0, "T00:00,25", 9 },
{ 7, 0, "T00:00,125", 10 },
{ 19, 0, "T00:00,333333333", 16 },
{ 19, 0, "T00:00,333333333999", 19 },
{ 59, 0, "T00:00,999999999", 16 },
{ 0, 0, "T0000.000000000", 15 },
{ 30, 0, "T0000.5", 7 },
{ 15, 0, "T0000.25", 8 },
{ 7, 0, "T0000.125", 9 },
{ 19, 0, "T0000.333333333", 15 },
{ 19, 0, "T0000.333333333999", 18 },
{ 59, 0, "T0000.999999999", 15 },
{ 0, 0, "T0000,000000000", 15 },
{ 30, 0, "T0000,5", 7 },
{ 15, 0, "T0000,25", 8 },
{ 7, 0, "T0000,125", 9 },
{ 19, 0, "T0000,333333333", 15 },
{ 19, 0, "T0000,333333333999", 18 },
{ 59, 0, "T0000,999999999", 15 },
};

const struct bad_t {
Expand Down