From c34334eca3e583a689dcc4a5d4641dfa700c8f49 Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Wed, 20 Jan 2016 20:48:54 -0500
Subject: [PATCH 01/10] parse-datetime: debugging - print parsed dateetime
 parts

* lib/parse-datetime.h: add global debug variable.
* lib/parse-datetime.y: when debugging, print parsed parts to stderr.
---
 lib/parse-datetime.h |   2 +
 lib/parse-datetime.y | 132 ++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 128 insertions(+), 6 deletions(-)

diff --git a/lib/parse-datetime.h b/lib/parse-datetime.h
index c38b000..b849db3 100644
--- a/lib/parse-datetime.h
+++ b/lib/parse-datetime.h
@@ -19,4 +19,6 @@
 #include <stdbool.h>
 #include <time.h>
 
+extern bool parse_datetime_debug;
+
 bool parse_datetime (struct timespec *, char const *, struct timespec const *);
diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index 163a0ea..230d050 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -118,6 +118,10 @@ typedef time_t long_time_t;
    errors that the cast doesn't.  */
 static unsigned char to_uchar (char ch) { return ch; }
 
+/* Enable diagnostic debug output to STDERR */
+bool parse_datetime_debug = false;
+
+
 /* Lots of this code assumes time_t and time_t-like values fit into
    long_time_t.  */
 verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t)
@@ -282,6 +286,86 @@ set_hhmmss (parser_control *pc, long int hour, long int minutes,
   pc->seconds.tv_nsec = nsec;
 }
 
