bug#50115: date command arithmetic involving the epoch produces "invalid date"

2022-02-05 Thread Paul Eggert
Thanks for the bug report. I installed the attached patches to Gnulib 
and to Coreutils, and the fix should be in the next Coreutils release.From aa0d1e7800903f2d75432d78aa64a0e9770e83f2 Mon Sep 17 00:00:00 2001
From: Paul Eggert 
Date: Sat, 5 Feb 2022 11:05:44 -0800
Subject: [PATCH] parse-datetime: allow calculations to yield -1

Problem reported by Jeremy Cantrell .
* lib/parse-datetime.y (parse_datetime_body): When calling mktime,
use an unmodifed and negative tm_wday or tm_yday to detect an error,
as a (time_t) -1 return value is valid on most hosts.
* tests/test-parse-datetime.c (main): Add a test for the bug.
---
 ChangeLog   |  9 +
 lib/parse-datetime.y| 22 +++---
 tests/test-parse-datetime.c |  8 
 3 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5445802ea2..18dcb3fe3f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2022-02-05  Paul Eggert  
+
+	parse-datetime: allow calculations to yield -1
+	Problem reported by Jeremy Cantrell .
+	* lib/parse-datetime.y (parse_datetime_body): When calling mktime,
+	use an unmodifed and negative tm_wday or tm_yday to detect an error,
+	as a (time_t) -1 return value is valid on most hosts.
+	* tests/test-parse-datetime.c (main): Add a test for the bug.
+
 2022-02-04  Paul Eggert  
 
 	userspec: help fix GNU ‘id’ incompatibility
diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index c40fdcef7f..9fc14c9d46 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -2076,21 +2076,20 @@ parse_datetime_body (struct timespec *result, char const *p,
   if (pc.days_seen && ! pc.dates_seen)
 {
   intmax_t dayincr;
-  if (INT_MULTIPLY_WRAPV ((pc.day_ordinal
-   - (0 < pc.day_ordinal
-  && tm.tm_wday != pc.day_number)),
-  7, )
-  || INT_ADD_WRAPV ((pc.day_number - tm.tm_wday + 7) % 7,
-dayincr, )
-  || INT_ADD_WRAPV (dayincr, tm.tm_mday, _mday))
-Start = -1;
-  else
+  tm.tm_yday = -1;
+  if (! (INT_MULTIPLY_WRAPV ((pc.day_ordinal
+  - (0 < pc.day_ordinal
+ && tm.tm_wday != pc.day_number)),
+ 7, )
+ || INT_ADD_WRAPV ((pc.day_number - tm.tm_wday + 7) % 7,
+   dayincr, )
+ || INT_ADD_WRAPV (dayincr, tm.tm_mday, _mday)))
 {
   tm.tm_isdst = -1;
   Start = mktime_z (tz, );
 }
 
-  if (Start == (time_t) -1)
+  if (tm.tm_yday < 0)
 {
   if (debugging ())
 dbg_printf (_("error: day '%s' "
@@ -2156,8 +2155,9 @@ parse_datetime_body (struct timespec *result, char const *p,
   tm.tm_min = tm0.tm_min;
   tm.tm_sec = tm0.tm_sec;
   tm.tm_isdst = tm0.tm_isdst;
+  tm.tm_wday = -1;
   Start = mktime_z (tz, );
-  if (Start == (time_t) -1)
+  if (tm.tm_wday < 0)
 {
   if (debugging ())
 dbg_printf (_("error: adding relative date resulted "
diff --git a/tests/test-parse-datetime.c b/tests/test-parse-datetime.c
index 059c810cd1..1e7955bc96 100644
--- a/tests/test-parse-datetime.c
+++ b/tests/test-parse-datetime.c
@@ -398,6 +398,14 @@ main (_GL_UNUSED int argc, char **argv)
   ASSERT (result.tv_sec == thur2 + ((i + 3) % 7 - 7) * 24 * 3600);
 }
 
+  p = "1970-12-31T23:59:59+00:00 - 1 year";  /* Bug#50115 */
+  now.tv_sec = -1;
+  now.tv_nsec = 0;
+  ASSERT (parse_datetime (, p, ));
+  LOG (p, now, result);
+  ASSERT (result.tv_sec == now.tv_sec
+  && result.tv_nsec == now.tv_nsec);
+
   p = "THURSDAY UTC+00";  /* The epoch was on Thursday.  */
   now.tv_sec = 0;
   now.tv_nsec = 0;
-- 
2.32.0

From cf6c84989968c5081c683bbef77825fc35e03c9d Mon Sep 17 00:00:00 2001
From: Paul Eggert 
Date: Sat, 5 Feb 2022 11:08:45 -0800
Subject: [PATCH 1/2] build: update gnulib submodule to latest

---
 gnulib | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gnulib b/gnulib
index ff208d546..aa0d1e780 16
--- a/gnulib
+++ b/gnulib
@@ -1 +1 @@
-Subproject commit ff208d546a26fee39a0191297c11560da74b5dee
+Subproject commit aa0d1e7800903f2d75432d78aa64a0e9770e83f2
-- 
2.32.0

From 8a3dedfef9479c53cd9016139ce00d58a6006ba2 Mon Sep 17 00:00:00 2001
From: Paul Eggert 
Date: Sat, 5 Feb 2022 13:46:44 -0800
Subject: [PATCH 2/2] date: test against bug#50115

* tests/misc/date.pl: Add test.
---
 tests/misc/date.pl | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tests/misc/date.pl b/tests/misc/date.pl
index e9de8e453..ef7080e33 100755
--- a/tests/misc/date.pl
+++ b/tests/misc/date.pl
@@ -301,6 +301,9 @@ my @Tests =
  # 

bug#50115: date command arithmetic involving the epoch produces "invalid date"

2021-08-18 Thread Jeremy Cantrell
Using `date --utc --date="..."` with a date specification that
decrements by years that should result in epoch=0
(1969-12-31T23:59:59+00:00) produces an "invalid date" error message.

The following commands should illustrate the problem:

Notice that the only difference between the starting dates is 1 second.
```sh
$ date -u -d "1970-12-31T23:59:59+00:00 - 1 year"
date: invalid date ‘1970-12-31T23:59:59+00:00 - 1 year’
$ date -u -d "1970-12-31T23:59:58+00:00 - 1 year"
Wed Dec 31 11:59:58 PM UTC 1969
```

The dates are only considered invalid if they fall on epoch=0:
```sh
$ date -u -d "1971-12-31T23:59:59+00:00 - 2 year"
date: invalid date ‘1971-12-31T23:59:59+00:00 - 2 year’
$ date -u -d "1972-12-31T23:59:59+00:00 - 3 year"
date: invalid date ‘1972-12-31T23:59:59+00:00 - 3 year’
```

Going the other direction seems to work:
```sh
$ date -u -d "1969-01-01T00:00:00+00:00 + 1 year"
Thu Jan  1 12:00:00 AM UTC 1970
```

It only seems to error when decrementing by years:
```sh
 date -u -d "1970-01-01T00:00:01+00:00 - 1 second"
Thu Jan  1 12:00:00 AM UTC 1970
```

It only seems to error when using --utc, because the following works
(my time zone is America/Chicago):
```sh
$ date -d "Wed Dec 31 06:00:00 PM CST 1970 - 1 year" +%s
0
```