GNU bug report logs - #18328
date: '8pm -0500' is invalid (am/pm problem)

Previous Next

Package: coreutils;

Reported by: 積丹尼 Dan Jacobson <jidanni <at> jidanni.org>

Date: Mon, 25 Aug 2014 16:02:01 UTC

Severity: normal

Tags: confirmed

To reply to this bug, email your comments to 18328 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-coreutils <at> gnu.org:
bug#18328; Package coreutils. (Mon, 25 Aug 2014 16:02:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to 積丹尼 Dan Jacobson <jidanni <at> jidanni.org>:
New bug report received and forwarded. Copy sent to bug-coreutils <at> gnu.org. (Mon, 25 Aug 2014 16:02:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: 積丹尼 Dan Jacobson <jidanni <at> jidanni.org>
To: bug-coreutils <at> gnu.org
Subject: can't say date -d '8pm -0500' though other combos work
Date: Tue, 26 Aug 2014 00:01:16 +0800
$ date -d '8pm -0500'
date: invalid date ‘8pm -0500’ <--why can't this combo work?
$ date -d '20:00 -0500'
二  8月 26 09:00:00 CST 2014
$ date -d 'sun 8pm'
日  8月 31 20:00:00 CST 2014
$ date -d '8pm'
一  8月 25 20:00:00 CST 2014




Information forwarded to bug-coreutils <at> gnu.org:
bug#18328; Package coreutils. (Sat, 20 Oct 2018 04:26:02 GMT) Full text and rfc822 format available.

Message #8 received at 18328 <at> debbugs.gnu.org (full text, mbox):

From: Assaf Gordon <assafgordon <at> gmail.com>
To: 積丹尼 Dan Jacobson <jidanni <at> jidanni.org>,
 18328 <at> debbugs.gnu.org
Subject: Re: bug#18328: can't say date -d '8pm -0500' though other combos work
Date: Fri, 19 Oct 2018 22:25:00 -0600
tags 18328 confirmed
retitle 18328 date: '8pm -0500' is invalid (am/pm problem)
stop

(triaging old bugs)

Hello,

On 25/08/14 10:01 AM, 積丹尼 Dan Jacobson wrote:
> $ date -d '8pm -0500'
> date: invalid date ‘8pm -0500’ <--why can't this combo work?

This is indeed a bug (specifically in gnulib's date parsing module,
but easier to track here).

It seems the existence of the "am/pm" string causes the parser
to take a slightly different rule, then reject additional relative
values, unless they have a unit, e.g.:

  $ date --debug -d '8pm +5 days'
  date: parsed time part: 08:00:00pm
  date: parsed relative part: +5 day(s)
  [...]

Contrast it with a different (and confusing) rules when there is
no "am/pm", the relative number is always taken as the time zone, e.g.:

  $ date --debug -d '8:00 +5 days'
  date: parsed time part: 08:00:00 UTC+05
  date: parsed relative part: +1 day(s)
  date: input timezone: parsed date/time string (+05)
  [...]

(from https://bugs.gnu.org/17161#31 )

I hope to get to this bug soon.

-assaf




Added tag(s) confirmed. Request was from Assaf Gordon <assafgordon <at> gmail.com> to control <at> debbugs.gnu.org. (Sat, 20 Oct 2018 04:26:02 GMT) Full text and rfc822 format available.

Changed bug title to 'date: '8pm -0500' is invalid (am/pm problem)' from 'can't say date -d '8pm -0500' though other combos work' Request was from Assaf Gordon <assafgordon <at> gmail.com> to control <at> debbugs.gnu.org. (Sat, 20 Oct 2018 04:26:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-coreutils <at> gnu.org:
bug#18328; Package coreutils. (Sat, 20 Oct 2018 06:31:02 GMT) Full text and rfc822 format available.

Message #15 received at 18328 <at> debbugs.gnu.org (full text, mbox):

From: 積丹尼 Dan Jacobson <jidanni <at> jidanni.org>
To: Assaf Gordon <assafgordon <at> gmail.com>
Cc: 18328 <at> debbugs.gnu.org
Subject: Re: bug#18328: can't say date -d '8pm -0500' though other combos work
Date: Sat, 20 Oct 2018 14:30:25 +0800
AG> I hope to get to this bug soon.

Good.




Information forwarded to bug-coreutils <at> gnu.org:
bug#18328; Package coreutils. (Tue, 29 Jul 2025 05:16:03 GMT) Full text and rfc822 format available.

Message #18 received at 18328 <at> debbugs.gnu.org (full text, mbox):

From: Jeffery Palm <palmje <at> gmail.com>
To: 18328 <at> debbugs.gnu.org
Subject: Re: bug#18328: can't say date -d '8pm -0500' though other combos work
Date: Mon, 28 Jul 2025 22:02:30 -0700
[Message part 1 (text/plain, inline)]
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;
[Message part 2 (text/html, inline)]

Information forwarded to bug-coreutils <at> gnu.org:
bug#18328; Package coreutils. (Tue, 29 Jul 2025 11:23:02 GMT) Full text and rfc822 format available.

Message #21 received at 18328 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Jeffery Palm <palmje <at> gmail.com>, 18328 <at> debbugs.gnu.org
Cc: Geoff Kuenning <geoff <at> cs.hmc.edu>
Subject: Re: bug#18328: can't say date -d '8pm -0500' though other combos work
Date: Tue, 29 Jul 2025 12:22:32 +0100
On 29/07/2025 06:02, Jeffery Palm wrote:
> 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;

Better to use a non shadowing name here ^

> +                  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;
Thanks for looking at this.
This changes relative handling unfortunately:

  $ src/date --debug -d '2024-01-01 8:00:00PM -5 days'
  date: parsed date part: (Y-M-D) 2024-01-01
  date: parsed time part: 08:00:00pm UTC-05
  date: parsed relative part: +1 day(s)
  ...
  Wed 03 Jan 2024 01:00:00 GMT

  $ date --debug -d '2024-01-01 8:00:00PM -5 days'
  date: parsed date part: (Y-M-D) 2024-01-01
  date: parsed time part: 08:00:00pm
  date: parsed relative part: -5 day(s)
  ...
  Wed 27 Dec 2023 20:00:00 GMT

Now there is an existing ambiguity here,
where the AM/PM induces the relative interpretation:

  $ date --debug -d '2024-01-01 8:00:00PM -5 days'
  date: parsed date part: (Y-M-D) 2024-01-01
  date: parsed time part: 08:00:00pm
  date: parsed relative part: -5 day(s)

  $ date --debug -d '2024-01-01 8:00:00 -5 days'
  date: parsed date part: (Y-M-D) 2024-01-01
  date: parsed time part: 08:00:00 UTC-05
  date: parsed relative part: +1 day(s)

BTW https://bugs.gnu.org/79078 was a recent bug report
along the same lines of the relative part being unexpectedly
considered as a timezone offset

Now I agree we're already inconsistent in this regard, but I'm sure
folks are relying on the AM/PM inducing a relative interpretation.

If we were trying to make all this more consistent, IMHO
we should change things so that we always interpret +|-<int> <days|minutes|...>
as a relative adjustment, whereas your change does the opposite for the AM/PM case.

cheers,
Padraig




This bug report was last modified 44 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.