+/* debugging: print the current PC value */
+static void
+debug_print_pc (const char* item, const parser_control *pc)
+{
+  fprintf(stderr,"date: parsed %s part: ", item);
+
+  if (pc->dates_seen)
+    {
+      /*TODO: use pc->year.negative? */
+      fprintf(stderr,"(Y-M-D) %04ld-%02ld-%02ld ", pc->year.value, pc->month, pc->day);
+    }
+
+  if (pc->times_seen)
+    {
+      fprintf(stderr,"%02ld:%02ld:%02ld", pc->hour, pc->minutes,
+              pc->seconds.tv_sec);
+      if (pc->seconds.tv_nsec!=0)
+        fprintf(stderr,"%09ld", pc->seconds.tv_nsec);
+      if (pc->meridian==MERpm)
+        fputs("pm",stderr);
+      fputc(' ',stderr);
+    }
+
+  if (pc->days_seen)
+    {
+      fprintf(stderr,"day-ordinal=%ld ", pc->day_ordinal);
+      fprintf(stderr,"day-number=%d ", pc->day_number);
+    }
+
+  if (pc->dsts_seen)
+    fprintf(stderr,"is-dst=%d ", pc->local_isdst);
+
+  /* TODO: print local/non-local separately?? */
+  /* TODO: fix incorrect display of EST=2:08h? */
+  if (pc->zones_seen || pc->local_zones_seen)
+    fprintf(stderr,"timezone=%+d:%02dh",
+	    (int)(pc->time_zone/60),
+	    abs(pc->time_zone%60)
+	    );
+
+  if (pc->timespec_seen)
+    fprintf(stderr,"timespec=%lds", pc->seconds.tv_sec);
+
+  fputs("\n", stderr);
+}
+
+/* debugging: print the current REL value */
+static void
+debug_print_rel (const char* item, const parser_control *pc)
+{
+  fprintf(stderr,"date: parsed %s part: ", item);
+
+  if (pc->rel.year==0 && pc->rel.month==0 && pc->rel.day==0
+      && pc->rel.hour==0 && pc->rel.minutes==00 && pc->rel.seconds == 0
+      && pc->rel.ns==0)
+    {
+      /* Special case: relative time of this/today/now */
+      fputs("today/this/now\n",stderr);
+      return ;
+    }
+
+#define PRINT_REL_PART(x,name)			\
+  do { \
+    if ( (pc->rel.x) != 0 ) \
+      fprintf(stderr,"%+ld %s ", pc->rel.x, name); \
+  } while (0)
+
+  PRINT_REL_PART(year,"year(s)");
+  PRINT_REL_PART(month,"month(s)");
+  PRINT_REL_PART(day,"day(s)");
+  PRINT_REL_PART(hour,"hour(s)");
+  PRINT_REL_PART(minutes,"minutes");
+  PRINT_REL_PART(seconds,"seconds");
+  PRINT_REL_PART(ns,"nanoseconds");
+
+  fputs("\n",stderr);
+}
+
+
+
 %}
 
 /* We want a reentrant parser, even if the TZ manipulation and the calls to
@@ -340,20 +424,56 @@ items:
 
 item:
     datetime
-      { pc->times_seen++; pc->dates_seen++; }
+      {
+	pc->times_seen++; pc->dates_seen++;
+	if (parse_datetime_debug)
+	  debug_print_pc("datetime", pc);
+      }
   | time
-      { pc->times_seen++; }
+      {
+	pc->times_seen++;
+	if (parse_datetime_debug)
+	  debug_print_pc("time", pc);
+      }
   | local_zone
-      { pc->local_zones_seen++; }
+      {
+	pc->local_zones_seen++;
+	if (parse_datetime_debug)
+	  debug_print_pc("local_zone", pc);
+      }
   | zone
-      { pc->zones_seen++; }
+      {
+	pc->zones_seen++;
+	if (parse_datetime_debug)
+	  debug_print_pc("zone", pc);
+      }
   | date
-      { pc->dates_seen++; }
+      {
+	pc->dates_seen++;
+	if (parse_datetime_debug)
+	  debug_print_pc("date", pc);
+      }
   | day
-      { pc->days_seen++; }
+      {
+	pc->days_seen++;
+	if (parse_datetime_debug)
+	  debug_print_pc("day", pc);
+      }
   | rel
+      {
+	if (parse_datetime_debug)
+	  debug_print_rel("relative", pc);
+      }
   | number
+      {
+	if (parse_datetime_debug)
+	  debug_print_rel("number", pc);
+      }
   | hybrid
+      {
+	if (parse_datetime_debug)
+	  debug_print_rel("hybrid", pc);
+      }
   ;
 
 datetime:
-- 
2.4.3


From 9330f051e8a845ddefaab809f8b4797dfacedc70 Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Wed, 20 Jan 2016 20:54:22 -0500
Subject: [PATCH 02/10] parse-datetime: print logic errors

---
 lib/parse-datetime.y | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index 230d050..04a00b7 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -1531,7 +1531,11 @@ parse_datetime (struct timespec *result, char const *p,
     }
 
   if (yyparse (&pc) != 0)
-    goto fail;
+    {
+      if (parse_datetime_debug)
+	fprintf(stderr,"date: parsing failed, stopped at '%s'\n", pc.input);
+      goto fail;
+    }
 
   if (pc.timespec_seen)
     *result = pc.seconds;
@@ -1539,7 +1543,23 @@ parse_datetime (struct timespec *result, char const *p,
     {
       if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
                | (pc.local_zones_seen + pc.zones_seen)))
-        goto fail;
+	{
+	  if (parse_datetime_debug)
+	    {
+	      if (pc.times_seen > 1)
+		fputs("date: error: seen multiple time parts\n",stderr);
+	      if (pc.dates_seen > 1)
+		fputs("date: error: seen multiple date parts\n",stderr);
+	      if (pc.days_seen > 1)
+		fputs("date: error: seen multiple days parts\n",stderr);
+	      if (pc.dsts_seen > 1)
+		fputs("date: error: seen multiple daylight-saving parts\n",
+		      stderr);
+	      if ( (pc.local_zones_seen + pc.zones_seen) > 1)
+		fputs("date: error: seen multiple time-zoze parts\n", stderr);
+	    }
+	  goto fail;
+	}
 
       tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
       tm.tm_mon = pc.month - 1;
-- 
2.4.3


From e8ffc5f8bd42d7148d77d135c65c9d65d49f5dce Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Wed, 20 Jan 2016 21:06:37 -0500
Subject: [PATCH 03/10] parse-datetime.y: debug-print on invalid AM/PM hours

---
 lib/parse-datetime.y | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index 04a00b7..962effd 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -1568,7 +1568,16 @@ parse_datetime (struct timespec *result, char const *p,
         {
           tm.tm_hour = to_hour (pc.hour, pc.meridian);
           if (tm.tm_hour < 0)
-            goto fail;
+	    {
+	      if (parse_datetime_debug)
+		{
+		  fprintf(stderr, "date: error: invalid hour %ld%s\n", pc.hour,
+			  (pc.meridian==MERam)?"am":
+			  (pc.meridian==MERpm)?"pm":"");
+		}
+
+	      goto fail;
+	    }
           tm.tm_min = pc.minutes;
           tm.tm_sec = pc.seconds.tv_sec;
         }
-- 
2.4.3


From 58524c21fe4e8065b52984e5d0887e4bfd41f547 Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Wed, 20 Jan 2016 21:08:23 -0500
Subject: [PATCH 04/10] parse-datetime.y: debug-print invalid
 day-ordinal/numbers

---
 lib/parse-datetime.y | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index 962effd..d9b586c 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -1645,7 +1645,15 @@ parse_datetime (struct timespec *result, char const *p,
           tm.tm_isdst = -1;
           Start = mktime (&tm);
           if (Start == (time_t) -1)
-            goto fail;
+	    {
+	      if (parse_datetime_debug)
+		{
+		  fprintf(stderr,"date: error: invalid day specification:" \
+			  "ordinal=%ld day-of-week=%d\n",
+			  pc.day_ordinal, pc.day_number);
+		}
+	      goto fail;
+	    }
         }
 
       /* Add relative date.  */
