From 55d067b5172b233c63b364cad8efc171910eddee Mon Sep 17 00:00:00 2001 From: =?utf-8?q?Ond=C5=99ej=20Va=C5=A1=C3=ADk?= 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 --- 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 tDAY_UNIT +%token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT tDAY_UNIT +%token tDAY_SHIFT tWEEK_UNIT %token tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN %token 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