The parser now accepts the basic format for combined date and time representations, which ommits the date and time separators, "-" and ":".
See bug 23767 for GNU coreutils, <https://savannah.gnu.org/bugs/?23767>. * lib/parse-datetime.y: Parse combined date and time representations in ISO 8601 basic format. (set_hhmmss_iso_8601_basic_time) New function. * tests/test-parse-datetime.c: Add new tests for combined date and time representations in ISO 8601 basic format. --- lib/parse-datetime.y | 78 +++++++++++++++++++++++++++++++++++++++++-- tests/test-parse-datetime.c | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y index 77d95b7..20bf1ac 100644 --- a/lib/parse-datetime.y +++ b/lib/parse-datetime.y @@ -282,6 +282,60 @@ set_hhmmss (parser_control *pc, long int hour, long int minutes, pc->seconds.tv_nsec = nsec; } +/* Set PC-> hour, minutes, seconds and nanoseconds members from ISO 8601 basic + time. */ +static void +set_hhmmss_iso_8601_basic_time (parser_control *pc, long int integer_part, + long int fractional_part) +{ + if (integer_part / 1000000 > 0) + { + /* not ISO 8601 time, forcing mktime error */ + pc->hour = 90; + pc->minutes = 0; + pc->seconds.tv_sec = 0; + pc->seconds.tv_nsec = 0; + } + else + { + pc->hour = integer_part / 10000; + if (pc->hour > 0) /* HHMMSS */ + { + pc->minutes = (integer_part % 10000) / 100; + pc->seconds.tv_sec = integer_part % 100; + pc->seconds.tv_nsec = fractional_part; + } + else + { + if (fractional_part != 0) + { + /* FIXME support fractional part for minutes and hours */ + pc->hour = 90; + pc->minutes = 0; + pc->seconds.tv_sec = 0; + pc->seconds.tv_nsec = 0; + } + else + { + pc->hour = integer_part / 100; + if (pc->hour > 0) /* HHMM */ + { + pc->minutes = integer_part % 100; + pc->seconds.tv_sec = 0; + pc->seconds.tv_nsec = 0; + } + else /* HH */ + { + pc->hour = integer_part; + pc->minutes = 0; + pc->seconds.tv_sec = 0; + pc->seconds.tv_nsec = 0; + } + } + } + } +} + %} /* We want a reentrant parser, even if the TZ manipulation and the calls to @@ -290,8 +344,8 @@ set_hhmmss (parser_control *pc, long int hour, long int minutes, %parse-param { parser_control *pc } %lex-param { parser_control *pc } -/* This grammar has 31 shift/reduce conflicts. */ -%expect 31 +/* This grammar has 34 shift/reduce conflicts. */ +%expect 34 %union { @@ -358,12 +412,18 @@ item: datetime: iso_8601_datetime + | iso_8601_basic_datetime ; iso_8601_datetime: iso_8601_date 'T' iso_8601_time ; +iso_8601_basic_datetime: + number 'T' iso_8601_basic_time + { pc->dates_seen--; } /* already incremented in digits_to_date_time */ + ; + time: tUNUMBER tMERIDIAN { @@ -401,6 +461,20 @@ iso_8601_time: } ; +iso_8601_basic_time: + tUNUMBER o_zone_offset + { + set_hhmmss_iso_8601_basic_time (pc, $1.value, 0); + pc->meridian = MER24; + } + | tUDECIMAL_NUMBER o_zone_offset + { + /* FIXME avoid time_t to long int cast */ + set_hhmmss_iso_8601_basic_time (pc, (long int)$1.tv_sec, $1.tv_nsec); + pc->meridian = MER24; + } + ; + o_zone_offset: /* empty */ | zone_offset diff --git a/tests/test-parse-datetime.c b/tests/test-parse-datetime.c index 7eba9ad..c620009 100644 --- a/tests/test-parse-datetime.c +++ b/tests/test-parse-datetime.c @@ -216,6 +216,67 @@ main (int argc _GL_UNUSED, char **argv) && expected.tv_nsec == result.tv_nsec); + /* ISO 8601 basic date and time of day representation, + 'T' separator, local time zone */ + p = "20110501T115518"; + expected.tv_sec = ref_time - gmtoff; + expected.tv_nsec = 0; + ASSERT (parse_datetime (&result, p, 0)); + LOG (p, expected, result); + ASSERT (expected.tv_sec == result.tv_sec + && expected.tv_nsec == result.tv_nsec); + + + /* ISO 8601 basic date and time of day representation, + 'T' separator, UTC */ + p = "20110501T115518Z"; + expected.tv_sec = ref_time; + expected.tv_nsec = 0; + ASSERT (parse_datetime (&result, p, 0)); + LOG (p, expected, result); + ASSERT (expected.tv_sec == result.tv_sec + && expected.tv_nsec == result.tv_nsec); + + + /* ISO 8601 basic date and time of day representation, + 'T' separator, w/UTC offset */ + p = "20110501T115518-0700"; + expected.tv_sec = 1304276118; + expected.tv_nsec = 0; + ASSERT (parse_datetime (&result, p, 0)); + LOG (p, expected, result); + ASSERT (expected.tv_sec == result.tv_sec + && expected.tv_nsec == result.tv_nsec); + + + /* ISO 8601 basic date and time of day representation, + 'T' separator, w/hour only UTC offset */ + p = "20110501T115518-07"; + expected.tv_sec = 1304276118; + expected.tv_nsec = 0; + ASSERT (parse_datetime (&result, p, 0)); + LOG (p, expected, result); + ASSERT (expected.tv_sec == result.tv_sec + && expected.tv_nsec == result.tv_nsec); + + + /* ISO 8601 basic date and time of day representation, + 'T' separator, w/hour only UTC offset, with ns */ + p = "20110501T115518,123456789-07"; + expected.tv_sec = 1304276118; + expected.tv_nsec = 123456789; + ASSERT (parse_datetime (&result, p, 0)); + LOG (p, expected, result); + ASSERT (expected.tv_sec == result.tv_sec + && expected.tv_nsec == result.tv_nsec); + + + /* Invalid ISO 8601 basic date and time of day representation, + too many digits for time */ + p = "20110501T11551800"; + ASSERT (!parse_datetime (&result, p, 0)); + + now.tv_sec = 4711; now.tv_nsec = 1267; p = "now"; -- 1.7.9.5