Hello,
while playing a bit with date formats, I found several issues (still
present in GIT head). Current date on my machine is Fri Jun 27 11:58:47
CEST 2008, used in all examples.

1) "Strange" results of signed relative time offset
[EMAIL PROTECTED] ~]$ date -d "11:40 + 60 minutes"
Fri Jun 27 12:41:00 CEST 2008
[EMAIL PROTECTED] ~]$ date -d "11:40 + 61 minutes"
Fri Jun 27 12:40:00 CEST 2008
[EMAIL PROTECTED] ~]$ date -d "11:40 + 62 minutes"
Fri Jun 27 12:39:00 CEST 2008

2) TZ shift ignored when relative time offset by days/months/years done
(doesn't matter if signed or unsigned)
[EMAIL PROTECTED] ~]$ date -d "11:40 UTC+0400 +24 hours"
Sat Jun 28 09:40:00 CEST 2008
[EMAIL PROTECTED] ~]$ date -d "11:40 UTC+0400 +1 day"
Sat Jun 28 11:40:00 CEST 2008

3) numeric TZ is not limited by usual TZ limits UTC+14 and UTC-12
(timezone used in example is UTC+5000000:00)
[EMAIL PROTECTED] ~]$ date -d "11:40 +500000000" 
Fri Jul  2 07:33:04 CEST 1982

4) Date with dayshift could be quiet wrong
[EMAIL PROTECTED] ~]$ date -d "11:40 UTC+0200 yesterday" 
Mon Dec 10 11:40:00 CET 2007

5) Multiplied dayshift is allowed as valid date 
[EMAIL PROTECTED] src]$ date -d "11:40 40 yesterday"
Sun May 18 11:40:00 CEST 2008

All of those issues are fixed by the getdate.y patch (and additional
three test cases for date added in the second patch). Increase of
potential shift/reduce conflicts in grammar is from 20 to 36, usually
caused by some splits (tDAY_UNIT split to tDAY_UNIT, tWEEK_UNIT and
tDAY_SHIFT). Some code duplicities in parser were replaced by functions.
Input from example #3 and #5 now considered as invalid, others handled
correctly by new grammar. This grammar past coreutils tests/misc/date
without troubles. If you know about any situation where this new grammar
will output invalid values, please let me know and I will try to fix it.
TIA.

Greetings,
         Ondrej Vasik
From 1a6311778c0203b116259914b321196d53ccde67 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Ond=C5=99ej=20Va=C5=A1=C3=ADk?= <[EMAIL PROTECTED]>
Date: Fri, 27 Jun 2008 12:31:10 +0200
Subject: [PATCH] * tests/misc/date: add tests for new fixes of date format


Signed-off-by: Ondřej Vašík <[EMAIL PROTECTED]>
---
 tests/misc/date |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/tests/misc/date b/tests/misc/date
index fb700b5..98651a3 100755
--- a/tests/misc/date
+++ b/tests/misc/date
@@ -118,6 +118,11 @@ my @Tests =
      ['rel-1day',  "-d '20050101  1 day'  +%F", {OUT=>"2005-01-02"}],
      # ...but up to coreutils-6.9, this was rejected due to the "+".
      ['rel-plus1', "-d '20050101 +1 day'  +%F", {OUT=>"2005-01-02"}],
+     #...and following three were failing up to 6-12
+     ['rel-plus60m', "-d '11:40 + 60 minute' +%T", {OUT=>"12:40:00"}],
+     ['rel-plus6d', "-d '20070101 12:40 +6 day' $fmt", {OUT=>"2007-01-07 12:40:00"}],
+     ['rel-plusTZ6d', "-d '20070101 12:40 UTC+4 +6 day' $fmt", {OUT=>"2007-01-07 08:40:00"}],
+
 
      ['next-s', "-d '$d1 next second' '+%Y-%m-%d %T'", {OUT=>"$d0 $ts"}],
      ['next-m', "-d '$d1 next minute' '+%Y-%m-%d %T'", {OUT=>"$d0 $tm"}],
