Home | History | Annotate | Line # | Download | only in libutil
parsedate.y revision 1.3
      1  1.1  christos %{
      2  1.1  christos /*
      3  1.1  christos **  Originally written by Steven M. Bellovin <smb (at) research.att.com> while
      4  1.1  christos **  at the University of North Carolina at Chapel Hill.  Later tweaked by
      5  1.1  christos **  a couple of people on Usenet.  Completely overhauled by Rich $alz
      6  1.1  christos **  <rsalz (at) bbn.com> and Jim Berets <jberets (at) bbn.com> in August, 1990;
      7  1.1  christos **
      8  1.1  christos **  This grammar has 10 shift/reduce conflicts.
      9  1.1  christos **
     10  1.1  christos **  This code is in the public domain and has no copyright.
     11  1.1  christos */
     12  1.1  christos /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
     13  1.1  christos /* SUPPRESS 288 on yyerrlab *//* Label unused */
     14  1.1  christos 
     15  1.1  christos #include <stdio.h>
     16  1.1  christos #include <ctype.h>
     17  1.1  christos #include <string.h>
     18  1.1  christos #include <time.h>
     19  1.1  christos #include <util.h>
     20  1.3  drochner #include <stdlib.h>
     21  1.1  christos 
     22  1.1  christos /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
     23  1.1  christos    releases):
     24  1.1  christos 
     25  1.1  christos    We don't want to mess with all the portability hassles of alloca.
     26  1.1  christos    In particular, most (all?) versions of bison will use alloca in
     27  1.1  christos    their parser.  If bison works on your system (e.g. it should work
     28  1.1  christos    with gcc), then go ahead and use it, but the more general solution
     29  1.1  christos    is to use byacc instead of bison, which should generate a portable
     30  1.1  christos    parser.  I played with adding "#define alloca dont_use_alloca", to
     31  1.1  christos    give an error if the parser generator uses alloca (and thus detect
     32  1.1  christos    unportable parsedate.c's), but that seems to cause as many problems
     33  1.1  christos    as it solves.  */
     34  1.1  christos 
     35  1.1  christos #define yyparse parsedate_yyparse
     36  1.1  christos #define yylex parsedate_yylex
     37  1.1  christos #define yyerror parsedate_yyerror
     38  1.1  christos 
     39  1.1  christos #define EPOCH		1970
     40  1.1  christos #define HOUR(x)		((time_t)(x) * 60)
     41  1.1  christos #define SECSPERDAY	(24L * 60L * 60L)
     42  1.1  christos 
     43  1.1  christos 
     44  1.1  christos /*
     45  1.1  christos **  An entry in the lexical lookup table.
     46  1.1  christos */
     47  1.1  christos typedef struct _TABLE {
     48  1.1  christos     const char	*name;
     49  1.1  christos     int		type;
     50  1.1  christos     time_t	value;
     51  1.1  christos } TABLE;
     52  1.1  christos 
     53  1.1  christos 
     54  1.1  christos /*
     55  1.1  christos **  Daylight-savings mode:  on, off, or not yet known.
     56  1.1  christos */
     57  1.1  christos typedef enum _DSTMODE {
     58  1.1  christos     DSTon, DSToff, DSTmaybe
     59  1.1  christos } DSTMODE;
     60  1.1  christos 
     61  1.1  christos /*
     62  1.1  christos **  Meridian:  am, pm, or 24-hour style.
     63  1.1  christos */
     64  1.1  christos typedef enum _MERIDIAN {
     65  1.1  christos     MERam, MERpm, MER24
     66  1.1  christos } MERIDIAN;
     67  1.1  christos 
     68  1.1  christos 
     69  1.1  christos /*
     70  1.1  christos **  Global variables.  We could get rid of most of these by using a good
     71  1.1  christos **  union as the yacc stack.  (This routine was originally written before
     72  1.1  christos **  yacc had the %union construct.)  Maybe someday; right now we only use
     73  1.1  christos **  the %union very rarely.
     74  1.1  christos */
     75  1.1  christos static const char *yyInput;
     76  1.1  christos static DSTMODE	yyDSTmode;
     77  1.1  christos static time_t	yyDayOrdinal;
     78  1.1  christos static time_t	yyDayNumber;
     79  1.1  christos static int	yyHaveDate;
     80  1.1  christos static int	yyHaveDay;
     81  1.1  christos static int	yyHaveRel;
     82  1.1  christos static int	yyHaveTime;
     83  1.1  christos static int	yyHaveZone;
     84  1.1  christos static time_t	yyTimezone;
     85  1.1  christos static time_t	yyDay;
     86  1.1  christos static time_t	yyHour;
     87  1.1  christos static time_t	yyMinutes;
     88  1.1  christos static time_t	yyMonth;
     89  1.1  christos static time_t	yySeconds;
     90  1.1  christos static time_t	yyYear;
     91  1.1  christos static MERIDIAN	yyMeridian;
     92  1.1  christos static time_t	yyRelMonth;
     93  1.1  christos static time_t	yyRelSeconds;
     94  1.1  christos 
     95  1.1  christos %}
     96  1.1  christos 
     97  1.1  christos %union {
     98  1.1  christos     time_t		Number;
     99  1.1  christos     enum _MERIDIAN	Meridian;
    100  1.1  christos }
    101  1.1  christos 
    102  1.1  christos %token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
    103  1.1  christos %token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
    104  1.1  christos 
    105  1.1  christos %type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
    106  1.1  christos %type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
    107  1.1  christos %type	<Meridian>	tMERIDIAN o_merid
    108  1.1  christos 
    109  1.1  christos %%
    110  1.1  christos 
    111  1.1  christos spec	: /* NULL */
    112  1.1  christos 	| spec item
    113  1.1  christos 	;
    114  1.1  christos 
    115  1.1  christos item	: time {
    116  1.1  christos 	    yyHaveTime++;
    117  1.1  christos 	}
    118  1.1  christos 	| zone {
    119  1.1  christos 	    yyHaveZone++;
    120  1.1  christos 	}
    121  1.1  christos 	| date {
    122  1.1  christos 	    yyHaveDate++;
    123  1.1  christos 	}
    124  1.1  christos 	| day {
    125  1.1  christos 	    yyHaveDay++;
    126  1.1  christos 	}
    127  1.1  christos 	| rel {
    128  1.1  christos 	    yyHaveRel++;
    129  1.1  christos 	}
    130  1.1  christos 	| cvsstamp {
    131  1.1  christos 	    yyHaveTime++;
    132  1.1  christos 	    yyHaveDate++;
    133  1.1  christos 	    yyHaveZone++;
    134  1.1  christos 	}
    135  1.1  christos 	| number
    136  1.1  christos 	;
    137  1.1  christos 
    138  1.1  christos cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
    139  1.1  christos 	    yyYear = $1;
    140  1.1  christos 	    if (yyYear < 100) yyYear += 1900;
    141  1.1  christos 	    yyMonth = $3;
    142  1.1  christos 	    yyDay = $5;
    143  1.1  christos 	    yyHour = $7;
    144  1.1  christos 	    yyMinutes = $9;
    145  1.1  christos 	    yySeconds = $11;
    146  1.1  christos 	    yyDSTmode = DSToff;
    147  1.1  christos 	    yyTimezone = 0;
    148  1.1  christos 	}
    149  1.1  christos 	;
    150  1.1  christos 
    151  1.1  christos time	: tUNUMBER tMERIDIAN {
    152  1.1  christos 	    yyHour = $1;
    153  1.1  christos 	    yyMinutes = 0;
    154  1.1  christos 	    yySeconds = 0;
    155  1.1  christos 	    yyMeridian = $2;
    156  1.1  christos 	}
    157  1.1  christos 	| tUNUMBER ':' tUNUMBER o_merid {
    158  1.1  christos 	    yyHour = $1;
    159  1.1  christos 	    yyMinutes = $3;
    160  1.1  christos 	    yySeconds = 0;
    161  1.1  christos 	    yyMeridian = $4;
    162  1.1  christos 	}
    163  1.1  christos 	| tUNUMBER ':' tUNUMBER tSNUMBER {
    164  1.1  christos 	    yyHour = $1;
    165  1.1  christos 	    yyMinutes = $3;
    166  1.1  christos 	    yyMeridian = MER24;
    167  1.1  christos 	    yyDSTmode = DSToff;
    168  1.1  christos 	    yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
    169  1.1  christos 	}
    170  1.1  christos 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
    171  1.1  christos 	    yyHour = $1;
    172  1.1  christos 	    yyMinutes = $3;
    173  1.1  christos 	    yySeconds = $5;
    174  1.1  christos 	    yyMeridian = $6;
    175  1.1  christos 	}
    176  1.1  christos 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
    177  1.1  christos 	    yyHour = $1;
    178  1.1  christos 	    yyMinutes = $3;
    179  1.1  christos 	    yySeconds = $5;
    180  1.1  christos 	    yyMeridian = MER24;
    181  1.1  christos 	    yyDSTmode = DSToff;
    182  1.1  christos 	    yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
    183  1.1  christos 	}
    184  1.1  christos 	;
    185  1.1  christos 
    186  1.1  christos zone	: tZONE {
    187  1.1  christos 	    yyTimezone = $1;
    188  1.1  christos 	    yyDSTmode = DSToff;
    189  1.1  christos 	}
    190  1.1  christos 	| tDAYZONE {
    191  1.1  christos 	    yyTimezone = $1;
    192  1.1  christos 	    yyDSTmode = DSTon;
    193  1.1  christos 	}
    194  1.1  christos 	|
    195  1.1  christos 	  tZONE tDST {
    196  1.1  christos 	    yyTimezone = $1;
    197  1.1  christos 	    yyDSTmode = DSTon;
    198  1.1  christos 	}
    199  1.1  christos 	;
    200  1.1  christos 
    201  1.1  christos day	: tDAY {
    202  1.1  christos 	    yyDayOrdinal = 1;
    203  1.1  christos 	    yyDayNumber = $1;
    204  1.1  christos 	}
    205  1.1  christos 	| tDAY ',' {
    206  1.1  christos 	    yyDayOrdinal = 1;
    207  1.1  christos 	    yyDayNumber = $1;
    208  1.1  christos 	}
    209  1.1  christos 	| tUNUMBER tDAY {
    210  1.1  christos 	    yyDayOrdinal = $1;
    211  1.1  christos 	    yyDayNumber = $2;
    212  1.1  christos 	}
    213  1.1  christos 	;
    214  1.1  christos 
    215  1.1  christos date	: tUNUMBER '/' tUNUMBER {
    216  1.1  christos 	    yyMonth = $1;
    217  1.1  christos 	    yyDay = $3;
    218  1.1  christos 	}
    219  1.1  christos 	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
    220  1.1  christos 	    if ($1 >= 100) {
    221  1.1  christos 		yyYear = $1;
    222  1.1  christos 		yyMonth = $3;
    223  1.1  christos 		yyDay = $5;
    224  1.1  christos 	    } else {
    225  1.1  christos 		yyMonth = $1;
    226  1.1  christos 		yyDay = $3;
    227  1.1  christos 		yyYear = $5;
    228  1.1  christos 	    }
    229  1.1  christos 	}
    230  1.1  christos 	| tUNUMBER tSNUMBER tSNUMBER {
    231  1.1  christos 	    /* ISO 8601 format.  yyyy-mm-dd.  */
    232  1.1  christos 	    yyYear = $1;
    233  1.1  christos 	    yyMonth = -$2;
    234  1.1  christos 	    yyDay = -$3;
    235  1.1  christos 	}
    236  1.1  christos 	| tUNUMBER tMONTH tSNUMBER {
    237  1.1  christos 	    /* e.g. 17-JUN-1992.  */
    238  1.1  christos 	    yyDay = $1;
    239  1.1  christos 	    yyMonth = $2;
    240  1.1  christos 	    yyYear = -$3;
    241  1.1  christos 	}
    242  1.1  christos 	| tMONTH tUNUMBER {
    243  1.1  christos 	    yyMonth = $1;
    244  1.1  christos 	    yyDay = $2;
    245  1.1  christos 	}
    246  1.1  christos 	| tMONTH tUNUMBER ',' tUNUMBER {
    247  1.1  christos 	    yyMonth = $1;
    248  1.1  christos 	    yyDay = $2;
    249  1.1  christos 	    yyYear = $4;
    250  1.1  christos 	}
    251  1.1  christos 	| tUNUMBER tMONTH {
    252  1.1  christos 	    yyMonth = $2;
    253  1.1  christos 	    yyDay = $1;
    254  1.1  christos 	}
    255  1.1  christos 	| tUNUMBER tMONTH tUNUMBER {
    256  1.1  christos 	    yyMonth = $2;
    257  1.1  christos 	    yyDay = $1;
    258  1.1  christos 	    yyYear = $3;
    259  1.1  christos 	}
    260  1.1  christos 	;
    261  1.1  christos 
    262  1.1  christos rel	: relunit tAGO {
    263  1.1  christos 	    yyRelSeconds = -yyRelSeconds;
    264  1.1  christos 	    yyRelMonth = -yyRelMonth;
    265  1.1  christos 	}
    266  1.1  christos 	| relunit
    267  1.1  christos 	;
    268  1.1  christos 
    269  1.1  christos relunit	: tUNUMBER tMINUTE_UNIT {
    270  1.1  christos 	    yyRelSeconds += $1 * $2 * 60L;
    271  1.1  christos 	}
    272  1.1  christos 	| tSNUMBER tMINUTE_UNIT {
    273  1.1  christos 	    yyRelSeconds += $1 * $2 * 60L;
    274  1.1  christos 	}
    275  1.1  christos 	| tMINUTE_UNIT {
    276  1.1  christos 	    yyRelSeconds += $1 * 60L;
    277  1.1  christos 	}
    278  1.1  christos 	| tSNUMBER tSEC_UNIT {
    279  1.1  christos 	    yyRelSeconds += $1;
    280  1.1  christos 	}
    281  1.1  christos 	| tUNUMBER tSEC_UNIT {
    282  1.1  christos 	    yyRelSeconds += $1;
    283  1.1  christos 	}
    284  1.1  christos 	| tSEC_UNIT {
    285  1.1  christos 	    yyRelSeconds++;
    286  1.1  christos 	}
    287  1.1  christos 	| tSNUMBER tMONTH_UNIT {
    288  1.1  christos 	    yyRelMonth += $1 * $2;
    289  1.1  christos 	}
    290  1.1  christos 	| tUNUMBER tMONTH_UNIT {
    291  1.1  christos 	    yyRelMonth += $1 * $2;
    292  1.1  christos 	}
    293  1.1  christos 	| tMONTH_UNIT {
    294  1.1  christos 	    yyRelMonth += $1;
    295  1.1  christos 	}
    296  1.1  christos 	;
    297  1.1  christos 
    298  1.1  christos number	: tUNUMBER {
    299  1.1  christos 	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
    300  1.1  christos 		yyYear = $1;
    301  1.1  christos 	    else {
    302  1.1  christos 		if($1>10000) {
    303  1.1  christos 		    yyHaveDate++;
    304  1.1  christos 		    yyDay= ($1)%100;
    305  1.1  christos 		    yyMonth= ($1/100)%100;
    306  1.1  christos 		    yyYear = $1/10000;
    307  1.1  christos 		}
    308  1.1  christos 		else {
    309  1.1  christos 		    yyHaveTime++;
    310  1.1  christos 		    if ($1 < 100) {
    311  1.1  christos 			yyHour = $1;
    312  1.1  christos 			yyMinutes = 0;
    313  1.1  christos 		    }
    314  1.1  christos 		    else {
    315  1.1  christos 		    	yyHour = $1 / 100;
    316  1.1  christos 		    	yyMinutes = $1 % 100;
    317  1.1  christos 		    }
    318  1.1  christos 		    yySeconds = 0;
    319  1.1  christos 		    yyMeridian = MER24;
    320  1.1  christos 	        }
    321  1.1  christos 	    }
    322  1.1  christos 	}
    323  1.1  christos 	;
    324  1.1  christos 
    325  1.1  christos o_merid	: /* NULL */ {
    326  1.1  christos 	    $$ = MER24;
    327  1.1  christos 	}
    328  1.1  christos 	| tMERIDIAN {
    329  1.1  christos 	    $$ = $1;
    330  1.1  christos 	}
    331  1.1  christos 	;
    332  1.1  christos 
    333  1.1  christos %%
    334  1.1  christos 
    335  1.1  christos /* Month and day table. */
    336  1.1  christos static TABLE const MonthDayTable[] = {
    337  1.1  christos     { "january",	tMONTH,  1 },
    338  1.1  christos     { "february",	tMONTH,  2 },
    339  1.1  christos     { "march",		tMONTH,  3 },
    340  1.1  christos     { "april",		tMONTH,  4 },
    341  1.1  christos     { "may",		tMONTH,  5 },
    342  1.1  christos     { "june",		tMONTH,  6 },
    343  1.1  christos     { "july",		tMONTH,  7 },
    344  1.1  christos     { "august",		tMONTH,  8 },
    345  1.1  christos     { "september",	tMONTH,  9 },
    346  1.1  christos     { "sept",		tMONTH,  9 },
    347  1.1  christos     { "october",	tMONTH, 10 },
    348  1.1  christos     { "november",	tMONTH, 11 },
    349  1.1  christos     { "december",	tMONTH, 12 },
    350  1.1  christos     { "sunday",		tDAY, 0 },
    351  1.1  christos     { "monday",		tDAY, 1 },
    352  1.1  christos     { "tuesday",	tDAY, 2 },
    353  1.1  christos     { "tues",		tDAY, 2 },
    354  1.1  christos     { "wednesday",	tDAY, 3 },
    355  1.1  christos     { "wednes",		tDAY, 3 },
    356  1.1  christos     { "thursday",	tDAY, 4 },
    357  1.1  christos     { "thur",		tDAY, 4 },
    358  1.1  christos     { "thurs",		tDAY, 4 },
    359  1.1  christos     { "friday",		tDAY, 5 },
    360  1.1  christos     { "saturday",	tDAY, 6 },
    361  1.1  christos     { NULL,		0,    0 }
    362  1.1  christos };
    363  1.1  christos 
    364  1.1  christos /* Time units table. */
    365  1.1  christos static TABLE const UnitsTable[] = {
    366  1.1  christos     { "year",		tMONTH_UNIT,	12 },
    367  1.1  christos     { "month",		tMONTH_UNIT,	1 },
    368  1.1  christos     { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
    369  1.1  christos     { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
    370  1.1  christos     { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
    371  1.1  christos     { "hour",		tMINUTE_UNIT,	60 },
    372  1.1  christos     { "minute",		tMINUTE_UNIT,	1 },
    373  1.1  christos     { "min",		tMINUTE_UNIT,	1 },
    374  1.1  christos     { "second",		tSEC_UNIT,	1 },
    375  1.1  christos     { "sec",		tSEC_UNIT,	1 },
    376  1.1  christos     { NULL,		0,		0 }
    377  1.1  christos };
    378  1.1  christos 
    379  1.1  christos /* Assorted relative-time words. */
    380  1.1  christos static TABLE const OtherTable[] = {
    381  1.1  christos     { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
    382  1.1  christos     { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
    383  1.1  christos     { "today",		tMINUTE_UNIT,	0 },
    384  1.1  christos     { "now",		tMINUTE_UNIT,	0 },
    385  1.1  christos     { "last",		tUNUMBER,	-1 },
    386  1.1  christos     { "this",		tMINUTE_UNIT,	0 },
    387  1.1  christos     { "next",		tUNUMBER,	2 },
    388  1.1  christos     { "first",		tUNUMBER,	1 },
    389  1.1  christos /*  { "second",		tUNUMBER,	2 }, */
    390  1.1  christos     { "third",		tUNUMBER,	3 },
    391  1.1  christos     { "fourth",		tUNUMBER,	4 },
    392  1.1  christos     { "fifth",		tUNUMBER,	5 },
    393  1.1  christos     { "sixth",		tUNUMBER,	6 },
    394  1.1  christos     { "seventh",	tUNUMBER,	7 },
    395  1.1  christos     { "eighth",		tUNUMBER,	8 },
    396  1.1  christos     { "ninth",		tUNUMBER,	9 },
    397  1.1  christos     { "tenth",		tUNUMBER,	10 },
    398  1.1  christos     { "eleventh",	tUNUMBER,	11 },
    399  1.1  christos     { "twelfth",	tUNUMBER,	12 },
    400  1.1  christos     { "ago",		tAGO,	1 },
    401  1.1  christos     { NULL,		0,	0 }
    402  1.1  christos };
    403  1.1  christos 
    404  1.1  christos /* The timezone table. */
    405  1.1  christos /* Some of these are commented out because a time_t can't store a float. */
    406  1.1  christos static TABLE const TimezoneTable[] = {
    407  1.1  christos     { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
    408  1.1  christos     { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
    409  1.1  christos     { "utc",	tZONE,     HOUR( 0) },
    410  1.1  christos     { "wet",	tZONE,     HOUR( 0) },	/* Western European */
    411  1.1  christos     { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
    412  1.1  christos     { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
    413  1.1  christos     { "at",	tZONE,     HOUR( 2) },	/* Azores */
    414  1.1  christos #if	0
    415  1.1  christos     /* For completeness.  BST is also British Summer, and GST is
    416  1.1  christos      * also Guam Standard. */
    417  1.1  christos     { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
    418  1.1  christos     { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
    419  1.1  christos #endif
    420  1.1  christos #if 0
    421  1.1  christos     { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
    422  1.1  christos     { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
    423  1.1  christos     { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
    424  1.1  christos #endif
    425  1.1  christos     { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
    426  1.1  christos     { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
    427  1.1  christos     { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
    428  1.1  christos     { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
    429  1.1  christos     { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
    430  1.1  christos     { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
    431  1.1  christos     { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
    432  1.1  christos     { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
    433  1.1  christos     { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
    434  1.1  christos     { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
    435  1.1  christos     { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
    436  1.1  christos     { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
    437  1.1  christos     { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
    438  1.1  christos     { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
    439  1.1  christos     { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
    440  1.1  christos     { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
    441  1.1  christos     { "nt",	tZONE,     HOUR(11) },	/* Nome */
    442  1.1  christos     { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
    443  1.1  christos     { "cet",	tZONE,     -HOUR(1) },	/* Central European */
    444  1.1  christos     { "met",	tZONE,     -HOUR(1) },	/* Middle European */
    445  1.1  christos     { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
    446  1.1  christos     { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
    447  1.1  christos     { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
    448  1.1  christos     { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
    449  1.1  christos     { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
    450  1.1  christos     { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
    451  1.1  christos     { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
    452  1.1  christos     { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
    453  1.1  christos #if 0
    454  1.1  christos     { "it",	tZONE,     -HOUR(3.5) },/* Iran */
    455  1.1  christos #endif
    456  1.1  christos     { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
    457  1.1  christos     { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
    458  1.1  christos #if 0
    459  1.1  christos     { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
    460  1.1  christos #endif
    461  1.1  christos     { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
    462  1.1  christos #if	0
    463  1.1  christos     /* For completeness.  NST is also Newfoundland Stanard, and SST is
    464  1.1  christos      * also Swedish Summer. */
    465  1.1  christos     { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
    466  1.1  christos     { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
    467  1.1  christos #endif	/* 0 */
    468  1.1  christos     { "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
    469  1.1  christos     { "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
    470  1.1  christos #if 0
    471  1.1  christos     { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
    472  1.1  christos #endif
    473  1.1  christos     { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
    474  1.1  christos     { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
    475  1.1  christos #if 0
    476  1.1  christos     { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
    477  1.1  christos     { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
    478  1.1  christos #endif
    479  1.1  christos     { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
    480  1.1  christos     { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
    481  1.1  christos     { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
    482  1.1  christos     { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
    483  1.1  christos     { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
    484  1.1  christos     { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
    485  1.1  christos     { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
    486  1.1  christos     {  NULL,	0,	    0 }
    487  1.1  christos };
    488  1.1  christos 
    489  1.1  christos /* Military timezone table. */
    490  1.1  christos static TABLE const MilitaryTable[] = {
    491  1.1  christos     { "a",	tZONE,	HOUR(  1) },
    492  1.1  christos     { "b",	tZONE,	HOUR(  2) },
    493  1.1  christos     { "c",	tZONE,	HOUR(  3) },
    494  1.1  christos     { "d",	tZONE,	HOUR(  4) },
    495  1.1  christos     { "e",	tZONE,	HOUR(  5) },
    496  1.1  christos     { "f",	tZONE,	HOUR(  6) },
    497  1.1  christos     { "g",	tZONE,	HOUR(  7) },
    498  1.1  christos     { "h",	tZONE,	HOUR(  8) },
    499  1.1  christos     { "i",	tZONE,	HOUR(  9) },
    500  1.1  christos     { "k",	tZONE,	HOUR( 10) },
    501  1.1  christos     { "l",	tZONE,	HOUR( 11) },
    502  1.1  christos     { "m",	tZONE,	HOUR( 12) },
    503  1.1  christos     { "n",	tZONE,	HOUR(- 1) },
    504  1.1  christos     { "o",	tZONE,	HOUR(- 2) },
    505  1.1  christos     { "p",	tZONE,	HOUR(- 3) },
    506  1.1  christos     { "q",	tZONE,	HOUR(- 4) },
    507  1.1  christos     { "r",	tZONE,	HOUR(- 5) },
    508  1.1  christos     { "s",	tZONE,	HOUR(- 6) },
    509  1.1  christos     { "t",	tZONE,	HOUR(- 7) },
    510  1.1  christos     { "u",	tZONE,	HOUR(- 8) },
    511  1.1  christos     { "v",	tZONE,	HOUR(- 9) },
    512  1.1  christos     { "w",	tZONE,	HOUR(-10) },
    513  1.1  christos     { "x",	tZONE,	HOUR(-11) },
    514  1.1  christos     { "y",	tZONE,	HOUR(-12) },
    515  1.1  christos     { "z",	tZONE,	HOUR(  0) },
    516  1.1  christos     { NULL,	0,	0 }
    517  1.1  christos };
    518  1.1  christos 
    519  1.1  christos 
    520  1.1  christos 
    522  1.1  christos 
    523  1.1  christos /* ARGSUSED */
    524  1.2  christos static int
    525  1.1  christos yyerror(const char *s __unused)
    526  1.1  christos {
    527  1.1  christos   return 0;
    528  1.1  christos }
    529  1.1  christos 
    530  1.1  christos 
    531  1.1  christos static time_t
    532  1.1  christos ToSeconds(
    533  1.1  christos     time_t	Hours,
    534  1.1  christos     time_t	Minutes,
    535  1.1  christos     time_t	Seconds,
    536  1.1  christos     MERIDIAN	Meridian
    537  1.1  christos )
    538  1.1  christos {
    539  1.1  christos     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
    540  1.1  christos 	return -1;
    541  1.1  christos     switch (Meridian) {
    542  1.1  christos     case MER24:
    543  1.1  christos 	if (Hours < 0 || Hours > 23)
    544  1.1  christos 	    return -1;
    545  1.1  christos 	return (Hours * 60L + Minutes) * 60L + Seconds;
    546  1.1  christos     case MERam:
    547  1.1  christos 	if (Hours < 1 || Hours > 12)
    548  1.1  christos 	    return -1;
    549  1.1  christos 	if (Hours == 12)
    550  1.1  christos 	    Hours = 0;
    551  1.1  christos 	return (Hours * 60L + Minutes) * 60L + Seconds;
    552  1.1  christos     case MERpm:
    553  1.1  christos 	if (Hours < 1 || Hours > 12)
    554  1.1  christos 	    return -1;
    555  1.1  christos 	if (Hours == 12)
    556  1.1  christos 	    Hours = 0;
    557  1.1  christos 	return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
    558  1.1  christos     default:
    559  1.1  christos 	abort ();
    560  1.1  christos     }
    561  1.1  christos     /* NOTREACHED */
    562  1.1  christos }
    563  1.1  christos 
    564  1.1  christos 
    565  1.1  christos /* Year is either
    566  1.1  christos    * A negative number, which means to use its absolute value (why?)
    567  1.1  christos    * A number from 0 to 99, which means a year from 1900 to 1999, or
    568  1.1  christos    * The actual year (>=100).  */
    569  1.1  christos static time_t
    570  1.1  christos Convert(
    571  1.1  christos     time_t	Month,
    572  1.1  christos     time_t	Day,
    573  1.1  christos     time_t	Year,
    574  1.1  christos     time_t	Hours,
    575  1.1  christos     time_t	Minutes,
    576  1.1  christos     time_t	Seconds,
    577  1.1  christos     MERIDIAN	Meridian,
    578  1.1  christos     DSTMODE	DSTmode
    579  1.1  christos )
    580  1.1  christos {
    581  1.1  christos     static int DaysInMonth[12] = {
    582  1.1  christos 	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    583  1.1  christos     };
    584  1.1  christos     time_t	tod;
    585  1.1  christos     time_t	Julian;
    586  1.1  christos     int		i;
    587  1.1  christos 
    588  1.1  christos     if (Year < 0)
    589  1.1  christos 	Year = -Year;
    590  1.1  christos     if (Year < 69)
    591  1.1  christos 	Year += 2000;
    592  1.1  christos     else if (Year < 100)
    593  1.1  christos 	Year += 1900;
    594  1.1  christos     DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
    595  1.1  christos 		    ? 29 : 28;
    596  1.1  christos     /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
    597  1.1  christos        I'm too lazy to try to check for time_t overflow in another way.  */
    598  1.1  christos     if (Year < EPOCH || Year > 2038
    599  1.1  christos      || Month < 1 || Month > 12
    600  1.1  christos      /* Lint fluff:  "conversion from long may lose accuracy" */
    601  1.1  christos      || Day < 1 || Day > DaysInMonth[(int)--Month])
    602  1.1  christos 	/* FIXME:
    603  1.1  christos 	 * It would be nice to set a global error string here.
    604  1.1  christos 	 * "February 30 is not a valid date" is much more informative than
    605  1.1  christos 	 * "Can't parse date/time: 100 months" when the user input was
    606  1.1  christos 	 * "100 months" and addition resolved that to February 30, for
    607  1.1  christos 	 * example.  See rcs2-7 in src/sanity.sh for more. */
    608  1.1  christos 	return -1;
    609  1.1  christos 
    610  1.1  christos     for (Julian = Day - 1, i = 0; i < Month; i++)
    611  1.1  christos 	Julian += DaysInMonth[i];
    612  1.1  christos     for (i = EPOCH; i < Year; i++)
    613  1.1  christos 	Julian += 365 + (i % 4 == 0);
    614  1.1  christos     Julian *= SECSPERDAY;
    615  1.1  christos     Julian += yyTimezone * 60L;
    616  1.1  christos     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
    617  1.1  christos 	return -1;
    618  1.1  christos     Julian += tod;
    619  1.1  christos     if (DSTmode == DSTon
    620  1.1  christos      || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
    621  1.1  christos 	Julian -= 60 * 60;
    622  1.1  christos     return Julian;
    623  1.1  christos }
    624  1.1  christos 
    625  1.1  christos 
    626  1.1  christos static time_t
    627  1.1  christos DSTcorrect(
    628  1.1  christos     time_t	Start,
    629  1.1  christos     time_t	Future
    630  1.1  christos )
    631  1.1  christos {
    632  1.1  christos     time_t	StartDay;
    633  1.1  christos     time_t	FutureDay;
    634  1.1  christos 
    635  1.1  christos     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
    636  1.1  christos     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
    637  1.1  christos     return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
    638  1.1  christos }
    639  1.1  christos 
    640  1.1  christos 
    641  1.1  christos static time_t
    642  1.1  christos RelativeDate(
    643  1.1  christos     time_t	Start,
    644  1.1  christos     time_t	DayOrdinal,
    645  1.1  christos     time_t	DayNumber
    646  1.1  christos )
    647  1.1  christos {
    648  1.1  christos     struct tm	*tm;
    649  1.1  christos     time_t	now;
    650  1.1  christos 
    651  1.1  christos     now = Start;
    652  1.1  christos     tm = localtime(&now);
    653  1.1  christos     now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
    654  1.1  christos     now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
    655  1.1  christos     return DSTcorrect(Start, now);
    656  1.1  christos }
    657  1.1  christos 
    658  1.1  christos 
    659  1.1  christos static time_t
    660  1.1  christos RelativeMonth(
    661  1.1  christos     time_t	Start,
    662  1.1  christos     time_t	RelMonth
    663  1.1  christos )
    664  1.1  christos {
    665  1.1  christos     struct tm	*tm;
    666  1.1  christos     time_t	Month;
    667  1.1  christos     time_t	Year;
    668  1.1  christos 
    669  1.1  christos     if (RelMonth == 0)
    670  1.1  christos 	return 0;
    671  1.1  christos     tm = localtime(&Start);
    672  1.1  christos     Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
    673  1.1  christos     Year = Month / 12;
    674  1.1  christos     Month = Month % 12 + 1;
    675  1.1  christos     return DSTcorrect(Start,
    676  1.1  christos 	    Convert(Month, (time_t)tm->tm_mday, Year,
    677  1.1  christos 		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
    678  1.1  christos 		MER24, DSTmaybe));
    679  1.1  christos }
    680  1.1  christos 
    681  1.1  christos 
    682  1.1  christos static int
    683  1.1  christos LookupWord(char *buff)
    684  1.1  christos {
    685  1.1  christos     register char	*p;
    686  1.1  christos     register char	*q;
    687  1.1  christos     register const TABLE	*tp;
    688  1.1  christos     int			i;
    689  1.1  christos     int			abbrev;
    690  1.1  christos 
    691  1.1  christos     /* Make it lowercase. */
    692  1.1  christos     for (p = buff; *p; p++)
    693  1.1  christos 	if (isupper((unsigned char)*p))
    694  1.1  christos 	    *p = tolower((unsigned char)*p);
    695  1.1  christos 
    696  1.1  christos     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
    697  1.1  christos 	yylval.Meridian = MERam;
    698  1.1  christos 	return tMERIDIAN;
    699  1.1  christos     }
    700  1.1  christos     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
    701  1.1  christos 	yylval.Meridian = MERpm;
    702  1.1  christos 	return tMERIDIAN;
    703  1.1  christos     }
    704  1.1  christos 
    705  1.1  christos     /* See if we have an abbreviation for a month. */
    706  1.1  christos     if (strlen(buff) == 3)
    707  1.1  christos 	abbrev = 1;
    708  1.1  christos     else if (strlen(buff) == 4 && buff[3] == '.') {
    709  1.1  christos 	abbrev = 1;
    710  1.1  christos 	buff[3] = '\0';
    711  1.1  christos     }
    712  1.1  christos     else
    713  1.1  christos 	abbrev = 0;
    714  1.1  christos 
    715  1.1  christos     for (tp = MonthDayTable; tp->name; tp++) {
    716  1.1  christos 	if (abbrev) {
    717  1.1  christos 	    if (strncmp(buff, tp->name, 3) == 0) {
    718  1.1  christos 		yylval.Number = tp->value;
    719  1.1  christos 		return tp->type;
    720  1.1  christos 	    }
    721  1.1  christos 	}
    722  1.1  christos 	else if (strcmp(buff, tp->name) == 0) {
    723  1.1  christos 	    yylval.Number = tp->value;
    724  1.1  christos 	    return tp->type;
    725  1.1  christos 	}
    726  1.1  christos     }
    727  1.1  christos 
    728  1.1  christos     for (tp = TimezoneTable; tp->name; tp++)
    729  1.1  christos 	if (strcmp(buff, tp->name) == 0) {
    730  1.1  christos 	    yylval.Number = tp->value;
    731  1.1  christos 	    return tp->type;
    732  1.1  christos 	}
    733  1.1  christos 
    734  1.1  christos     if (strcmp(buff, "dst") == 0)
    735  1.1  christos 	return tDST;
    736  1.1  christos 
    737  1.1  christos     for (tp = UnitsTable; tp->name; tp++)
    738  1.1  christos 	if (strcmp(buff, tp->name) == 0) {
    739  1.1  christos 	    yylval.Number = tp->value;
    740  1.1  christos 	    return tp->type;
    741  1.1  christos 	}
    742  1.1  christos 
    743  1.1  christos     /* Strip off any plural and try the units table again. */
    744  1.1  christos     i = strlen(buff) - 1;
    745  1.1  christos     if (buff[i] == 's') {
    746  1.1  christos 	buff[i] = '\0';
    747  1.1  christos 	for (tp = UnitsTable; tp->name; tp++)
    748  1.1  christos 	    if (strcmp(buff, tp->name) == 0) {
    749  1.1  christos 		yylval.Number = tp->value;
    750  1.1  christos 		return tp->type;
    751  1.1  christos 	    }
    752  1.1  christos 	buff[i] = 's';		/* Put back for "this" in OtherTable. */
    753  1.1  christos     }
    754  1.1  christos 
    755  1.1  christos     for (tp = OtherTable; tp->name; tp++)
    756  1.1  christos 	if (strcmp(buff, tp->name) == 0) {
    757  1.1  christos 	    yylval.Number = tp->value;
    758  1.1  christos 	    return tp->type;
    759  1.1  christos 	}
    760  1.1  christos 
    761  1.1  christos     /* Military timezones. */
    762  1.1  christos     if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
    763  1.1  christos 	for (tp = MilitaryTable; tp->name; tp++)
    764  1.1  christos 	    if (strcmp(buff, tp->name) == 0) {
    765  1.1  christos 		yylval.Number = tp->value;
    766  1.1  christos 		return tp->type;
    767  1.1  christos 	    }
    768  1.1  christos     }
    769  1.1  christos 
    770  1.1  christos     /* Drop out any periods and try the timezone table again. */
    771  1.1  christos     for (i = 0, p = q = buff; *q; q++)
    772  1.1  christos 	if (*q != '.')
    773  1.1  christos 	    *p++ = *q;
    774  1.1  christos 	else
    775  1.1  christos 	    i++;
    776  1.1  christos     *p = '\0';
    777  1.1  christos     if (i)
    778  1.1  christos 	for (tp = TimezoneTable; tp->name; tp++)
    779  1.1  christos 	    if (strcmp(buff, tp->name) == 0) {
    780  1.1  christos 		yylval.Number = tp->value;
    781  1.1  christos 		return tp->type;
    782  1.1  christos 	    }
    783  1.1  christos 
    784  1.1  christos     return tID;
    785  1.1  christos }
    786  1.1  christos 
    787  1.1  christos 
    788  1.1  christos static int
    789  1.1  christos yylex(void)
    790  1.1  christos {
    791  1.1  christos     register char	c;
    792  1.1  christos     register char	*p;
    793  1.1  christos     char		buff[20];
    794  1.1  christos     int			Count;
    795  1.1  christos     int			sign;
    796  1.1  christos 
    797  1.1  christos     for ( ; ; ) {
    798  1.1  christos 	while (isspace((unsigned char)*yyInput))
    799  1.1  christos 	    yyInput++;
    800  1.1  christos 
    801  1.1  christos 	if (isdigit((unsigned char)(c = *yyInput)) || c == '-' || c == '+') {
    802  1.1  christos 	    if (c == '-' || c == '+') {
    803  1.1  christos 		sign = c == '-' ? -1 : 1;
    804  1.1  christos 		if (!isdigit((unsigned char)*++yyInput))
    805  1.1  christos 		    /* skip the '-' sign */
    806  1.1  christos 		    continue;
    807  1.1  christos 	    }
    808  1.1  christos 	    else
    809  1.1  christos 		sign = 0;
    810  1.1  christos 	    for (yylval.Number = 0; isdigit((unsigned char)(c = *yyInput++)); )
    811  1.1  christos 		yylval.Number = 10 * yylval.Number + c - '0';
    812  1.1  christos 	    yyInput--;
    813  1.1  christos 	    if (sign < 0)
    814  1.1  christos 		yylval.Number = -yylval.Number;
    815  1.1  christos 	    return sign ? tSNUMBER : tUNUMBER;
    816  1.1  christos 	}
    817  1.1  christos 	if (isalpha((unsigned char)c)) {
    818  1.1  christos 	    for (p = buff; isalpha((unsigned char)(c = *yyInput++)) || c == '.'; )
    819  1.1  christos 		if (p < &buff[sizeof buff - 1])
    820  1.1  christos 		    *p++ = c;
    821  1.1  christos 	    *p = '\0';
    822  1.1  christos 	    yyInput--;
    823  1.1  christos 	    return LookupWord(buff);
    824  1.1  christos 	}
    825  1.1  christos 	if (c != '(')
    826  1.1  christos 	    return *yyInput++;
    827  1.1  christos 	Count = 0;
    828  1.1  christos 	do {
    829  1.1  christos 	    c = *yyInput++;
    830  1.1  christos 	    if (c == '\0')
    831  1.1  christos 		return c;
    832  1.1  christos 	    if (c == '(')
    833  1.1  christos 		Count++;
    834  1.1  christos 	    else if (c == ')')
    835  1.1  christos 		Count--;
    836  1.1  christos 	} while (Count > 0);
    837  1.1  christos     }
    838  1.1  christos }
    839  1.1  christos 
    840  1.1  christos #define TM_YEAR_ORIGIN 1900
    841  1.1  christos 
    842  1.1  christos /* Yield A - B, measured in seconds.  */
    843  1.1  christos static long
    844  1.1  christos difftm (struct tm *a, struct tm *b)
    845  1.1  christos {
    846  1.1  christos   int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
    847  1.1  christos   int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
    848  1.1  christos   int days = (
    849  1.1  christos 	      /* difference in day of year */
    850  1.1  christos 	      a->tm_yday - b->tm_yday
    851  1.1  christos 	      /* + intervening leap days */
    852  1.1  christos 	      +  ((ay >> 2) - (by >> 2))
    853  1.1  christos 	      -  (ay/100 - by/100)
    854  1.1  christos 	      +  ((ay/100 >> 2) - (by/100 >> 2))
    855  1.1  christos 	      /* + difference in years * 365 */
    856  1.1  christos 	      +  (long)(ay-by) * 365
    857  1.1  christos 	      );
    858  1.1  christos   return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
    859  1.1  christos 	      + (a->tm_min - b->tm_min))
    860  1.1  christos 	  + (a->tm_sec - b->tm_sec));
    861  1.1  christos }
    862  1.1  christos 
    863  1.1  christos time_t
    864  1.1  christos parsedate(const char *p, const time_t *now, const int *zone)
    865  1.1  christos {
    866  1.1  christos     struct tm gmt, local, *gmt_ptr, *tm;
    867  1.1  christos     time_t		nowt;
    868  1.1  christos     int			zonet;
    869  1.1  christos     time_t		Start;
    870  1.1  christos     time_t		tod;
    871  1.1  christos 
    872  1.1  christos     yyInput = p;
    873  1.1  christos     if (now == NULL || zone == NULL) {
    874  1.1  christos         now = &nowt;
    875  1.1  christos 	zone = &zonet;
    876  1.1  christos 	(void)time(&nowt);
    877  1.1  christos 
    878  1.1  christos 	gmt_ptr = gmtime_r(now, &gmt);
    879  1.1  christos 	if ((tm = localtime_r(now, &local)) == NULL)
    880  1.1  christos 	    return -1;
    881  1.1  christos 
    882  1.1  christos 	if (gmt_ptr != NULL)
    883  1.1  christos 	    zonet = difftm(&gmt, &local) / 60;
    884  1.1  christos 	else
    885  1.1  christos 	    /* We are on a system like VMS, where the system clock is
    886  1.1  christos 	       in local time and the system has no concept of timezones.
    887  1.1  christos 	       Hopefully we can fake this out (for the case in which the
    888  1.1  christos 	       user specifies no timezone) by just saying the timezone
    889  1.1  christos 	       is zero.  */
    890  1.1  christos 	    zonet = 0;
    891  1.1  christos 
    892  1.1  christos 	if (local.tm_isdst)
    893  1.1  christos 	    zonet += 60;
    894  1.1  christos     } else {
    895  1.1  christos 	if ((tm = localtime_r(now, &local)) == NULL)
    896  1.1  christos 	    return -1;
    897  1.1  christos     }
    898  1.1  christos     yyYear = tm->tm_year + 1900;
    899  1.1  christos     yyMonth = tm->tm_mon + 1;
    900  1.1  christos     yyDay = tm->tm_mday;
    901  1.1  christos     yyTimezone = *zone;
    902  1.1  christos     yyDSTmode = DSTmaybe;
    903  1.1  christos     yyHour = 0;
    904  1.1  christos     yyMinutes = 0;
    905  1.1  christos     yySeconds = 0;
    906  1.1  christos     yyMeridian = MER24;
    907  1.1  christos     yyRelSeconds = 0;
    908  1.1  christos     yyRelMonth = 0;
    909  1.1  christos     yyHaveDate = 0;
    910  1.1  christos     yyHaveDay = 0;
    911  1.1  christos     yyHaveRel = 0;
    912  1.1  christos     yyHaveTime = 0;
    913  1.1  christos     yyHaveZone = 0;
    914  1.1  christos 
    915  1.1  christos     if (yyparse()
    916  1.1  christos      || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
    917  1.1  christos 	return -1;
    918  1.1  christos 
    919  1.1  christos     if (yyHaveDate || yyHaveTime || yyHaveDay) {
    920  1.1  christos 	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
    921  1.1  christos 		    yyMeridian, yyDSTmode);
    922  1.1  christos 	if (Start < 0)
    923  1.1  christos 	    return -1;
    924  1.1  christos     }
    925  1.1  christos     else {
    926  1.1  christos 	Start = *now;
    927  1.1  christos 	if (!yyHaveRel)
    928  1.1  christos 	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
    929  1.1  christos     }
    930  1.1  christos 
    931  1.1  christos     Start += yyRelSeconds;
    932  1.1  christos     Start += RelativeMonth(Start, yyRelMonth);
    933  1.1  christos 
    934  1.1  christos     if (yyHaveDay && !yyHaveDate) {
    935  1.1  christos 	tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
    936  1.1  christos 	Start += tod;
    937  1.1  christos     }
    938  1.1  christos 
    939  1.1  christos     /* Have to do *something* with a legitimate -1 so it's distinguishable
    940  1.1  christos      * from the error return value.  (Alternately could set errno on error.) */
    941  1.1  christos     return Start == -1 ? 0 : Start;
    942  1.1  christos }
    943  1.1  christos 
    944  1.1  christos 
    945  1.1  christos #if	defined(TEST)
    946  1.1  christos 
    947  1.1  christos /* ARGSUSED */
    948  1.1  christos int
    949  1.1  christos main(ac, av)
    950  1.1  christos     int		ac;
    951  1.1  christos     char	*av[];
    952  1.1  christos {
    953  1.1  christos     char	buff[128];
    954  1.1  christos     time_t	d;
    955  1.1  christos 
    956  1.1  christos     (void)printf("Enter date, or blank line to exit.\n\t> ");
    957  1.1  christos     (void)fflush(stdout);
    958  1.1  christos     while (gets(buff) && buff[0]) {
    959  1.1  christos 	d = parsedate(buff, NULL, NULL);
    960  1.1  christos 	if (d == -1)
    961  1.1  christos 	    (void)printf("Bad format - couldn't convert.\n");
    962  1.1  christos 	else
    963  1.1  christos 	    (void)printf("%s", ctime(&d));
    964  1.1  christos 	(void)printf("\t> ");
    965  1.1  christos 	(void)fflush(stdout);
    966  1.1  christos     }
    967  1.1  christos     exit(0);
    968  1.1  christos     /* NOTREACHED */
    969  1.1  christos }
    970                #endif	/* defined(TEST) */
    971