From ec560ef5cab3aec7a68fb2da6b2dbbbd7415caf1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ond=C5=99ej=20Va=C5=A1=C3=ADk?= Date: Sat, 30 Aug 2008 15:04:04 +0200 Subject: [PATCH] getdate.y: reject an out-of-range timezone value * lib/getdate.y (time_zone_hhmm): Reject any TZ offset that is outside the range [-24...+24]. When specified with only one or two digits, * tests/test-getdate.c: Tests for the fix. * doc/getdate.texi: Document this change. --- ChangeLog | 8 ++++++ doc/getdate.texi | 13 ++++++---- lib/getdate.y | 34 ++++++++++++++++++------ tests/test-getdate.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0169170866..8109f7b2a1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2008-09-04 Ondřej Vašík + + getdate.y: reject an out-of-range timezone value + * lib/getdate.y (time_zone_hhmm): Reject any TZ offset that is outside + the range [-24...+24]. When specified with only one or two digits, + * tests/test-getdate.c: Tests for the fix. + * doc/getdate.texi: Document this change. + 2008-09-03 Bruno Haible * doc/glibc-functions/strverscmp.texi: Mention the strverscmp module. diff --git a/doc/getdate.texi b/doc/getdate.texi index eae4526b48..7175e3d5f2 100644 --- a/doc/getdate.texi +++ b/doc/getdate.texi @@ -265,16 +265,19 @@ which uses @samp{12m} for noon and @samp{12pm} for midnight.) The time may alternatively be followed by a time zone correction, expressed as @samp{@var{s}@var{hh}@var{mm}}, where @var{s} is @samp{+} or @samp{-}, @var{hh} is a number of zone hours and @var{mm} is a number -of zone minutes. You can also separate @var{hh} from @var{mm} with a colon. +of zone minutes. +The zone minutes term, @var{mm}, may be omitted, in which case +the one- or two-digit correction is interpreted as a number of hours. +You can also separate @var{hh} from @var{mm} with a colon. When a time zone correction is given this way, it forces interpretation of the time relative to Coordinated Universal Time (@sc{utc}), overriding any previous specification for the time zone or the local time zone. For example, @samp{+0530} and @samp{+05:30} both stand for the time zone 5.5 hours -ahead of @sc{utc} (e.g., India). The @var{minute} -part of the time of day may not be elided when a time zone correction -is used. This is the best way to specify a time zone correction by -fractional parts of an hour. +ahead of @sc{utc} (e.g., India). +This is the best way to +specify a time zone correction by fractional parts of an hour. +The maximum zone correction is 24 hours. Either @samp{am}/@samp{pm} or a time zone correction may be specified, but not both. diff --git a/lib/getdate.y b/lib/getdate.y index a94bf8be56..f9cd86c3a3 100644 --- a/lib/getdate.y +++ b/lib/getdate.y @@ -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, @@ -358,7 +358,7 @@ time: set_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 { @@ -370,7 +370,7 @@ time: set_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); } ; @@ -394,7 +394,7 @@ zone: { pc->time_zone = $1; apply_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); } | tDAYZONE { pc->time_zone = $1 + 60; } | tZONE tDST @@ -795,15 +795,33 @@ static table const military_table[] = /* Convert a time zone expressed as HH:MM into an integer count of minutes. If MM is negative, then S is of the form HHMM and needs - to be picked apart; otherwise, S is of the form HH. */ + to be picked apart; otherwise, S is of the form HH. As specified in + http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow + only valid TZ range, and consider first two digits as hours, if no + minutes specified. */ static long int -time_zone_hhmm (textint s, long int mm) +time_zone_hhmm (parser_control *pc, textint s, long int mm) { + long int n_minutes; + + /* If the length of S is 1 or 2 and no minutes are specified, + interpret it as a number of hours. */ + if (s.digits <= 2 && mm < 0) + s.value *= 100; + if (mm < 0) - return (s.value / 100) * 60 + s.value % 100; + n_minutes = (s.value / 100) * 60 + s.value % 100; else - return s.value * 60 + (s.negative ? -mm : mm); + n_minutes = s.value * 60 + (s.negative ? -mm : mm); + + /* If the absolute number of minutes is larger than 24 hours, + arrange to reject it by incrementing pc->zones_seen. Thus, + we allow only values in the range UTC-24:00 to UTC+24:00. */ + if (24 * 60 < abs (n_minutes)) + pc->zones_seen++; + + return n_minutes; } static int diff --git a/tests/test-getdate.c b/tests/test-getdate.c index 901adf953a..adbcf3a698 100644 --- a/tests/test-getdate.c +++ b/tests/test-getdate.c @@ -98,5 +98,67 @@ main (int argc, char **argv) ASSERT (result.tv_sec == result2.tv_sec && result.tv_nsec == result2.tv_nsec); + /* test if several time zones formats are handled same way */ + now.tv_sec = 4711; + now.tv_nsec = 1267; + p = "UTC+14:00"; + ASSERT (get_date (&result, p, &now)); + LOG (p, now, result); + p = "UTC+14"; + ASSERT (get_date (&result2, p, &now)); + LOG (p, now, result2); + ASSERT (result.tv_sec == result2.tv_sec + && result.tv_nsec == result2.tv_nsec); + p = "UTC+1400"; + ASSERT (get_date (&result2, p, &now)); + LOG (p, now, result2); + ASSERT (result.tv_sec == result2.tv_sec + && result.tv_nsec == result2.tv_nsec); + + now.tv_sec = 4711; + now.tv_nsec = 1267; + p = "UTC-14:00"; + ASSERT (get_date (&result, p, &now)); + LOG (p, now, result); + p = "UTC-14"; + ASSERT (get_date (&result2, p, &now)); + LOG (p, now, result2); + ASSERT (result.tv_sec == result2.tv_sec + && result.tv_nsec == result2.tv_nsec); + p = "UTC-1400"; + ASSERT (get_date (&result2, p, &now)); + LOG (p, now, result2); + ASSERT (result.tv_sec == result2.tv_sec + && result.tv_nsec == result2.tv_nsec); + + now.tv_sec = 4711; + now.tv_nsec = 1267; + p = "UTC+0:15"; + ASSERT (get_date (&result, p, &now)); + LOG (p, now, result); + p = "UTC+0015"; + ASSERT (get_date (&result2, p, &now)); + LOG (p, now, result2); + ASSERT (result.tv_sec == result2.tv_sec + && result.tv_nsec == result2.tv_nsec); + + now.tv_sec = 4711; + now.tv_nsec = 1267; + p = "UTC-1:30"; + ASSERT (get_date (&result, p, &now)); + LOG (p, now, result); + p = "UTC-130"; + ASSERT (get_date (&result2, p, &now)); + LOG (p, now, result2); + ASSERT (result.tv_sec == result2.tv_sec + && result.tv_nsec == result2.tv_nsec); + + + /* TZ out of range should cause get_date failure */ + now.tv_sec = 4711; + now.tv_nsec = 1267; + p = "UTC+25:00"; + ASSERT (!get_date (&result, p, &now)); + return 0; } -- 2.30.2