-- 
1.5.2.2

From 55d067b5172b233c63b364cad8efc171910eddee Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Ond=C5=99ej=20Va=C5=A1=C3=ADk?= <[EMAIL PROTECTED]>
Date: Fri, 27 Jun 2008 12:34:08 +0200
Subject: [PATCH] * lib/getdate.y: Several fixes for date grammar, no longer replace time zone
  after relative day/month/year offset

Signed-off-by: Ondřej Vašík <[EMAIL PROTECTED]>
---
 lib/getdate.y |  242 +++++++++++++++++++++++++++++++++------------------------
 1 files changed, 140 insertions(+), 102 deletions(-)

diff --git a/lib/getdate.y b/lib/getdate.y
index 1deec51..f0773e9 100644
--- a/lib/getdate.y
+++ b/lib/getdate.y
@@ -1,8 +1,8 @@
 %{
 /* Parse a string into an internal time stamp.
 
-   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software
-   Foundation, Inc.
+   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008 
+   Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -205,7 +205,7 @@ typedef struct
 union YYSTYPE;
 static int yylex (union YYSTYPE *, parser_control *);
 static int yyerror (parser_control const *, char const *);
-static long int time_zone_hhmm (textint, long int);
+static long int time_zone_hhmm (parser_control *,textint, long int);
 
 /* Extract into *PC any date and time info from a string of digits
    of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
@@ -246,6 +246,28 @@ digits_to_date_time (parser_control *pc, textint text_int)
     }
 }
 
+static void extract_hhmmss (parser_control *pc, long int ho, long int mi, long int sec, long int nsec)
+{
+	pc->hour = ho;
+	pc->minutes = mi;
+	pc->seconds.tv_sec = sec;
+	pc->seconds.tv_nsec = nsec;
+}
+
+/* Extract relative time multiplied by factor */
+static void
+extract_relative_time (parser_control *pc, relative_time rel, int factor)
+{
+	pc->rel.ns += factor*rel.ns;
+	pc->rel.seconds += factor*rel.seconds;
+	pc->rel.minutes += factor*rel.minutes;
+	pc->rel.hour += factor*rel.hour;
+	pc->rel.day += factor*rel.day;
+	pc->rel.month += factor*rel.month;
+	pc->rel.year += factor*rel.year;
+	pc->rels_seen = true;
+} 
+
 %}
 
 /* We want a reentrant parser, even if the TZ manipulation and the calls to
@@ -254,8 +276,8 @@ digits_to_date_time (parser_control *pc, textint text_int)
 %parse-param { parser_control *pc }
 %lex-param { parser_control *pc }
 
-/* This grammar has 20 shift/reduce conflicts. */
-%expect 20
+/* This grammar has 36 shift/reduce conflicts. */
+%expect 36
 
 %union
 {
@@ -267,8 +289,8 @@ digits_to_date_time (parser_control *pc, textint text_int)
 
 %token tAGO tDST
 
-%token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
-%token <intval> tDAY_UNIT
+%token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT tDAY_UNIT
+%token <intval> tDAY_SHIFT tWEEK_UNIT
 
 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
 %token <intval> tMONTH tORDINAL tZONE
@@ -313,7 +335,6 @@ item:
   | day
       { pc->days_seen++; }
   | rel
-      { pc->rels_seen = true; }
   | number
   | hybrid
   ;
@@ -321,47 +342,70 @@ item:
 time:
     tUNUMBER tMERIDIAN
       {
-	pc->hour = $1.value;
-	pc->minutes = 0;
-	pc->seconds.tv_sec = 0;
-	pc->seconds.tv_nsec = 0;
+	extract_hhmmss (pc, $1.value, 0, 0, 0);
 	pc->meridian = $2;
       }
   | tUNUMBER ':' tUNUMBER o_merid
       {
-	pc->hour = $1.value;
-	pc->minutes = $3.value;
-	pc->seconds.tv_sec = 0;
-	pc->seconds.tv_nsec = 0;
+	extract_hhmmss (pc, $1.value, $3.value, 0, 0);
 	pc->meridian = $4;
       }
   | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
       {
-	pc->hour = $1.value;
-	pc->minutes = $3.value;
-	pc->seconds.tv_sec = 0;
-	pc->seconds.tv_nsec = 0;
+	extract_hhmmss (pc, $1.value, $3.value, 0, 0);
 	pc->meridian = MER24;
 	pc->zones_seen++;
-	pc->time_zone = time_zone_hhmm ($4, $5);
+	pc->time_zone = time_zone_hhmm (pc, $4, $5);
       }
   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
       {
-	pc->hour = $1.value;
-	pc->minutes = $3.value;
-	pc->seconds = $5;
+	extract_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
 	pc->meridian = $6;
       }
   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
       {
-	pc->hour = $1.value;
-	pc->minutes = $3.value;
-	pc->seconds = $5;
+	extract_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
 	pc->meridian = MER24;
 	pc->zones_seen++;
-	pc->time_zone = time_zone_hhmm ($6, $7);
+	pc->time_zone = time_zone_hhmm (pc, $6, $7);
       }
-  ;
+  | tUNUMBER ':' tUNUMBER relunit_snumber
+      {
+	extract_hhmmss (pc, $1.value, $3.value, 0, 0);
+	pc->meridian = MER24;
+	extract_relative_time (pc, $4, 1); 
+      }
+  | tUNUMBER ':' tUNUMBER ':' unsigned_seconds relunit_snumber
+      {
+	extract_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
+	pc->meridian = MER24;
+	extract_relative_time (pc, $6, 1); 
+      } 
+  | tUNUMBER ':' tUNUMBER tSNUMBER
+      {
+	extract_hhmmss (pc, $1.value, $3.value, 0, 0);
+	pc->meridian = MER24;
+	pc->zones_seen++;
+	pc->time_zone = time_zone_hhmm (pc, $4, -1);
+      }
+  | tUNUMBER ':' tUNUMBER ':' unsigned_seconds
+      {
+	extract_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
+	pc->meridian = MER24;
+      }
+  | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER 
+      {
+	extract_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
+	pc->meridian = MER24;
+	pc->zones_seen++;
+	pc->time_zone = time_zone_hhmm (pc, $6, -1);
+      }
+  | tUNUMBER ':' tUNUMBER
+      {
+	extract_hhmmss (pc, $1.value, $3.value, 0, 0);
+	pc->meridian = MER24;
+      }
+   ;
 
 local_zone:
     tLOCAL_ZONE
@@ -381,16 +425,12 @@ zone:
       { pc->time_zone = $1; }
   | tZONE relunit_snumber
       { pc->time_zone = $1;
-	pc->rel.ns += $2.ns;
-	pc->rel.seconds += $2.seconds;
-	pc->rel.minutes += $2.minutes;
-	pc->rel.hour += $2.hour;
-	pc->rel.day += $2.day;
-	pc->rel.month += $2.month;
-	pc->rel.year += $2.year;
-        pc->rels_seen = true; }
+        extract_relative_time (pc, $2, 1);
+      }
   | tZONE tSNUMBER o_colon_minutes
