From 59b455cf2fe9b48378902a9c46b931cd66908bf6 Mon Sep 17 00:00:00 2001 From: Andrei Zmievski Date: Thu, 15 Jun 2000 16:24:10 +0000 Subject: [PATCH] @- Updated strtotime() to handle many more formats. It now has complete @ feature parity with GNU date command. (Andrei) --- configure.in | 1 + ext/standard/datetime.c | 47 +- ext/standard/parsedate.y | 1473 ++++++++++++++++++++------------------ 3 files changed, 818 insertions(+), 703 deletions(-) diff --git a/configure.in b/configure.in index 12f58140267..9c8e293432f 100644 --- a/configure.in +++ b/configure.in @@ -341,6 +341,7 @@ getrusage \ gettimeofday \ gmtime_r \ inet_aton \ +isascii \ link \ localtime_r \ lockf \ diff --git a/ext/standard/datetime.c b/ext/standard/datetime.c index d3da443f9b2..c67383c68d8 100644 --- a/ext/standard/datetime.c +++ b/ext/standard/datetime.c @@ -53,7 +53,7 @@ char *day_short_names[] = }; #if !defined(HAVE_TM_ZONE) && !defined(_TIMEZONE) && !defined(HAVE_DECLARED_TIMEZONE) -extern time_t timezone; +extern time_t timezone, altzone; extern int daylight; #endif @@ -65,7 +65,7 @@ static int phpday_tab[2][12] = #define isleap(year) (((year%4) == 0 && (year%100)!=0) || (year%400)==0) -extern PHPAPI time_t parsedate(char *p, struct timeval *now); +extern PHPAPI time_t parse_date (const char *p, const time_t *now); /* {{{ proto int time(void) Return current UNIX timestamp */ @@ -81,6 +81,7 @@ void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gm) struct tm *ta, tmbuf; time_t t; int i, gmadjust, seconds, arg_count = ZEND_NUM_ARGS(); + int is_dst = -1; if (arg_count > 7 || zend_get_parameters_array_ex(arg_count,arguments) == FAILURE) { WRONG_PARAM_COUNT; @@ -116,7 +117,7 @@ void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gm) */ switch(arg_count) { case 7: - ta->tm_isdst = (*arguments[6])->value.lval; + ta->tm_isdst = is_dst = (*arguments[6])->value.lval; /* fall-through */ case 6: /* @@ -155,6 +156,8 @@ void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gm) } seconds = mktime(ta); + if (is_dst == -1) + is_dst = ta->tm_isdst; if (gm) { #if HAVE_TM_GMTOFF @@ -165,11 +168,12 @@ void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gm) gmadjust = ta->tm_gmtoff; #else /* - ** Without tm_gmtoff, the non-ANSI C run-time global 'timezone' - ** variable simply returns the current Winter GMT offset - ** in the current locale (defined in DOS/Windows compilers). + ** If correcting for daylight savings time, we set the adjustment to + ** the value of altzone variable. Otherwise, we need to overcorrect and + ** set the adjustment to the main timezone offset plus difference + ** between the main and alternate ones. */ - gmadjust = timezone; + gmadjust = -(is_dst ? altzone : timezone + (timezone - altzone)); #endif seconds += gmadjust; } @@ -405,9 +409,9 @@ php_date(INTERNAL_FUNCTION_PARAMETERS, int gm) break; case 'Z': /* timezone offset in seconds */ #if HAVE_TM_GMTOFF - sprintf(tmp_buff, "%ld", ta->tm_isdst ? ta->tm_gmtoff-3600 : ta->tm_gmtoff); + sprintf(tmp_buff, "%ld", ta->tm_gmtoff); #else - sprintf(tmp_buff, "%ld", daylight ? timezone-3600 : timezone); + sprintf(tmp_buff, "%ld", ta->tm_isdst ? altzone : timezone); #endif strcat(return_value->value.str.val, tmp_buff); break; @@ -710,29 +714,28 @@ PHP_FUNCTION(gmstrftime) Convert string representation of date and time to a timestamp */ PHP_FUNCTION(strtotime) { - pval **timep, **nowp; - int ac; - struct timeval tv; + zval **z_time, **z_now; + int argc; + time_t now; - ac = ZEND_NUM_ARGS(); + argc = ZEND_NUM_ARGS(); - if (ac < 1 || ac > 2 || zend_get_parameters_ex(ac, &timep,&nowp)==FAILURE) { + if (argc < 1 || argc > 2 || zend_get_parameters_ex(argc, &z_time, &z_now)==FAILURE) { WRONG_PARAM_COUNT; } - convert_to_string_ex(timep); - if (ac == 2) { - convert_to_long_ex(nowp); - tv.tv_sec = (*nowp)->value.lval; - tv.tv_usec = 0; - RETURN_LONG(parsedate((*timep)->value.str.val, &tv)); + convert_to_string_ex(z_time); + if (argc == 2) { + convert_to_long_ex(z_now); + now = Z_LVAL_PP(z_now); + RETURN_LONG(parse_date(Z_STRVAL_PP(z_time), &now)); } else { - RETURN_LONG(parsedate((*timep)->value.str.val, NULL)); + RETURN_LONG(parse_date(Z_STRVAL_PP(z_time), NULL)); } } - /* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/ext/standard/parsedate.y b/ext/standard/parsedate.y index f0a5e80f8a8..d92eccd1e22 100644 --- a/ext/standard/parsedate.y +++ b/ext/standard/parsedate.y @@ -1,23 +1,12 @@ %{ -/* $Revision$ -** +/* ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz -** and Jim Berets in August, 1990. -** Further revised (removed obsolete constructs and cleaned up timezone -** names) in August, 1991, by Rich. Paul Eggert -** helped in September, 1992. -** -** This grammar has six shift/reduce conflicts. +** and Jim Berets in August, 1990. ** ** This code is in the public domain and has no copyright. */ -/* SUPPRESS 530 *//* Empty body for statement */ -/* SUPPRESS 593 on yyerrlab *//* Label was not used */ -/* SUPPRESS 593 on yynewstate *//* Label was not used */ -/* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */ - #include "php.h" @@ -25,13 +14,10 @@ #include #endif -#ifdef HAVE_STDLIB_H -# include -#endif - #include #include #include +#include #ifdef HAVE_SYS_TIME_H # include @@ -40,78 +26,118 @@ # include "win32/time.h" #endif -#include +#if HAVE_STDLIB_H +# include /* for `free'; used by Bison 1.27 */ +#endif #if defined(_HPUX_SOURCE) #include #endif -#if !defined(HAVE_TM_ZONE) && !defined(_TIMEZONE) && !defined(HAVE_DECLARED_TIMEZONE) -extern time_t timezone; +#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) +# define IN_CTYPE_DOMAIN(c) 1 +#else +# define IN_CTYPE_DOMAIN(c) isascii(c) #endif -#define yylhs date_yylhs -#define yylen date_yylen -#define yydefred date_yydefred -#define yydgoto date_yydgoto -#define yysindex date_yysindex -#define yyrindex date_yyrindex -#define yygindex date_yygindex -#define yytable date_yytable -#define yycheck date_yycheck -#define yyparse date_parse -#define yyparse date_parse -#define yylex date_lex -#define yyerror date_error +#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) +#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) +#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) +#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) +/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: + - Its arg may be any int or unsigned int; it need not be an unsigned char. + - It's guaranteed to evaluate its argument exactly once. + - It's typically faster. + Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that + only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless + it's important to use the locale's definition of `digit' even when the + host does not conform to Posix. */ +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#if defined (STDC_HEADERS) || defined (USG) +# include +#endif + +#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __attribute__(x) +#endif + +#ifndef ATTRIBUTE_UNUSED +# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +# define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitiously global symbol names, so we can have multiple + yacc generated parsers in the same program. Note that these are only + the variables produced by yacc. If other parser generators (bison, + byacc, etc) produce additional global names that conflict at link time, + then those parser generators need to be fixed instead of adding those + names to this list. */ + +#define yymaxdepth gd_maxdepth +#define yyparse gd_parse +#define yylex gd_lex +#define yyerror gd_error +#define yylval gd_lval +#define yychar gd_char +#define yydebug gd_debug +#define yypact gd_pact +#define yyr1 gd_r1 +#define yyr2 gd_r2 +#define yydef gd_def +#define yychk gd_chk +#define yypgo gd_pgo +#define yyact gd_act +#define yyexca gd_exca +#define yyerrflag gd_errflag +#define yynerrs gd_nerrs +#define yyps gd_ps +#define yypv gd_pv +#define yys gd_s +#define yy_yys gd_yys +#define yystate gd_state +#define yytmp gd_tmp +#define yyv gd_v +#define yy_yyv gd_yyv +#define yyval gd_val +#define yylloc gd_lloc +#define yyreds gd_reds /* With YYDEBUG defined */ +#define yytoks gd_toks /* With YYDEBUG defined */ +#define yylhs gd_yylhs +#define yylen gd_yylen +#define yydefred gd_yydefred +#define yydgoto gd_yydgoto +#define yysindex gd_yysindex +#define yyrindex gd_yyrindex +#define yygindex gd_yygindex +#define yytable gd_yytable +#define yycheck gd_yycheck + +static int yylex (); +static int yyerror (); - /* See the LeapYears table in Convert. */ #define EPOCH 1970 -#define END_OF_TIME 2038 - /* Constants for general time calculations. */ -#define DST_OFFSET 1 -#define SECSPERDAY (24L * 60L * 60L) - /* Readability for TABLE stuff. */ -#define HOUR(x) (x * 60) +#define HOUR(x) ((x) * 60) -#define LPAREN '(' -#define RPAREN ')' -#define IS7BIT(x) ((unsigned int)(x) < 0200) - -/* -** Get the number of elements in a fixed-size array, or a pointer just -** past the end of it. -*/ -#define SIZEOF(array) ((int)(sizeof array / sizeof array[0])) -#define ENDOF(array) (&array[SIZEOF(array)]) -#define CTYPE(isXXXXX, c) ((isascii((c)) && isXXXXX((c)))) - -typedef struct _TIMEINFO { - time_t time; - long usec; - long tzone; -} TIMEINFO; - -int GetTimeInfo(TIMEINFO *Now); - -typedef char const *STRING; -typedef char * const CSTRING; +#define MAX_BUFF_LEN 128 /* size of buffer to read the date into */ /* ** An entry in the lexical lookup table. */ typedef struct _TABLE { - STRING name; + const char *name; int type; - time_t value; + int value; } TABLE; -/* -** Daylight-savings mode: on, off, or not yet known. -*/ -typedef enum _DSTMODE { - DSTon, DSToff, DSTmaybe -} DSTMODE; /* ** Meridian: am, pm, or 24-hour style. @@ -122,49 +148,51 @@ typedef enum _MERIDIAN { /* -** Global variables. We could get rid of most of them by using a yacc -** union, but this is more efficient. (This routine predates the -** yacc %union construct.) +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. */ -static char *yyInput; -static DSTMODE yyDSTmode; +static const char *yyInput; +static int yyDayOrdinal; +static int yyDayNumber; static int yyHaveDate; +static int yyHaveDay; static int yyHaveRel; static int yyHaveTime; -static time_t yyTimezone; -static time_t yyDay; -static time_t yyHour; -static time_t yyMinutes; -static time_t yyMonth; -static time_t yySeconds; -static time_t yyYear; +static int yyHaveZone; +static int yyTimezone; +static int yyDay; +static int yyHour; +static int yyMinutes; +static int yyMonth; +static int yySeconds; +static int yyYear; static MERIDIAN yyMeridian; -static time_t yyRelMonth; -static time_t yyRelSeconds; - - - -static void date_error(char *s); +static int yyRelDay; +static int yyRelHour; +static int yyRelMinutes; +static int yyRelMonth; +static int yyRelSeconds; +static int yyRelYear; %} -%pure_parser -%expect 6 +/* This grammar has 13 shift/reduce conflicts. */ +%expect 13 %union { - time_t Number; + int Number; enum _MERIDIAN Meridian; } -%{ -static int date_lex(YYSTYPE *yylval); -%} +%token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID +%token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE -%token tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER -%token tUNUMBER tZONE - -%type tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT -%type tSNUMBER tUNUMBER tZONE numzone zone +%type tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT +%type tMONTH tMONTH_UNIT +%type tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE %type tMERIDIAN o_merid %% @@ -175,34 +203,25 @@ spec : /* NULL */ item : time { yyHaveTime++; -#if defined(lint) - /* I am compulsive about lint natterings... */ - if (yyHaveTime == -1) { - YYERROR; - } -#endif /* defined(lint) */ } - | time zone { - yyHaveTime++; - yyTimezone = $2; + | zone { + yyHaveZone++; } | date { yyHaveDate++; } - | rel { - yyHaveRel = 1; + | day { + yyHaveDay++; } + | rel { + yyHaveRel++; + } + | number ; -time : tUNUMBER o_merid { - if ($1 < 100) { - yyHour = $1; - yyMinutes = 0; - } - else { - yyHour = $1 / 100; - yyMinutes = $1 % 100; - } +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; yySeconds = 0; yyMeridian = $2; } @@ -212,12 +231,14 @@ time : tUNUMBER o_merid { yySeconds = 0; yyMeridian = $4; } - | tUNUMBER ':' tUNUMBER numzone { + | tUNUMBER ':' tUNUMBER tSNUMBER { yyHour = $1; yyMinutes = $3; - yyTimezone = $4; yyMeridian = MER24; - yyDSTmode = DSToff; + yyHaveZone++; + yyTimezone = ($4 < 0 + ? -$4 % 100 + (-$4 / 100) * 60 + : - ($4 % 100 + ($4 / 100) * 60)); } | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; @@ -225,56 +246,41 @@ time : tUNUMBER o_merid { yySeconds = $5; yyMeridian = $6; } - | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone { + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { yyHour = $1; yyMinutes = $3; yySeconds = $5; - yyTimezone = $6; yyMeridian = MER24; - yyDSTmode = DSToff; + yyHaveZone++; + yyTimezone = ($6 < 0 + ? -$6 % 100 + (-$6 / 100) * 60 + : - ($6 % 100 + ($6 / 100) * 60)); } ; zone : tZONE { - $$ = $1; - yyDSTmode = DSToff; + yyTimezone = $1; } | tDAYZONE { - $$ = $1; - yyDSTmode = DSTon; + yyTimezone = $1 - 60; } - | tZONE numzone { - /* Only allow "GMT+300" and "GMT-0800" */ - if ($1 != 0) { - YYABORT; - } - $$ = $2; - yyDSTmode = DSToff; - } - | numzone { - $$ = $1; - yyDSTmode = DSToff; + | + tZONE tDST { + yyTimezone = $1 - 60; } ; -numzone : tSNUMBER { - int i; - - /* Unix and GMT and numeric timezones -- a little confusing. */ - if ($1 < 0) { - /* Don't work with negative modulus. */ - $1 = -$1; - if ($1 > 9999 || (i = $1 % 100) >= 60) { - YYABORT; - } - $$ = ($1 / 100) * 60 + i; - } - else { - if ($1 > 9999 || (i = $1 % 100) >= 60) { - YYABORT; - } - $$ = -(($1 / 100) * 60 + i); - } +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; } ; @@ -283,25 +289,35 @@ date : tUNUMBER '/' tUNUMBER { yyDay = $3; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { - if ($1 > 100) { - /* assume YYYY/MM/DD format, so need not to add 1900 */ - yyYear = $1; - yyMonth = $3; - yyDay = $5; + /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. + The goal in recognizing YYYY/MM/DD is solely to support legacy + machine-generated dates like those in an RCS log listing. If + you want portability, use the ISO 8601 format. */ + if ($1 >= 1000) + { + yyYear = $1; + yyMonth = $3; + yyDay = $5; } - else { - /* assume MM/DD/YY* format */ - yyMonth = $1; - yyDay = $3; - if ($5 > 100) { - /* assume year is YYYY format, so need not to add 1900 */ - yyYear = $5; - } else { - /* assume year is YY format, so need to add 1900 */ - yyYear = $5 + 1900; - } + else + { + yyMonth = $1; + yyDay = $3; + yyYear = $5; } } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } | tMONTH tUNUMBER { yyMonth = $1; yyDay = $2; @@ -309,68 +325,139 @@ date : tUNUMBER '/' tUNUMBER { | tMONTH tUNUMBER ',' tUNUMBER { yyMonth = $1; yyDay = $2; - if ($4 > 100) { - /* assume year is YYYY format, so need not to add 1900 */ - yyYear = $4; - } else { - /* assume year is YY format, so need to add 1900 */ - yyYear = $4 + 1900; - } + yyYear = $4; } | tUNUMBER tMONTH { - yyDay = $1; yyMonth = $2; + yyDay = $1; } | tUNUMBER tMONTH tUNUMBER { - yyDay = $1; yyMonth = $2; - if ($3 > 100) { - /* assume year is YYYY format, so need not to add 1900 */ - yyYear = $3; - } else { - /* assume year is YY format, so need to add 1900 */ - yyYear = $3 + 1900; - } - } - | tDAY ',' tUNUMBER tMONTH tUNUMBER { - yyDay = $3; - yyMonth = $4; - if ($5 > 100) { - /* assume year is YYYY format, so need not to add 1900 */ - yyYear = $5; - } else { - /* assume year is YY format, so need to add 1900 */ - yyYear = $5 + 1900; - } + yyDay = $1; + yyYear = $3; } ; -rel : tSNUMBER tSEC_UNIT { - yyRelSeconds += $1 * $2; +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMinutes = -yyRelMinutes; + yyRelHour = -yyRelHour; + yyRelDay = -yyRelDay; + yyRelMonth = -yyRelMonth; + yyRelYear = -yyRelYear; } - | tUNUMBER tSEC_UNIT { - yyRelSeconds += $1 * $2; + | relunit + ; + +relunit : tUNUMBER tYEAR_UNIT { + yyRelYear += $1 * $2; } - | tSNUMBER tMONTH_UNIT { - yyRelMonth += $1 * $2; + | tSNUMBER tYEAR_UNIT { + yyRelYear += $1 * $2; + } + | tYEAR_UNIT { + yyRelYear += $1; } | tUNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + | tUNUMBER tDAY_UNIT { + yyRelDay += $1 * $2; + } + | tSNUMBER tDAY_UNIT { + yyRelDay += $1 * $2; + } + | tDAY_UNIT { + yyRelDay += $1; + } + | tUNUMBER tHOUR_UNIT { + yyRelHour += $1 * $2; + } + | tSNUMBER tHOUR_UNIT { + yyRelHour += $1 * $2; + } + | tHOUR_UNIT { + yyRelHour += $1; + } + | tUNUMBER tMINUTE_UNIT { + yyRelMinutes += $1 * $2; + } + | tSNUMBER tMINUTE_UNIT { + yyRelMinutes += $1 * $2; + } + | tMINUTE_UNIT { + yyRelMinutes += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1 * $2; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1 * $2; + } + | tSEC_UNIT { + yyRelSeconds += $1; + } ; -o_merid : /* NULL */ { +number : tUNUMBER + { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else + { + if ($1>10000) + { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else + { + yyHaveTime++; + if ($1 < 100) + { + yyHour = $1; + yyMinutes = 0; + } + else + { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ + { $$ = MER24; - } - | tMERIDIAN { + } + | tMERIDIAN + { $$ = $1; - } + } ; %% +time_t get_date (const char *p, const time_t *now); + +extern struct tm *gmtime (); +extern struct tm *localtime (); +extern time_t mktime (); + /* Month and day table. */ -static TABLE MonthDayTable[] = { +static TABLE const MonthDayTable[] = { { "january", tMONTH, 1 }, { "february", tMONTH, 2 }, { "march", tMONTH, 3 }, @@ -380,528 +467,552 @@ static TABLE MonthDayTable[] = { { "july", tMONTH, 7 }, { "august", tMONTH, 8 }, { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, { "october", tMONTH, 10 }, { "november", tMONTH, 11 }, { "december", tMONTH, 12 }, - /* The value of the day isn't used... */ { "sunday", tDAY, 0 }, - { "monday", tDAY, 0 }, - { "tuesday", tDAY, 0 }, - { "wednesday", tDAY, 0 }, - { "thursday", tDAY, 0 }, - { "friday", tDAY, 0 }, - { "saturday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL, 0, 0 } }; /* Time units table. */ -static TABLE UnitsTable[] = { - { "year", tMONTH_UNIT, 12 }, +static TABLE const UnitsTable[] = { + { "year", tYEAR_UNIT, 1 }, { "month", tMONTH_UNIT, 1 }, - { "week", tSEC_UNIT, 7 * 24 * 60 * 60 }, - { "day", tSEC_UNIT, 1 * 24 * 60 * 60 }, - { "hour", tSEC_UNIT, 60 * 60 }, - { "minute", tSEC_UNIT, 60 }, - { "min", tSEC_UNIT, 60 }, + { "fortnight", tDAY_UNIT, 14 }, + { "week", tDAY_UNIT, 7 }, + { "day", tDAY_UNIT, 1 }, + { "hour", tHOUR_UNIT, 1 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, { "second", tSEC_UNIT, 1 }, { "sec", tSEC_UNIT, 1 }, + { NULL, 0, 0 } }; -/* Timezone table. */ -static TABLE TimezoneTable[] = { - { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ - { "ut", tZONE, HOUR( 0) }, /* Universal */ - { "utc", tZONE, HOUR( 0) }, /* Universal Coordinated */ - { "cut", tZONE, HOUR( 0) }, /* Coordinated Universal */ - { "z", tZONE, HOUR( 0) }, /* Greenwich Mean */ - { "wet", tZONE, HOUR( 0) }, /* Western European */ - { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ - { "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */ - { "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */ - { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ - { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ - { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ - { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ - { "cst", tZONE, HOUR( 6) }, /* Central Standard */ - { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ - { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ - { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ - { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ - { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ - { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ - { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ - { "akst", tZONE, HOUR( 9) }, /* Alaska Standard */ - { "akdt", tDAYZONE, HOUR( 9) }, /* Alaska Daylight */ - { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ - { "hast", tZONE, HOUR(10) }, /* Hawaii-Aleutian Standard */ - { "hadt", tDAYZONE, HOUR(10) }, /* Hawaii-Aleutian Daylight */ - { "ces", tDAYZONE, -HOUR(1) }, /* Central European Summer */ - { "cest", tDAYZONE, -HOUR(1) }, /* Central European Summer */ - { "mez", tZONE, -HOUR(1) }, /* Middle European */ - { "mezt", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ - { "cet", tZONE, -HOUR(1) }, /* Central European */ - { "met", tZONE, -HOUR(1) }, /* Middle European */ - { "eet", tZONE, -HOUR(2) }, /* Eastern Europe */ - { "msk", tZONE, -HOUR(3) }, /* Moscow Winter */ - { "msd", tDAYZONE, -HOUR(3) }, /* Moscow Summer */ - { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */ - { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */ - { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */ - { "cct", tZONE, -HOUR(8) }, /* China Coast */ - { "jst", tZONE, -HOUR(9) }, /* Japan Standard */ - { "kst", tZONE, -HOUR(9) }, /* Korean Standard */ - { "kdt", tZONE, -HOUR(9) }, /* Korean Daylight */ - { "cast", tZONE, -(HOUR(9)+30) }, /* Central Australian Standard */ - { "cadt", tDAYZONE, -(HOUR(9)+30) }, /* Central Australian Daylight */ - { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ - { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ - { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ - { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tDAY_UNIT, 1 }, + { "yesterday", tDAY_UNIT, -1 }, + { "today", tDAY_UNIT, 0 }, + { "now", tDAY_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 1 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL, 0, 0 } +}; - /* For completeness we include the following entries. */ +/* The timezone table. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR ( 0) }, + { "wet", tZONE, HOUR ( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */ + { "wat", tZONE, HOUR ( 1) }, /* West Africa */ + { "at", tZONE, HOUR ( 2) }, /* Azores */ #if 0 - - /* Duplicate names. Either they conflict with a zone listed above - * (which is either more likely to be seen or just been in circulation - * longer), or they conflict with another zone in this section and - * we could not reasonably choose one over the other. */ - { "fst", tZONE, HOUR( 2) }, /* Fernando De Noronha Standard */ - { "fdt", tDAYZONE, HOUR( 2) }, /* Fernando De Noronha Daylight */ - { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ - { "est", tZONE, HOUR( 3) }, /* Eastern Standard (Brazil) */ - { "edt", tDAYZONE, HOUR( 3) }, /* Eastern Daylight (Brazil) */ - { "wst", tZONE, HOUR( 4) }, /* Western Standard (Brazil) */ - { "wdt", tDAYZONE, HOUR( 4) }, /* Western Daylight (Brazil) */ - { "cst", tZONE, HOUR( 5) }, /* Chile Standard */ - { "cdt", tDAYZONE, HOUR( 5) }, /* Chile Daylight */ - { "ast", tZONE, HOUR( 5) }, /* Acre Standard */ - { "adt", tDAYZONE, HOUR( 5) }, /* Acre Daylight */ - { "cst", tZONE, HOUR( 5) }, /* Cuba Standard */ - { "cdt", tDAYZONE, HOUR( 5) }, /* Cuba Daylight */ - { "est", tZONE, HOUR( 6) }, /* Easter Island Standard */ - { "edt", tDAYZONE, HOUR( 6) }, /* Easter Island Daylight */ - { "sst", tZONE, HOUR(11) }, /* Samoa Standard */ - { "ist", tZONE, -HOUR(2) }, /* Israel Standard */ - { "idt", tDAYZONE, -HOUR(2) }, /* Israel Daylight */ - { "idt", tDAYZONE, -(HOUR(3)+30) }, /* Iran Daylight */ - { "ist", tZONE, -(HOUR(3)+30) }, /* Iran Standard */ - { "cst", tZONE, -HOUR(8) }, /* China Standard */ - { "cdt", tDAYZONE, -HOUR(8) }, /* China Daylight */ - { "sst", tZONE, -HOUR(8) }, /* Singapore Standard */ - - /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */ - { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ - { "wat", tZONE, -HOUR(1) }, /* West Africa */ - { "at", tZONE, HOUR( 2) }, /* Azores */ - { "gst", tZONE, -HOUR(10) }, /* Guam Standard */ - { "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */ - { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ - { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ - { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ - { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ - { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ - { "fwt", tZONE, -HOUR(1) }, /* French Winter */ - { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ - { "bt", tZONE, -HOUR(3) }, /* Baghdad */ - { "it", tZONE, -(HOUR(3)+30) }, /* Iran */ - { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ - { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ - { "ist", tZONE, -(HOUR(5)+30) }, /* Indian Standard */ - { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ - { "nst", tZONE, -HOUR(7) }, /* North Sumatra */ - { "sst", tZONE, -HOUR(7) }, /* South Sumatra */ - { "jt", tZONE, -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */ - { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ - { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ - { "cat", tZONE, HOUR(10) }, /* -- expired 1967 */ - { "nt", tZONE, HOUR(11) }, /* -- expired 1967 */ - { "ahst", tZONE, HOUR(10) }, /* -- expired 1983 */ - { "hdt", tDAYZONE, HOUR(10) }, /* -- expired 1986 */ + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR ( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR (10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR (11) }, /* Nome */ + { "idlw", tZONE, HOUR (12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR (1) }, /* Central European */ + { "met", tZONE, -HOUR (1) }, /* Middle European */ + { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR (1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */ + { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR (3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Standard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */ #endif /* 0 */ + { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */ +#if 0 + { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR (12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR (12) }, /* International Date Line East */ + { NULL, 0, 0 } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR ( 1) }, + { "b", tZONE, HOUR ( 2) }, + { "c", tZONE, HOUR ( 3) }, + { "d", tZONE, HOUR ( 4) }, + { "e", tZONE, HOUR ( 5) }, + { "f", tZONE, HOUR ( 6) }, + { "g", tZONE, HOUR ( 7) }, + { "h", tZONE, HOUR ( 8) }, + { "i", tZONE, HOUR ( 9) }, + { "k", tZONE, HOUR ( 10) }, + { "l", tZONE, HOUR ( 11) }, + { "m", tZONE, HOUR ( 12) }, + { "n", tZONE, HOUR (- 1) }, + { "o", tZONE, HOUR (- 2) }, + { "p", tZONE, HOUR (- 3) }, + { "q", tZONE, HOUR (- 4) }, + { "r", tZONE, HOUR (- 5) }, + { "s", tZONE, HOUR (- 6) }, + { "t", tZONE, HOUR (- 7) }, + { "u", tZONE, HOUR (- 8) }, + { "v", tZONE, HOUR (- 9) }, + { "w", tZONE, HOUR (-10) }, + { "x", tZONE, HOUR (-11) }, + { "y", tZONE, HOUR (-12) }, + { "z", tZONE, HOUR ( 0) }, + { NULL, 0, 0 } }; + /* ARGSUSED */ -static void -date_error(char *s) +static int +yyerror (s) + char *s ATTRIBUTE_UNUSED; { - /* NOTREACHED */ + return 0; } -int GetTimeInfo(TIMEINFO *Now) +static int +ToHour (Hours, Meridian) + int Hours; + MERIDIAN Meridian; { - static time_t NextHour; - static long LastTzone; - struct tm *tm, tmbuf; - int secondsUntilNextHour; -#if defined(HAVE_GETTIMEOFDAY) - struct timeval tv; -#endif /* defined(HAVE_GETTIMEOFDAY) */ -#if !defined(HAVE_TM_GMTOFF) - struct tm local; - struct tm gmt; -#endif /* !defined(HAVE_TM_GMTOFF) */ - - /* Get the basic time. */ -#if defined(HAVE_GETTIMEOFDAY) - if (gettimeofday(&tv, (struct timezone *)NULL) == -1) + switch (Meridian) + { + case MER24: + if (Hours < 0 || Hours > 23) return -1; - Now->time = tv.tv_sec; - Now->usec = tv.tv_usec; -#else - /* Can't check for -1 since that might be a time, I guess. */ - (void)time(&Now->time); - Now->usec = 0; -#endif /* defined(HAVE_GETTIMEOFDAY) */ - - /* Now get the timezone if the last time < HH:00:00 <= now for some HH. */ - if (NextHour <= Now->time) { - if ((tm = php_localtime_r(&Now->time, &tmbuf)) == NULL) - return -1; - secondsUntilNextHour = 60 * (60 - tm->tm_min) - tm->tm_sec; -#if !defined(HAVE_TM_GMTOFF) - /* To get the timezone, compare localtime with GMT. */ - local = *tm; - if ((tm = php_gmtime_r(&Now->time, &tmbuf)) == NULL) - return -1; - gmt = *tm; - - /* Assume we are never more than 24 hours away. */ - LastTzone = gmt.tm_yday - local.tm_yday; - if (LastTzone > 1) - LastTzone = -24; - else if (LastTzone < -1) - LastTzone = 24; - else - LastTzone *= 24; - - /* Scale in the hours and minutes; ignore seconds. */ - LastTzone += gmt.tm_hour - local.tm_hour; - LastTzone *= 60; - LastTzone += gmt.tm_min - local.tm_min; -#else - LastTzone = (0 - tm->tm_gmtoff) / 60; -#endif /* defined(HAVE_TM_GMTOFF) */ - NextHour = Now->time + secondsUntilNextHour; + return Hours; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return Hours + 12; + default: + abort (); } - Now->tzone = LastTzone; - return 0; + /* NOTREACHED */ } - -static time_t -ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian) +static int +ToYear (Year) + int Year; { - if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61) - return -1; - if (Meridian == MER24) { - if (Hours < 0 || Hours > 23) - return -1; - } - else { - if (Hours < 1 || Hours > 12) - return -1; - if (Hours == 12) - Hours = 0; - if (Meridian == MERpm) - Hours += 12; - } - return (Hours * 60L + Minutes) * 60L + Seconds; -} + if (Year < 0) + Year = -Year; - -static time_t -Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian, DSTMODE dst) -{ - static int DaysNormal[13] = { - 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - static int DaysLeap[13] = { - 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - static int LeapYears[] = { - 1972, 1976, 1980, 1984, 1988, 1992, 1996, - 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036 - }; - int *yp; - int *mp; - time_t Julian; - int i; - time_t tod; - struct tm tmbuf; - - /* Year should not be passed as a relative value, but absolute one. - so this should not happen, but just ensure it */ - if (Year < 0) - Year = -Year; - if (Year < 100) - Year += 1900; - if (Year < EPOCH) - Year += 100; - for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++) - if (Year == *yp) { - mp = DaysLeap; - break; - } - if (Year < EPOCH || Year > END_OF_TIME - || Month < 1 || Month > 12 - /* NOSTRICT *//* conversion from long may lose accuracy */ - || Day < 1 || Day > mp[(int)Month]) - return -1; - - Julian = Day - 1 + (Year - EPOCH) * 365; - for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++) - if (Year <= *yp) - break; - for (i = 1; i < Month; i++) - Julian += *++mp; - Julian *= SECSPERDAY; - Julian += yyTimezone * 60L; - if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) - return -1; - Julian += tod; - tod = Julian; - if (dst == DSTon || (dst == DSTmaybe && php_localtime_r(&tod,&tmbuf)->tm_isdst)) - Julian -= DST_OFFSET * 60 * 60; - return Julian; -} - - -static time_t -DSTcorrect(time_t Start, time_t Future) -{ - time_t StartDay; - time_t FutureDay; - struct tm tmbuf; - - StartDay = (php_localtime_r(&Start,&tmbuf)->tm_hour + 1) % 24; - FutureDay = (php_localtime_r(&Future,&tmbuf)->tm_hour + 1) % 24; - return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60; -} - - -static time_t -RelativeMonth(time_t Start, time_t RelMonth) -{ - struct tm *tm, tmbuf; - time_t Month; - time_t Year; - - tm = php_localtime_r(&Start, &tmbuf); - Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; - Year = Month / 12; + /* XPG4 suggests that years 00-68 map to 2000-2068, and + years 69-99 map to 1969-1999. */ + if (Year < 69) + Year += 2000; + else if (Year < 100) Year += 1900; - Month = Month % 12 + 1; - return DSTcorrect(Start, - Convert(Month, (time_t)tm->tm_mday, Year, - (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, - MER24, DSTmaybe)); + + return Year; } - -static int LookupWord(char *buff, int length, YYSTYPE *yylval) +static int +LookupWord (buff) + char *buff; { - char *p; - STRING q; - TABLE *tp; - int c; + register char *p; + register char *q; + register const TABLE *tp; + int i; + int abbrev; - p = buff; - c = p[0]; + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (ISUPPER ((unsigned char) *p)) + *p = tolower (*p); - /* See if we have an abbreviation for a month. */ - if (length == 3 || (length == 4 && p[3] == '.')) - for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) { - q = tp->name; - if (c == q[0] && p[1] == q[1] && p[2] == q[2]) { - yylval->Number = tp->value; - return tp->type; + if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) + { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) + { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen (buff) == 3) + abbrev = 1; + else if (strlen (buff) == 4 && buff[3] == '.') + { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) + { + if (abbrev) + { + if (strncmp (buff, tp->name, 3) == 0) + { + yylval.Number = tp->value; + return tp->type; } } + else if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp (buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen (buff) - 1; + if (buff[i] == 's') + { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff)) + { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; else - for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) - if (c == tp->name[0] && strcmp(p, tp->name) == 0) { - yylval->Number = tp->value; - return tp->type; - } - - /* Try for a timezone. */ - for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) - if (c == tp->name[0] && p[1] == tp->name[1] - && strcmp(p, tp->name) == 0) { - yylval->Number = tp->value; - return tp->type; + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp (buff, tp->name) == 0) + { + yylval.Number = tp->value; + return tp->type; } - /* Try the units table. */ - for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) - if (c == tp->name[0] && strcmp(p, tp->name) == 0) { - yylval->Number = tp->value; - return tp->type; - } - - /* Strip off any plural and try the units table again. */ - if (--length > 0 && p[length] == 's') { - p[length] = '\0'; - for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) - if (c == tp->name[0] && strcmp(p, tp->name) == 0) { - p[length] = 's'; - yylval->Number = tp->value; - return tp->type; - } - p[length] = 's'; - } - length++; - - /* Drop out any periods. */ - for (p = buff, q = (STRING)buff; *q; q++) - if (*q != '.') - *p++ = *q; - *p = '\0'; - - /* Try the meridians. */ - if (buff[1] == 'm' && buff[2] == '\0') { - if (buff[0] == 'a') { - yylval->Meridian = MERam; - return tMERIDIAN; - } - if (buff[0] == 'p') { - yylval->Meridian = MERpm; - return tMERIDIAN; - } - } - - /* If we saw any periods, try the timezones again. */ - if (p - buff != length) { - c = buff[0]; - for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) - if (c == tp->name[0] && p[1] == tp->name[1] - && strcmp(p, tp->name) == 0) { - yylval->Number = tp->value; - return tp->type; - } - } - - /* Unknown word -- assume GMT timezone. */ - yylval->Number = 0; - return tZONE; + return tID; } - -static int date_lex(YYSTYPE *yylval) +static int +yylex () { - char c; - char *p; - char buff[20]; - int sign; - int i; - int nesting; + register unsigned char c; + register char *p; + char buff[20]; + int Count; + int sign; - for ( ; ; ) { - /* Get first character after the whitespace. */ - for ( ; ; ) { - while (CTYPE(isspace, (int)*yyInput)) - yyInput++; - c = *yyInput; + for (;;) + { + while (ISSPACE ((unsigned char) *yyInput)) + yyInput++; - /* Ignore RFC 822 comments, typically time zone names. */ - if (c != LPAREN) - break; - for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; ) - if (c == LPAREN) - nesting++; - else if (!IS7BIT(c) || c == '\0' || c == '\r' - || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c)))) - /* Lexical error: bad comment. */ - return '?'; - yyInput++; - } - - /* A number? */ - if (CTYPE(isdigit, (int)c) || c == '-' || c == '+') { - if (c == '-' || c == '+') { - sign = c == '-' ? -1 : 1; - yyInput++; - if (!CTYPE(isdigit, (int)*yyInput)) - /* Skip the plus or minus sign. */ - continue; + if (ISDIGIT (c = *yyInput) || c == '-' || c == '+') + { + if (c == '-' || c == '+') + { + sign = c == '-' ? -1 : 1; + if (!ISDIGIT (*++yyInput)) + /* skip the '-' sign */ + continue; } - else - sign = 0; - for (i = 0; (c = *yyInput++) != '\0' && CTYPE(isdigit, (int)c); ) - i = 10 * i + c - '0'; - yyInput--; - yylval->Number = sign < 0 ? -i : i; - return sign ? tSNUMBER : tUNUMBER; + else + sign = 0; + for (yylval.Number = 0; ISDIGIT (c = *yyInput++);) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; } - - /* A word? */ - if (CTYPE(isalpha, (int)c)) { - for (p = buff; (c = *yyInput++) == '.' || CTYPE(isalpha, (int)c); ) - if (p < &buff[sizeof buff - 1]) - *p++ = CTYPE(isupper, (int)c) ? tolower(c) : c; - *p = '\0'; - yyInput--; - return LookupWord(buff, p - buff, yylval); + if (ISALPHA (c)) + { + for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord (buff); } - + if (c != '(') return *yyInput++; + Count = 0; + do + { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } + while (Count > 0); } } +#define TM_YEAR_ORIGIN 1900 -time_t parsedate(char *p, TIMEINFO *now) +/* Yield A - B, measured in seconds. */ +static long +difftm (struct tm *a, struct tm *b) { - struct tm *tm, tmbuf; - TIMEINFO ti; - time_t Start; - - yyInput = p; - if (now == NULL) { - now = &ti; - (void)GetTimeInfo(&ti); - } - - tm = php_localtime_r(&now->time, &tmbuf); - yyYear = tm->tm_year + 1900; - yyMonth = tm->tm_mon + 1; - yyDay = tm->tm_mday; -#ifdef HAVE_TM_GMTOFF - yyTimezone = tm->tm_gmtoff/60; -#else - yyTimezone = timezone/60; -#endif - yyDSTmode = DSTmaybe; - yyHour = 0; - yyMinutes = 0; - yySeconds = 0; - yyMeridian = MER24; - yyRelSeconds = 0; - yyRelMonth = 0; - yyHaveDate = 0; - yyHaveRel = 0; - yyHaveTime = 0; - - if (date_parse(YYPARSE_PARAM_ARG) || yyHaveTime > 1 || yyHaveDate > 1) - return -1; - - if (yyHaveDate || yyHaveTime) { - Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, - yyMeridian, yyDSTmode); - if (Start < 0) - return -1; - } - else { - Start = now->time; - if (!yyHaveRel) - Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec; - } - - Start += yyRelSeconds; - if (yyRelMonth) - Start += RelativeMonth(Start, yyRelMonth); - - /* Have to do *something* with a legitimate -1 so it's distinguishable - * from the error return value. (Alternately could set errno on error.) */ - return Start == -1 ? 0 : Start; + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + long days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay / 100 - by / 100) + + ((ay / 100 >> 2) - (by / 100 >> 2)) + /* + difference in years * 365 */ + + (long) (ay - by) * 365 + ); + return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} + +time_t +parse_date (const char *p, const time_t *now) +{ + struct tm tm, tm0, *tmp; + time_t Start; + + yyInput = p; + Start = now ? *now : time ((time_t *) NULL); + tmp = localtime (&Start); + if (!tmp) + return -1; + yyYear = tmp->tm_year + TM_YEAR_ORIGIN; + yyMonth = tmp->tm_mon + 1; + yyDay = tmp->tm_mday; + yyHour = tmp->tm_hour; + yyMinutes = tmp->tm_min; + yySeconds = tmp->tm_sec; + tm.tm_isdst = tmp->tm_isdst; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMinutes = 0; + yyRelHour = 0; + yyRelDay = 0; + yyRelMonth = 0; + yyRelYear = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse () + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear; + tm.tm_mon = yyMonth - 1 + yyRelMonth; + tm.tm_mday = yyDay + yyRelDay; + if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay)) + { + tm.tm_hour = ToHour (yyHour, yyMeridian); + if (tm.tm_hour < 0) + return -1; + tm.tm_min = yyMinutes; + tm.tm_sec = yySeconds; + } + else + { + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + } + tm.tm_hour += yyRelHour; + tm.tm_min += yyRelMinutes; + tm.tm_sec += yyRelSeconds; + + /* Let mktime deduce tm_isdst if we have an absolute timestamp, + or if the relative timestamp mentions days, months, or years. */ + if (yyHaveDate | yyHaveDay | yyHaveTime | yyRelDay | yyRelMonth | yyRelYear) + tm.tm_isdst = -1; + + tm0 = tm; + + Start = mktime (&tm); + + if (Start == (time_t) -1) + { + + /* Guard against falsely reporting errors near the time_t boundaries + when parsing times in other time zones. For example, if the min + time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead + of UTC, then the min localtime value is 1970-01-01 08:00:00; if + we apply mktime to 1970-01-01 00:00:00 we will get an error, so + we apply mktime to 1970-01-02 08:00:00 instead and adjust the time + zone by 24 hours to compensate. This algorithm assumes that + there is no DST transition within a day of the time_t boundaries. */ + if (yyHaveZone) + { + tm = tm0; + if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) + { + tm.tm_mday++; + yyTimezone -= 24 * 60; + } + else + { + tm.tm_mday--; + yyTimezone += 24 * 60; + } + Start = mktime (&tm); + } + + if (Start == (time_t) -1) + return Start; + } + + if (yyHaveDay && !yyHaveDate) + { + tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7 + + 7 * (yyDayOrdinal - (0 < yyDayOrdinal))); + Start = mktime (&tm); + if (Start == (time_t) -1) + return Start; + } + + if (yyHaveZone) + { + long delta; + struct tm *gmt = gmtime (&Start); + if (!gmt) + return -1; + delta = yyTimezone * 60L + difftm (&tm, gmt); + if ((Start + delta < Start) != (delta < 0)) + return -1; /* time_t overflow */ + Start += delta; + } + + return Start; }