changeset 11535:f16395d91053

getdate: correctly interpret "next monday" when run on a Monday * lib/getdate.y (get_date): Correct the calculation of tm_mday so that e.g., "next tues" (when run on a tuesday) results in a date that is one week in the future, and not today's date. I.e., add a week when the wday is the same as the current one. Reported by Tom Broadhurst in http://savannah.gnu.org/bugs/?25406, and earlier by Martin Bernreuther and Jan Minář. * tests/test-getdate.c (main): Check that "next DAY" is always in the future and that "last DAY" is always in the past.
author Giuseppe Scrivano <gscrivano@gnu.org>
date Fri, 01 May 2009 09:23:20 +0200
parents 9f608fba8b1c
children 30ee15176b4d
files ChangeLog lib/getdate.y tests/test-getdate.c
diffstat 3 files changed, 72 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2009-05-03  Giuseppe Scrivano  <gscrivano@gnu.org>
+
+	getdate: correctly interpret "next monday" when run on a Monday
+	* lib/getdate.y (get_date): Correct the calculation of tm_mday so
+	that e.g., "next tues" (when run on a tuesday) results in a date
+	that is one week in the future, and not today's date.
+	I.e., add a week when the wday is the same as the current one.
+	Reported by Tom Broadhurst in http://savannah.gnu.org/bugs/?25406,
+	and earlier by Martin Bernreuther and Jan Minář.
+	* tests/test-getdate.c (main): Check that "next DAY" is always in
+	the future and that "last DAY" is always in the past.
+
 2009-05-02  Jim Meyering  <meyering@redhat.com>
 
 	build: ensure that a release build fails when a submodule is unclean
--- a/lib/getdate.y
+++ b/lib/getdate.y
@@ -1,7 +1,7 @@
 %{
 /* Parse a string into an internal time stamp.
 
-   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
    Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -404,12 +404,12 @@
 day:
     tDAY
       {
-	pc->day_ordinal = 1;
+	pc->day_ordinal = 0;
 	pc->day_number = $1;
       }
   | tDAY ','
       {
-	pc->day_ordinal = 1;
+	pc->day_ordinal = 0;
 	pc->day_number = $1;
       }
   | tORDINAL tDAY
@@ -1435,7 +1435,9 @@
       if (pc.days_seen && ! pc.dates_seen)
 	{
 	  tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
-			 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
+			 + 7 * (pc.day_ordinal
+				- (0 < pc.day_ordinal
+				   && tm.tm_wday != pc.day_number)));
 	  tm.tm_isdst = -1;
 	  Start = mktime (&tm);
 	  if (Start == (time_t) -1)
--- a/tests/test-getdate.c
+++ b/tests/test-getdate.c
@@ -48,6 +48,22 @@
 #define LOG(str, now, res) (void) 0
 #endif
 
+static const char* const day_table[] =
+{
+  "SUNDAY",
+  "MONDAY",
+  "TUESDAY",
+  "TUES",
+  "WEDNESDAY",
+  "WEDNES",
+  "THURSDAY",
+  "THUR",
+  "THURS",
+  "FRIDAY",
+  "SATURDAY",
+  NULL
+};
+
 int
 main (int argc, char **argv)
 {
@@ -55,6 +71,7 @@
   struct timespec result2;
   struct timespec now;
   const char *p;
+  int i;
 
   set_program_name (argv[0]);
 
@@ -211,5 +228,42 @@
   ASSERT (result.tv_sec == result2.tv_sec
 	  && result.tv_nsec == result2.tv_nsec);
 
+  /* Check that every 'last/next DAY' is in the past/future.  */
+  for (i = 0; day_table[i]; i++)
+    {
+      char tmp[32];
+      sprintf (tmp, "NEXT %s", day_table[i]);
+      now.tv_sec = 4711;
+      now.tv_nsec = 1267;
+      ASSERT (get_date (&result, tmp, &now));
+      LOG (tmp, now, result);
+      ASSERT (result.tv_sec > now.tv_sec
+              && result.tv_nsec == 0);
+
+      sprintf (tmp, "LAST %s", day_table[i]);
+      now.tv_sec = 4711;
+      now.tv_nsec = 1267;
+      ASSERT (get_date (&result, tmp, &now));
+      LOG (tmp, now, result);
+      ASSERT (result.tv_sec < now.tv_sec
+              && result.tv_nsec == 0);
+    }
+
+  p = "THURSDAY UTC+00";  /* The epoch was on Thursday.  */
+  now.tv_sec = 0;
+  now.tv_nsec = 0;
+  ASSERT (get_date (&result, p, &now));
+  LOG (p, now, result);
+  ASSERT (result.tv_sec == now.tv_sec
+	  && result.tv_nsec == now.tv_nsec);
+
+  p = "FRIDAY UTC+00";
+  now.tv_sec = 0;
+  now.tv_nsec = 0;
+  ASSERT (get_date (&result, p, &now));
+  LOG (p, now, result);
+  ASSERT (result.tv_sec >= now.tv_sec
+	  && result.tv_nsec == now.tv_nsec);
+
   return 0;
 }