-- 
2.4.3


From 5f710bcaa7121034156c3a591537b013173f974d Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Wed, 20 Jan 2016 21:11:08 -0500
Subject: [PATCH 05/10] parse-datetime.y: add generic debug-prints to all
 failures

---
 lib/parse-datetime.y | 57 ++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 49 insertions(+), 8 deletions(-)

diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index d9b586c..38bd186 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -1603,7 +1603,13 @@ parse_datetime (struct timespec *result, char const *p,
       if (! mktime_ok (&tm0, &tm, Start))
         {
           if (! pc.zones_seen)
-            goto fail;
+	    {
+	      if (parse_datetime_debug)
+		fputs("date: error: invalid date, possibly due to " \
+		      "dst/missing timezone\n", stderr);
+
+	      goto fail;
+	    }
           else
             {
               /* Guard against falsely reporting errors near the time_t
@@ -1627,12 +1633,22 @@ parse_datetime (struct timespec *result, char const *p,
               sprintf (tz1buf, "XXX%s%ld:%02d", &"-"[time_zone < 0],
                        abs_time_zone_hour, abs_time_zone_min);
               if (setenv ("TZ", tz1buf, 1) != 0)
-                goto fail;
+		{
+		  if (parse_datetime_debug)
+		    fprintf(stderr,"date: error: %s:%d\n", __FILE__,__LINE__);
+
+		  goto fail;
+		}
               tz_was_altered = true;
               tm = tm0;
               Start = mktime (&tm);
               if (! mktime_ok (&tm0, &tm, Start))
-                goto fail;
+		{
+		  if (parse_datetime_debug)
+		    fprintf(stderr,"date: error: %s:%d\n", __FILE__,__LINE__);
+
+		  goto fail;
+		}
             }
         }
 
@@ -1665,7 +1681,12 @@ parse_datetime (struct timespec *result, char const *p,
           if (((year < tm.tm_year) ^ (pc.rel.year < 0))
               | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
               | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
-            goto fail;
+	    {
+	      if (parse_datetime_debug)
+		  fprintf(stderr,"date: error: %s:%d\n", __FILE__,__LINE__);
+
+	      goto fail;
+	    }
           tm.tm_year = year;
           tm.tm_mon = month;
           tm.tm_mday = day;
@@ -1675,7 +1696,12 @@ parse_datetime (struct timespec *result, char const *p,
           tm.tm_isdst = tm0.tm_isdst;
           Start = mktime (&tm);
           if (Start == (time_t) -1)
-            goto fail;
+	    {
+	      if (parse_datetime_debug)
+		  fprintf(stderr,"date: error: %s:%d\n", __FILE__,__LINE__);
+
+	      goto fail;
+	    }
         }
 
       /* The only "output" of this if-block is an updated Start value,
@@ -1690,12 +1716,22 @@ parse_datetime (struct timespec *result, char const *p,
           time_t t = Start;
           struct tm const *gmt = gmtime (&t);
           if (! gmt)
-            goto fail;
+	    {
+	      if (parse_datetime_debug)
+		  fprintf(stderr,"date: error: %s:%d\n", __FILE__,__LINE__);
+
+	      goto fail;
+	    }
           delta -= tm_diff (&tm, gmt);
 #endif
           t1 = Start - delta;
           if ((Start < t1) != (delta < 0))
-            goto fail;  /* time_t overflow */
+	    {
+	      if (parse_datetime_debug)
+		  fprintf(stderr,"date: error: %s:%d\n", __FILE__,__LINE__);
+
+	      goto fail;  /* time_t overflow */
+	    }
           Start = t1;
         }
 
@@ -1727,7 +1763,12 @@ parse_datetime (struct timespec *result, char const *p,
             | ((t3 < t2) ^ (d3 < 0))
             | ((t4 < t3) ^ (d4 < 0))
             | (t5 != t4))
-          goto fail;
+	    {
+	      if (parse_datetime_debug)
+		  fprintf(stderr,"date: error: %s:%d\n", __FILE__,__LINE__);
+
+	      goto fail;
+	    }
 
         result->tv_sec = t5;
         result->tv_nsec = normalized_ns;
-- 
2.4.3


From 9b2a159cf90624ec9b45f83964986487e656f1cd Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Wed, 20 Jan 2016 21:15:52 -0500
Subject: [PATCH 06/10] parse-datetime.y: waring about unrecognized words

---
 lib/parse-datetime.y | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index 38bd186..b39c006 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -1290,7 +1290,15 @@ yylex (union YYSTYPE *lvalp, parser_control *pc)
           *p = '\0';
           tp = lookup_word (pc, buff);
           if (! tp)
-            return '?';
+	    {
+	      if (parse_datetime_debug)
+		{
+		  fputs("date: parse error: unknown word: ",stderr);
+		  fputs(buff,stderr);
+		  fputc('\n',stderr);
+		}
+	      return '?';
+	    }
           lvalp->intval = tp->value;
           return tp->type;
         }
-- 
2.4.3


From 1986722a417a0f0b41f25d8174857d140672a67c Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Wed, 20 Jan 2016 21:19:54 -0500
Subject: [PATCH 07/10] parse-datetime.y: warn about adding month/years with
 day=0

---
 lib/parse-datetime.y | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index b39c006..3c1cf12 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -1683,6 +1683,12 @@ parse_datetime (struct timespec *result, char const *p,
       /* Add relative date.  */
       if (pc.rel.year | pc.rel.month | pc.rel.day)
         {
+	  if ( parse_datetime_debug
+	       && (pc.rel.year != 0 || pc.rel.month !=0) && tm.tm_mday==1)
+	    fputs("date: warning: when using relative months/years, " \
+		  "it is recommended to specify the 15th of the months\n",
+		  stderr);
+
           int year = tm.tm_year + pc.rel.year;
           int month = tm.tm_mon + pc.rel.month;
           int day = tm.tm_mday + pc.rel.day;
-- 
2.4.3


From 2272752b8ee12d9935155f70e5ce7aecfcad906a Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Wed, 20 Jan 2016 21:23:07 -0500
Subject: [PATCH 08/10] parse-datetime.y: warn about relative days without 12pm

---
 lib/parse-datetime.y | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index 3c1cf12..6e7be09 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -1685,10 +1685,16 @@ parse_datetime (struct timespec *result, char const *p,
         {
 	  if ( parse_datetime_debug
 	       && (pc.rel.year != 0 || pc.rel.month !=0) && tm.tm_mday==1)
-	    fputs("date: warning: when using relative months/years, " \
+	    fputs("date: warning: when adding relative months/years, " \
 		  "it is recommended to specify the 15th of the months\n",
 		  stderr);
 
+	  if ( parse_datetime_debug
+	       && (pc.rel.day != 0 && tm.tm_hour==0))
+	    fputs("date: warning: when adding relative days, " \
+		  "it is recommended to specify 12:00pm\n",
+		  stderr);
+
           int year = tm.tm_year + pc.rel.year;
           int month = tm.tm_mon + pc.rel.month;
           int day = tm.tm_mday + pc.rel.day;
-- 
2.4.3


From da78c37c3198101822be672ed9c2dd2f66874677 Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Thu, 21 Jan 2016 00:23:24 -0500
Subject: [PATCH 09/10] parse-datetime.y: improve messages, show new start date

---
 lib/parse-datetime.y | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index 6e7be09..28442eb 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -321,13 +321,13 @@ debug_print_pc (const char* item, const parser_control *pc)
   /* TODO: print local/non-local separately?? */
   /* TODO: fix incorrect display of EST=2:08h? */
   if (pc->zones_seen || pc->local_zones_seen)
-    fprintf(stderr,"timezone=%+d:%02dh",
+    fprintf(stderr,"TimeZone=%+d:%02dh",
 	    (int)(pc->time_zone/60),
 	    abs(pc->time_zone%60)
 	    );
 
   if (pc->timespec_seen)
-    fprintf(stderr,"timespec=%lds", pc->seconds.tv_sec);
+    fprintf(stderr,"timespec=%ld seconds", pc->seconds.tv_sec);
 
   fputs("\n", stderr);
 }
@@ -1678,6 +1678,11 @@ parse_datetime (struct timespec *result, char const *p,
 		}
 	      goto fail;
 	    }
+          if (parse_datetime_debug)
+            fprintf(stderr,"date: new start date: %04d-%02d-%02d " \
+                           "(based on ordinal=%ld day-of-week=%d)\n",
+                           tm.tm_year+1900,tm.tm_mon+1,tm.tm_mday+1,
+                           pc.day_ordinal,pc.day_number);
         }
 
       /* Add relative date.  */
-- 
2.4.3


From d6f4ccf28edf53221e6a638a7269159e3c2dab0e Mon Sep 17 00:00:00 2001
From: Assaf Gordon <assafgordon@gmail.com>
Date: Thu, 21 Jan 2016 00:32:59 -0500
Subject: [PATCH 10/10] parse-datetime.y: improve messages

---
 lib/parse-datetime.y | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y
index 28442eb..29480d8 100644
--- a/lib/parse-datetime.y
+++ b/lib/parse-datetime.y
@@ -323,7 +323,7 @@ debug_print_pc (const char* item, const parser_control *pc)
   if (pc->zones_seen || pc->local_zones_seen)
     fprintf(stderr,"TimeZone=%+d:%02dh",
 	    (int)(pc->time_zone/60),
-	    abs(pc->time_zone%60)
+	    abs((int)pc->time_zone%60)
 	    );
 
   if (pc->timespec_seen)
@@ -1613,8 +1613,10 @@ parse_datetime (struct timespec *result, char const *p,
           if (! pc.zones_seen)
 	    {
 	      if (parse_datetime_debug)
-		fputs("date: error: invalid date, possibly due to " \
-		      "dst/missing timezone\n", stderr);
+		fprintf(stderr,"date: error: invalid date, possibly due to " \
+                               "incorrect day (%d) for this month (%d), " \
+                               "daylight-saving time, or missing timezone\n",
+                               tm0.tm_mday, tm0.tm_mon+1);
 
 	      goto fail;
 	    }
@@ -1681,7 +1683,7 @@ parse_datetime (struct timespec *result, char const *p,
           if (parse_datetime_debug)
             fprintf(stderr,"date: new start date: %04d-%02d-%02d " \
                            "(based on ordinal=%ld day-of-week=%d)\n",
-                           tm.tm_year+1900,tm.tm_mon+1,tm.tm_mday+1,
+                           tm.tm_year+1900,tm.tm_mon+1,tm.tm_mday,
                            pc.day_ordinal,pc.day_number);
         }
 
-- 
2.4.3

