I took a look at this bug, and believe I have a patch that will resolve it.
$ ../src/date --debug -d '2024-01-01 8:00:00PM -0500' date: parsed date part: (Y-M-D) 2024-01-01 date: parsed time part: 08:00:00pm UTC-05 date: input timezone: parsed date/time string (-05) date: using specified time as starting value: '20:00:00' date: starting date/time: '(Y-M-D) 2024-01-01 20:00:00 TZ=-05' date: '(Y-M-D) 2024-01-01 20:00:00 TZ=-05' = 1704157200 epoch-seconds date: timezone: system default date: final: 1704157200.000000000 (epoch-seconds) date: final: (Y-M-D) 2024-01-02 01:00:00 (UTC) date: final: (Y-M-D) 2024-01-01 17:00:00 (UTC-08) date: output format: ‘%a %d %b %Y %T %Z’ Mon 01 Jan 2024 17:00:00 PST And I was able to run the coreutils testsuite with no tests failing: ============================================================================ Testsuite summary for GNU coreutils 9.7.174-083f8 ============================================================================ # TOTAL: 533 # PASS: 476 # SKIP: 57 # XFAIL: 0 # FAIL: 0 # XPASS: 0 # ERROR: 0 ============================================================================ Are there any other tests/changes I should consider for this? Below is the patch for the changes I made for this, including a new testcase for AM/PM with timezone. --- a/lib/parse-datetime.y +++ b/lib/parse-datetime.y @@ -592,7 +592,7 @@ debug_print_relative_time (char const *item, parser_control const *pc) %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT %token <intval> tDAY_UNIT tDAY_SHIFT -%token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN +%token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN tMERIDIAN_WITH_ZONE %token <intval> tMONTH tORDINAL tZONE %token <textintval> tSNUMBER tUNUMBER @@ -698,6 +698,27 @@ time: set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec); pc->meridian = $6; } + | tUNUMBER tMERIDIAN_WITH_ZONE tSNUMBER o_colon_minutes + { + set_hhmmss (pc, $1.value, 0, 0, 0); + pc->meridian = $2; + pc->zones_seen++; + if (! time_zone_hhmm (pc, $3, $4)) YYABORT; + } + | tUNUMBER ':' tUNUMBER tMERIDIAN_WITH_ZONE tSNUMBER o_colon_minutes + { + set_hhmmss (pc, $1.value, $3.value, 0, 0); + pc->meridian = $4; + pc->zones_seen++; + if (! time_zone_hhmm (pc, $5, $6)) YYABORT; + } + | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN_WITH_ZONE tSNUMBER o_colon_minutes + { + set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec); + pc->meridian = $6; + pc->zones_seen++; + if (! time_zone_hhmm (pc, $7, $8)) YYABORT; + } | iso_8601_time ; @@ -1527,14 +1548,19 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) *p = '\0'; tp = lookup_word (pc, buff); - if (! tp) + if (tp) { - if (debugging (pc)) - dbg_printf (_("error: unknown word '%s'\n"), buff); - return '?'; + lvalp->intval = tp->value; + if (tp->type == tMERIDIAN) + { + char const *p = pc->input; + while (*p && c_isspace (*p)) + p++; + if (*p == '-' || *p == '+') + return tMERIDIAN_WITH_ZONE; + } + return tp->type; } - lvalp->intval = tp->value; - return tp->type; } if (c != '(') diff --git a/tests/test-parse-datetime.c b/tests/test-parse-datetime.c index 546b383c55..9766ed7a13 100644 --- a/tests/test-parse-datetime.c +++ b/tests/test-parse-datetime.c @@ -335,6 +335,15 @@ main (_GL_UNUSED int argc, char **argv) ASSERT (result.tv_sec == result2.tv_sec && result.tv_nsec == result2.tv_nsec); + /* Check that timeone works with AM/PM */ + p = "2024-01-01 8PM -08:00"; + expected.tv_sec = 1704168000; + expected.tv_nsec = 0; + ASSERT (parse_datetime (&result, p, NULL)); + LOG (p, expected, result); + ASSERT (expected.tv_sec == result.tv_sec + && expected.tv_nsec == result.tv_nsec); + /* TZ out of range should cause parse_datetime failure */ now.tv_sec = SOME_TIMEPOINT + 4711;