X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fparse-datetime.y;h=6ece7656a964b4267bb3f83fc359c7f532ec5411;hb=1276a2c5f24c0c932426aca9c899fa524d2443f2;hp=f22c754e0fdcde3eede5162418fa37828d9566fa;hpb=2bb63bfb25474ea147ee9f1523c0337997359a4c;p=gnulib.git diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y index f22c754e0..6ece7656a 100644 --- a/lib/parse-datetime.y +++ b/lib/parse-datetime.y @@ -1,8 +1,7 @@ %{ /* Parse a string into an internal time stamp. - Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - 2010 Free Software Foundation, Inc. + Copyright (C) 1999-2000, 2002-2014 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 @@ -50,7 +49,7 @@ #define YYMAXDEPTH 20 #define YYINITDEPTH YYMAXDEPTH -/* Since the code of getdate.y is not included in the Emacs executable +/* Since the code of parse-datetime.y is not included in the Emacs executable itself, there is no need to #define static in this file. Even if the code were included in the Emacs executable, it probably wouldn't do any harm to #undef it here; this will only cause @@ -68,6 +67,14 @@ #include "xalloc.h" +/* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers + use _STDLIB_H_ as witness. Map the latter to the one bison uses. */ +/* FIXME: this is temporary. Remove when we have a mechanism to ensure + that the version we're using is fixed, too. */ +#ifdef _STDLIB_H_ +# undef _STDLIB_H +# define _STDLIB_H 1 +#endif /* ISDIGIT differs from isdigit, as follows: - Its arg may be any int or unsigned int; it need not be an unsigned char @@ -75,7 +82,7 @@ - It's typically faster. POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless it's important to use the locale's definition - of `digit' even when the host does not conform to POSIX. */ + of "digit" even when the host does not conform to POSIX. */ #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9) /* Shift A right by B bits portably, by dividing A by 2**B and @@ -106,6 +113,11 @@ typedef long int long_time_t; typedef time_t long_time_t; #endif +/* Convert a possibly-signed character to an unsigned character. This is + a bit safer than casting to unsigned char, since it catches some type + errors that the cast doesn't. */ +static unsigned char to_uchar (char ch) { return ch; } + /* 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) @@ -197,7 +209,7 @@ typedef struct size_t times_seen; size_t zones_seen; - /* Table of local time zone abbrevations, terminated by a null entry. */ + /* Table of local time zone abbreviations, terminated by a null entry. */ table local_time_zone_table[3]; } parser_control; @@ -278,8 +290,8 @@ set_hhmmss (parser_control *pc, long int hour, long int minutes, %parse-param { parser_control *pc } %lex-param { parser_control *pc } -/* This grammar has 20 shift/reduce conflicts. */ -%expect 20 +/* This grammar has 31 shift/reduce conflicts. */ +%expect 31 %union { @@ -289,7 +301,8 @@ set_hhmmss (parser_control *pc, long int hour, long int minutes, relative_time rel; } -%token tAGO tDST +%token tAGO +%token tDST %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT %token tDAY_UNIT tDAY_SHIFT @@ -300,7 +313,7 @@ set_hhmmss (parser_control *pc, long int hour, long int minutes, %token tSNUMBER tUNUMBER %token tSDECIMAL_NUMBER tUDECIMAL_NUMBER -%type o_colon_minutes o_merid +%type o_colon_minutes %type seconds signed_seconds unsigned_seconds %type relunit relunit_snumber dayshift @@ -326,7 +339,9 @@ items: ; item: - time + datetime + { pc->times_seen++; pc->dates_seen++; } + | time { pc->times_seen++; } | local_zone { pc->local_zones_seen++; } @@ -341,35 +356,61 @@ item: | hybrid ; +datetime: + iso_8601_datetime + ; + +iso_8601_datetime: + iso_8601_date 'T' iso_8601_time + ; + time: tUNUMBER tMERIDIAN { set_hhmmss (pc, $1.value, 0, 0, 0); pc->meridian = $2; } - | tUNUMBER ':' tUNUMBER o_merid + | tUNUMBER ':' tUNUMBER tMERIDIAN { set_hhmmss (pc, $1.value, $3.value, 0, 0); pc->meridian = $4; } - | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes + | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN { - set_hhmmss (pc, $1.value, $3.value, 0, 0); + set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec); + pc->meridian = $6; + } + | iso_8601_time + ; + +iso_8601_time: + tUNUMBER zone_offset + { + set_hhmmss (pc, $1.value, 0, 0, 0); pc->meridian = MER24; - pc->zones_seen++; - pc->time_zone = time_zone_hhmm (pc, $4, $5); } - | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid + | tUNUMBER ':' tUNUMBER o_zone_offset { - set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec); - pc->meridian = $6; + set_hhmmss (pc, $1.value, $3.value, 0, 0); + pc->meridian = MER24; } - | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes + | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_zone_offset { set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec); pc->meridian = MER24; + } + ; + +o_zone_offset: + /* empty */ + | zone_offset + ; + +zone_offset: + tSNUMBER o_colon_minutes + { pc->zones_seen++; - pc->time_zone = time_zone_hhmm (pc, $6, $7); + pc->time_zone = time_zone_hhmm (pc, $1, $2); } ; @@ -386,12 +427,19 @@ local_zone: } ; +/* Note 'T' is a special case, as it is used as the separator in ISO + 8601 date and time of day representation. */ zone: tZONE { pc->time_zone = $1; } + | 'T' + { pc->time_zone = HOUR(7); } | tZONE relunit_snumber { pc->time_zone = $1; apply_relative_time (pc, $2, 1); } + | 'T' relunit_snumber + { pc->time_zone = HOUR(7); + apply_relative_time (pc, $2, 1); } | tZONE tSNUMBER o_colon_minutes { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); } | tDAYZONE @@ -449,13 +497,6 @@ date: pc->year = $5; } } - | tUNUMBER tSNUMBER tSNUMBER - { - /* ISO 8601 format. YYYY-MM-DD. */ - pc->year = $1; - pc->month = -$2.value; - pc->day = -$3.value; - } | tUNUMBER tMONTH tSNUMBER { /* e.g. 17-JUN-1992. */ @@ -494,11 +535,22 @@ date: pc->month = $2; pc->year = $3; } + | iso_8601_date + ; + +iso_8601_date: + tUNUMBER tSNUMBER tSNUMBER + { + /* ISO 8601 format. YYYY-MM-DD. */ + pc->year = $1; + pc->month = -$2.value; + pc->day = -$3.value; + } ; rel: relunit tAGO - { apply_relative_time (pc, $1, -1); } + { apply_relative_time (pc, $1, $2); } | relunit { apply_relative_time (pc, $1, 1); } | dayshift @@ -605,13 +657,6 @@ o_colon_minutes: { $$ = $2.value; } ; -o_merid: - /* empty */ - { $$ = MER24; } - | tMERIDIAN - { $$ = $1; } - ; - %% static table const meridian_table[] = @@ -694,7 +739,8 @@ static table const relative_time_table[] = { "TENTH", tORDINAL, 10 }, { "ELEVENTH", tORDINAL, 11 }, { "TWELFTH", tORDINAL, 12 }, - { "AGO", tAGO, 1 }, + { "AGO", tAGO, -1 }, + { "HENCE", tAGO, 1 }, { NULL, 0, 0 } }; @@ -712,8 +758,8 @@ static table const universal_time_zone_table[] = /* The time zone table. This table is necessarily incomplete, as time zone abbreviations are ambiguous; e.g. Australians interpret "EST" as Eastern time in Australia, not as US Eastern Standard Time. - You cannot rely on getdate to handle arbitrary time zone - abbreviations; use numeric abbreviations like `-0500' instead. */ + You cannot rely on parse_datetime to handle arbitrary time zone + abbreviations; use numeric abbreviations like "-0500" instead. */ static table const time_zone_table[] = { { "WET", tZONE, HOUR ( 0) }, /* Western European */ @@ -766,7 +812,10 @@ static table const time_zone_table[] = { NULL, 0, 0 } }; -/* Military time zone table. */ +/* Military time zone table. + + Note 'T' is a special case, as it is used as the separator in ISO + 8601 date and time of day representation. */ static table const military_table[] = { { "A", tZONE, -HOUR ( 1) }, @@ -787,7 +836,7 @@ static table const military_table[] = { "Q", tZONE, HOUR ( 4) }, { "R", tZONE, HOUR ( 5) }, { "S", tZONE, HOUR ( 6) }, - { "T", tZONE, HOUR ( 7) }, + { "T", 'T', 0 }, { "U", tZONE, HOUR ( 8) }, { "V", tZONE, HOUR ( 9) }, { "W", tZONE, HOUR (10) }, @@ -861,7 +910,7 @@ to_year (textint textyear) return year; } -static table const * +static table const * _GL_ATTRIBUTE_PURE lookup_zone (parser_control const *pc, char const *name) { table const *tp; @@ -1112,7 +1161,7 @@ yylex (YYSTYPE *lvalp, parser_control *pc) do { - if (p < buff + sizeof buff - 1) + if (p - buff < sizeof buff - 1) *p++ = c; c = *++pc->input; } @@ -1127,7 +1176,8 @@ yylex (YYSTYPE *lvalp, parser_control *pc) } if (c != '(') - return *pc->input++; + return to_uchar (*pc->input++); + count = 0; do { @@ -1350,7 +1400,7 @@ parse_datetime (struct timespec *result, char const *p, && ! strcmp (pc.local_time_zone_table[0].name, pc.local_time_zone_table[1].name)) { - /* This locale uses the same abbrevation for standard and + /* This locale uses the same abbreviation for standard and daylight times. So if we see that abbreviation, we don't know whether it's daylight time. */ pc.local_time_zone_table[0].value = -1; @@ -1422,7 +1472,7 @@ parse_datetime (struct timespec *result, char const *p, + sizeof pc.time_zone * CHAR_BIT / 3]; if (!tz_was_altered) tz0 = get_tz (tz0buf); - sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0), + 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;