-      { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
+      { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
+  | tZONE tSNUMBER
+      { pc->time_zone = $1 + time_zone_hhmm (pc, $2, -1); }
   | tDAYZONE
       { pc->time_zone = $1 + 60; }
   | tZONE tDST
@@ -495,25 +535,9 @@ date:
 
 rel:
     relunit tAGO
-      {
-	pc->rel.ns -= $1.ns;
-	pc->rel.seconds -= $1.seconds;
-	pc->rel.minutes -= $1.minutes;
-	pc->rel.hour -= $1.hour;
-	pc->rel.day -= $1.day;
-	pc->rel.month -= $1.month;
-	pc->rel.year -= $1.year;
-      }
+      { extract_relative_time (pc , $1, -1); }
   | relunit
-      {
-	pc->rel.ns += $1.ns;
-	pc->rel.seconds += $1.seconds;
-	pc->rel.minutes += $1.minutes;
-	pc->rel.hour += $1.hour;
-	pc->rel.day += $1.day;
-	pc->rel.month += $1.month;
-	pc->rel.year += $1.year;
-      }
+      { extract_relative_time (pc , $1, 1); }
   ;
 
 relunit:
@@ -530,10 +554,18 @@ relunit:
   | tMONTH_UNIT
       { $$ = RELATIVE_TIME_0; $$.month = 1; }
   | tORDINAL tDAY_UNIT
-      { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
+      { $$ = RELATIVE_TIME_0; $$.day = $1; }
   | tUNUMBER tDAY_UNIT
-      { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
+      { $$ = RELATIVE_TIME_0; $$.day = $1.value; }
   | tDAY_UNIT
+      { $$ = RELATIVE_TIME_0; $$.day = 1; }
+  | tORDINAL tWEEK_UNIT
+      { $$ = RELATIVE_TIME_0; $$.day = $1 * 7 * $2; }
+  | tUNUMBER tWEEK_UNIT
+      { $$ = RELATIVE_TIME_0; $$.day = $1.value * 7 * $2; }
+  | tWEEK_UNIT
+      { $$ = RELATIVE_TIME_0; $$.day = $1 * 7; }
+  | tDAY_SHIFT
       { $$ = RELATIVE_TIME_0; $$.day = $1; }
   | tORDINAL tHOUR_UNIT
       { $$ = RELATIVE_TIME_0; $$.hour = $1; }
@@ -566,7 +598,9 @@ relunit_snumber:
   | tSNUMBER tMONTH_UNIT
       { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
   | tSNUMBER tDAY_UNIT
-      { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
+      { $$ = RELATIVE_TIME_0; $$.day = $1.value; }
+  | tSNUMBER tWEEK_UNIT
+      { $$ = RELATIVE_TIME_0; $$.day = 7* $1.value * $2; }
   | tSNUMBER tHOUR_UNIT
       { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
   | tSNUMBER tMINUTE_UNIT
@@ -600,28 +634,17 @@ hybrid:
 	/* Hybrid all-digit and relative offset, so that we accept e.g.,
 	   "YYYYMMDD +N days" as well as "YYYYMMDD N days".  */
 	digits_to_date_time (pc, $1);
-	pc->rel.ns += $2.ns;
-	pc->rel.seconds += $2.seconds;
-	pc->rel.minutes += $2.minutes;
-	pc->rel.hour += $2.hour;
-	pc->rel.day += $2.day;
-	pc->rel.month += $2.month;
-	pc->rel.year += $2.year;
-	pc->rels_seen = true;
-      }
+	extract_relative_time (pc, $2, 1);
+	    }
   ;
 
 o_colon_minutes:
-    /* empty */
-      { $$ = -1; }
-  | ':' tUNUMBER
+  ':' tUNUMBER
       { $$ = $2.value; }
   ;
 
 o_merid:
-    /* empty */
-      { $$ = MER24; }
-  | tMERIDIAN
+  tMERIDIAN
       { $$ = $1; }
   ;
 
@@ -674,8 +697,8 @@ static table const time_units_table[] =
 {
   { "YEAR",	tYEAR_UNIT,	 1 },
   { "MONTH",	tMONTH_UNIT,	 1 },
-  { "FORTNIGHT",tDAY_UNIT,	14 },
-  { "WEEK",	tDAY_UNIT,	 7 },
+  { "FORTNIGHT",tWEEK_UNIT,	 2 },
+  { "WEEK",	tWEEK_UNIT,	 1 },
   { "DAY",	tDAY_UNIT,	 1 },
   { "HOUR",	tHOUR_UNIT,	 1 },
   { "MINUTE",	tMINUTE_UNIT,	 1 },
@@ -688,10 +711,10 @@ static table const time_units_table[] =
 /* Assorted relative-time words. */
 static table const relative_time_table[] =
 {
-  { "TOMORROW",	tDAY_UNIT,	 1 },
-  { "YESTERDAY",tDAY_UNIT,	-1 },
-  { "TODAY",	tDAY_UNIT,	 0 },
-  { "NOW",	tDAY_UNIT,	 0 },
+  { "TOMORROW",	tDAY_SHIFT,	 1 },
+  { "YESTERDAY",tDAY_SHIFT,	-1 },
+  { "TODAY",	tDAY_SHIFT,	 0 },
+  { "NOW",	tDAY_SHIFT,	 0 },
   { "LAST",	tORDINAL,	-1 },
   { "THIS",	tORDINAL,	 0 },
   { "NEXT",	tORDINAL,	 1 },
@@ -817,12 +840,27 @@ static table const military_table[] =
    to be picked apart; otherwise, S is of the form HH.  */
 
 static long int
-time_zone_hhmm (textint s, long int mm)
+time_zone_hhmm (parser_control *pc, textint s, long int mm)
 {
+  long int returnvalue;
+
+  /* if s.value is lower than 15, add 00 minutes if mm not specified
+     as common time zones ranges between UTC-1200 and UTC+1400 */
+  if ((abs (s.value) < 15) && (mm < 0))
+    s.value *= 100;
+
   if (mm < 0)
-    return (s.value / 100) * 60 + s.value % 100;
+    returnvalue = (s.value / 100) * 60 + s.value % 100;
   else
-    return s.value * 60 + (s.negative ? -mm : mm);
+    returnvalue = s.value * 60 + (s.negative ? -mm : mm);
+
+  /* check if the return value is in real timezone range, 
+     otherwise increment pc->zones_seen to cause time format 
+     error, allow UTC-1200 to UTC+1400 */
+  if ((returnvalue > 840) || (returnvalue < -720))
+    pc->zones_seen++; 
+
+  return returnvalue;
 }
 
 static int
@@ -1436,25 +1474,6 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
 	    goto fail;
 	}
 
-      if (pc.zones_seen)
-	{
-	  long int delta = pc.time_zone * 60;
-	  time_t t1;
-#ifdef HAVE_TM_GMTOFF
-	  delta -= tm.tm_gmtoff;
-#else
-	  time_t t = Start;
-	  struct tm const *gmt = gmtime (&t);
-	  if (! gmt)
-	    goto fail;
-	  delta -= tm_diff (&tm, gmt);
-#endif
-	  t1 = Start - delta;
-	  if ((Start < t1) != (delta < 0))
-	    goto fail;	/* time_t overflow */
-	  Start = t1;
-	}
-
       /* Add relative date.  */
       if (pc.rel.year | pc.rel.month | pc.rel.day)
 	{
@@ -1477,6 +1496,25 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
 	    goto fail;
 	}
 
+  if (pc.zones_seen)
+	{
+	  long int delta = pc.time_zone * 60;
+	  time_t t1;
+#ifdef HAVE_TM_GMTOFF
+	  delta -= tm.tm_gmtoff;
+#else
+	  time_t t = Start;
+	  struct tm const *gmt = gmtime (&t);
+	  if (! gmt)
+	    goto fail;
+	  delta -= tm_diff (&tm, gmt);
+#endif
+	  t1 = Start - delta;
+	  if ((Start < t1) != (delta < 0))
+	    goto fail;	/* time_t overflow */
+	  Start = t1;
+	}
+
       /* Add relative hours, minutes, and seconds.  On hosts that support
 	 leap seconds, ignore the possibility of leap seconds; e.g.,
 	 "+ 10 minutes" adds 600 seconds, even if one of them is a
-- 
1.5.2.2

Attachment: signature.asc
Description: Toto je digitálně podepsaná část zprávy

Reply via email to