getdate.y revision 94630
169587Sgreen%{
269587Sgreen/*
369587Sgreen**  Originally written by Steven M. Bellovin <smb@research.att.com> while
469587Sgreen**  at the University of North Carolina at Chapel Hill.  Later tweaked by
569587Sgreen**  a couple of people on Usenet.  Completely overhauled by Rich $alz
669587Sgreen**  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
769587Sgreen**
869587Sgreen**  This grammar has 10 shift/reduce conflicts.
969587Sgreen**
1069587Sgreen**  This code is in the public domain and has no copyright.
1169587Sgreen*/
1269587Sgreen/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
1369587Sgreen/* SUPPRESS 288 on yyerrlab *//* Label unused */
1469587Sgreen
1569587Sgreen/* $FreeBSD: head/usr.bin/find/getdate.y 94630 2002-04-14 01:30:20Z obrien $ */
1669587Sgreen
1769587Sgreen#ifdef HAVE_CONFIG_H
1869587Sgreen#if defined (emacs) || defined (CONFIG_BROKETS)
1969587Sgreen#include <config.h>
2069587Sgreen#else
2169587Sgreen#include "config.h"
2269587Sgreen#endif
2369587Sgreen#endif
2469587Sgreen
2569587Sgreen/* Since the code of getdate.y is not included in the Emacs executable
26106121Sdes   itself, there is no need to #define static in this file.  Even if
2769587Sgreen   the code were included in the Emacs executable, it probably
2869587Sgreen   wouldn't do any harm to #undef it here; this will only cause
2969587Sgreen   problems if we try to write to a static variable, which I don't
3069587Sgreen   think this code needs to do.  */
3169587Sgreen#ifdef emacs
3269587Sgreen#undef static
3369587Sgreen#endif
3469587Sgreen
3576259Sgreen#include <stdio.h>
3669587Sgreen#include <ctype.h>
3769587Sgreen
3876259Sgreen/* The code at the top of get_date which figures out the offset of the
3976259Sgreen   current time zone checks various CPP symbols to see if special
4076259Sgreen   tricks are need, but defaults to using the gettimeofday system call.
4169587Sgreen   Include <sys/time.h> if that will be used.  */
4292555Sdes
4369587Sgreen#if	defined(vms)
4469587Sgreen# include <types.h>
4569587Sgreen#else /* defined(vms) */
4669587Sgreen# include <sys/types.h>
4769587Sgreen/*# include "xtime.h"*/
4869587Sgreen# include <sys/time.h>
4969587Sgreen# include <sys/timeb.h>
5069587Sgreen#endif	/* !defined(vms) */
5169587Sgreen
5269587Sgreen#if defined (STDC_HEADERS) || defined (USG)
53106121Sdes#include <string.h>
5469587Sgreen#endif
5569587Sgreen
5669587Sgreen/* Some old versions of bison generate parsers that use bcopy.
5769587Sgreen   That loses on systems that don't provide the function, so we have
5869587Sgreen   to redefine it here.  */
5969587Sgreen#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
6069587Sgreen#define bcopy(from, to, len) memcpy ((to), (from), (len))
6169587Sgreen#endif
6269587Sgreen
6369587Sgreen#if defined (STDC_HEADERS)
6469587Sgreen#include <stdlib.h>
6569587Sgreen#endif
6669587Sgreen
6769587Sgreen/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
6869587Sgreen   releases):
6969587Sgreen
7069587Sgreen   We don't want to mess with all the portability hassles of alloca.
7169587Sgreen   In particular, most (all?) versions of bison will use alloca in
7276259Sgreen   their parser.  If bison works on your system (e.g. it should work
7376259Sgreen   with gcc), then go ahead and use it, but the more general solution
7469587Sgreen   is to use byacc instead of bison, which should generate a portable
7569587Sgreen   parser.  I played with adding "#define alloca dont_use_alloca", to
7669587Sgreen   give an error if the parser generator uses alloca (and thus detect
7769587Sgreen   unportable getdate.c's), but that seems to cause as many problems
7869587Sgreen   as it solves.  */
7969587Sgreen
8069587Sgreenextern struct tm	*gmtime();
8192555Sdesextern struct tm	*localtime();
8292555Sdes
8392555Sdes#define yyparse getdate_yyparse
8492555Sdes#define yylex getdate_yylex
8576259Sgreen#define yyerror getdate_yyerror
8676259Sgreen
8769587Sgreenstatic int yyparse ();
8876259Sgreenstatic int yylex ();
8976259Sgreenstatic int yyerror ();
9076259Sgreen
9176259Sgreen#define EPOCH		1970
9276259Sgreen#define HOUR(x)		((time_t)(x) * 60)
9376259Sgreen#define SECSPERDAY	(24L * 60L * 60L)
9469587Sgreen
9576259Sgreen
9676259Sgreen/*
9792555Sdes**  An entry in the lexical lookup table.
9892555Sdes*/
9969587Sgreentypedef struct _TABLE {
10076259Sgreen    char	*name;
10169587Sgreen    int		type;
10269587Sgreen    time_t	value;
10369587Sgreen} TABLE;
10469587Sgreen
10576259Sgreen
10669587Sgreen/*
10769587Sgreen**  Daylight-savings mode:  on, off, or not yet known.
10892555Sdes*/
10969587Sgreentypedef enum _DSTMODE {
11069587Sgreen    DSTon, DSToff, DSTmaybe
11169587Sgreen} DSTMODE;
11269587Sgreen
11392555Sdes/*
11492555Sdes**  Meridian:  am, pm, or 24-hour style.
11592555Sdes*/
11669587Sgreentypedef enum _MERIDIAN {
11769587Sgreen    MERam, MERpm, MER24
11869587Sgreen} MERIDIAN;
11969587Sgreen
12069587Sgreen
12169587Sgreen/*
12269587Sgreen**  Global variables.  We could get rid of most of these by using a good
12369587Sgreen**  union as the yacc stack.  (This routine was originally written before
12469587Sgreen**  yacc had the %union construct.)  Maybe someday; right now we only use
12592555Sdes**  the %union very rarely.
12692555Sdes*/
12769587Sgreenstatic char	*yyInput;
12876259Sgreenstatic DSTMODE	yyDSTmode;
12976259Sgreenstatic time_t	yyDayOrdinal;
13076259Sgreenstatic time_t	yyDayNumber;
13176259Sgreenstatic int	yyHaveDate;
13276259Sgreenstatic int	yyHaveDay;
13369587Sgreenstatic int	yyHaveRel;
13469587Sgreenstatic int	yyHaveTime;
13569587Sgreenstatic int	yyHaveZone;
13669587Sgreenstatic time_t	yyTimezone;
13769587Sgreenstatic time_t	yyDay;
13869587Sgreenstatic time_t	yyHour;
13992555Sdesstatic time_t	yyMinutes;
14069587Sgreenstatic time_t	yyMonth;
14169587Sgreenstatic time_t	yySeconds;
14292555Sdesstatic time_t	yyYear;
14376259Sgreenstatic MERIDIAN	yyMeridian;
14476259Sgreenstatic time_t	yyRelMonth;
14569587Sgreenstatic time_t	yyRelSeconds;
14669587Sgreen
14769587Sgreen%}
14869587Sgreen
14969587Sgreen%union {
15069587Sgreen    time_t		Number;
15169587Sgreen    enum _MERIDIAN	Meridian;
15276259Sgreen}
15376259Sgreen
15476259Sgreen%token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
15592555Sdes%token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
15692555Sdes
15769587Sgreen%type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
15869587Sgreen%type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
15969587Sgreen%type	<Meridian>	tMERIDIAN o_merid
16069587Sgreen
16169587Sgreen%%
16276259Sgreen
16376259Sgreenspec	: /* NULL */
16476259Sgreen	| spec item
16569587Sgreen	;
16669587Sgreen
16769587Sgreenitem	: time {
16876259Sgreen	    yyHaveTime++;
16976259Sgreen	}
17076259Sgreen	| zone {
17176259Sgreen	    yyHaveZone++;
17276259Sgreen	}
17376259Sgreen	| date {
17476259Sgreen	    yyHaveDate++;
17576259Sgreen	}
17676259Sgreen	| day {
17776259Sgreen	    yyHaveDay++;
17876259Sgreen	}
17976259Sgreen	| rel {
18076259Sgreen	    yyHaveRel++;
18176259Sgreen	}
18276259Sgreen	| number
18376259Sgreen	;
18476259Sgreen
18576259Sgreentime	: tUNUMBER tMERIDIAN {
18676259Sgreen	    yyHour = $1;
18776259Sgreen	    yyMinutes = 0;
18876259Sgreen	    yySeconds = 0;
18976259Sgreen	    yyMeridian = $2;
19076259Sgreen	}
19176259Sgreen	| tUNUMBER ':' tUNUMBER o_merid {
19276259Sgreen	    yyHour = $1;
19376259Sgreen	    yyMinutes = $3;
19476259Sgreen	    yySeconds = 0;
19576259Sgreen	    yyMeridian = $4;
19676259Sgreen	}
19776259Sgreen	| tUNUMBER ':' tUNUMBER tSNUMBER {
19876259Sgreen	    yyHour = $1;
19976259Sgreen	    yyMinutes = $3;
20076259Sgreen	    yyMeridian = MER24;
20176259Sgreen	    yyDSTmode = DSToff;
20276259Sgreen	    yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
20376259Sgreen	}
20476259Sgreen	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
20576259Sgreen	    yyHour = $1;
20692555Sdes	    yyMinutes = $3;
20792555Sdes	    yySeconds = $5;
20876259Sgreen	    yyMeridian = $6;
20976259Sgreen	}
21076259Sgreen	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
21176259Sgreen	    yyHour = $1;
21276259Sgreen	    yyMinutes = $3;
21376259Sgreen	    yySeconds = $5;
21476259Sgreen	    yyMeridian = MER24;
21576259Sgreen	    yyDSTmode = DSToff;
21676259Sgreen	    yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
21776259Sgreen	}
21876259Sgreen	;
21976259Sgreen
22076259Sgreenzone	: tZONE {
22176259Sgreen	    yyTimezone = $1;
22276259Sgreen	    yyDSTmode = DSToff;
22376259Sgreen	}
22476259Sgreen	| tDAYZONE {
22576259Sgreen	    yyTimezone = $1;
22676259Sgreen	    yyDSTmode = DSTon;
22776259Sgreen	}
22876259Sgreen	|
22992555Sdes	  tZONE tDST {
23092555Sdes	    yyTimezone = $1;
23176259Sgreen	    yyDSTmode = DSTon;
23276259Sgreen	}
23376259Sgreen	;
23476259Sgreen
23576259Sgreenday	: tDAY {
23676259Sgreen	    yyDayOrdinal = 1;
23776259Sgreen	    yyDayNumber = $1;
23876259Sgreen	}
23976259Sgreen	| tDAY ',' {
24076259Sgreen	    yyDayOrdinal = 1;
24176259Sgreen	    yyDayNumber = $1;
24276259Sgreen	}
24376259Sgreen	| tUNUMBER tDAY {
24476259Sgreen	    yyDayOrdinal = $1;
24576259Sgreen	    yyDayNumber = $2;
24676259Sgreen	}
24776259Sgreen	;
24876259Sgreen
24976259Sgreendate	: tUNUMBER '/' tUNUMBER {
25092555Sdes	    yyMonth = $1;
25192555Sdes	    yyDay = $3;
25276259Sgreen	}
25376259Sgreen	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
25476259Sgreen	    if ($1 >= 100) {
25576259Sgreen		yyYear = $1;
25676259Sgreen		yyMonth = $3;
25776259Sgreen		yyDay = $5;
25876259Sgreen	    } else {
25976259Sgreen		yyMonth = $1;
26076259Sgreen		yyDay = $3;
26176259Sgreen		yyYear = $5;
26276259Sgreen	    }
26376259Sgreen	}
26476259Sgreen	| tUNUMBER tSNUMBER tSNUMBER {
26576259Sgreen	    /* ISO 8601 format.  yyyy-mm-dd.  */
26676259Sgreen	    yyYear = $1;
26776259Sgreen	    yyMonth = -$2;
26876259Sgreen	    yyDay = -$3;
26976259Sgreen	}
27076259Sgreen	| tUNUMBER tMONTH tSNUMBER {
27176259Sgreen	    /* e.g. 17-JUN-1992.  */
27276259Sgreen	    yyDay = $1;
27376259Sgreen	    yyMonth = $2;
27476259Sgreen	    yyYear = -$3;
27576259Sgreen	}
27676259Sgreen	| tMONTH tUNUMBER {
27776259Sgreen	    yyMonth = $1;
27876259Sgreen	    yyDay = $2;
27976259Sgreen	}
28076259Sgreen	| tMONTH tUNUMBER ',' tUNUMBER {
28176259Sgreen	    yyMonth = $1;
28276259Sgreen	    yyDay = $2;
28376259Sgreen	    yyYear = $4;
28476259Sgreen	}
28576259Sgreen	| tUNUMBER tMONTH {
28676259Sgreen	    yyMonth = $2;
28776259Sgreen	    yyDay = $1;
28876259Sgreen	}
28976259Sgreen	| tUNUMBER tMONTH tUNUMBER {
290	    yyMonth = $2;
291	    yyDay = $1;
292	    yyYear = $3;
293	}
294	;
295
296rel	: relunit tAGO {
297	    yyRelSeconds = -yyRelSeconds;
298	    yyRelMonth = -yyRelMonth;
299	}
300	| relunit
301	;
302
303relunit	: tUNUMBER tMINUTE_UNIT {
304	    yyRelSeconds += $1 * $2 * 60L;
305	}
306	| tSNUMBER tMINUTE_UNIT {
307	    yyRelSeconds += $1 * $2 * 60L;
308	}
309	| tMINUTE_UNIT {
310	    yyRelSeconds += $1 * 60L;
311	}
312	| tSNUMBER tSEC_UNIT {
313	    yyRelSeconds += $1;
314	}
315	| tUNUMBER tSEC_UNIT {
316	    yyRelSeconds += $1;
317	}
318	| tSEC_UNIT {
319	    yyRelSeconds++;
320	}
321	| tSNUMBER tMONTH_UNIT {
322	    yyRelMonth += $1 * $2;
323	}
324	| tUNUMBER tMONTH_UNIT {
325	    yyRelMonth += $1 * $2;
326	}
327	| tMONTH_UNIT {
328	    yyRelMonth += $1;
329	}
330	;
331
332number	: tUNUMBER {
333	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
334		yyYear = $1;
335	    else {
336		if($1>10000) {
337		    yyHaveDate++;
338		    yyDay= ($1)%100;
339		    yyMonth= ($1/100)%100;
340		    yyYear = $1/10000;
341		}
342		else {
343		    yyHaveTime++;
344		    if ($1 < 100) {
345			yyHour = $1;
346			yyMinutes = 0;
347		    }
348		    else {
349		    	yyHour = $1 / 100;
350		    	yyMinutes = $1 % 100;
351		    }
352		    yySeconds = 0;
353		    yyMeridian = MER24;
354	        }
355	    }
356	}
357	;
358
359o_merid	: /* NULL */ {
360	    $$ = MER24;
361	}
362	| tMERIDIAN {
363	    $$ = $1;
364	}
365	;
366
367%%
368
369/* Month and day table. */
370static TABLE const MonthDayTable[] = {
371    { "january",	tMONTH,  1 },
372    { "february",	tMONTH,  2 },
373    { "march",		tMONTH,  3 },
374    { "april",		tMONTH,  4 },
375    { "may",		tMONTH,  5 },
376    { "june",		tMONTH,  6 },
377    { "july",		tMONTH,  7 },
378    { "august",		tMONTH,  8 },
379    { "september",	tMONTH,  9 },
380    { "sept",		tMONTH,  9 },
381    { "october",	tMONTH, 10 },
382    { "november",	tMONTH, 11 },
383    { "december",	tMONTH, 12 },
384    { "sunday",		tDAY, 0 },
385    { "monday",		tDAY, 1 },
386    { "tuesday",	tDAY, 2 },
387    { "tues",		tDAY, 2 },
388    { "wednesday",	tDAY, 3 },
389    { "wednes",		tDAY, 3 },
390    { "thursday",	tDAY, 4 },
391    { "thur",		tDAY, 4 },
392    { "thurs",		tDAY, 4 },
393    { "friday",		tDAY, 5 },
394    { "saturday",	tDAY, 6 },
395    { NULL }
396};
397
398/* Time units table. */
399static TABLE const UnitsTable[] = {
400    { "year",		tMONTH_UNIT,	12 },
401    { "month",		tMONTH_UNIT,	1 },
402    { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
403    { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
404    { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
405    { "hour",		tMINUTE_UNIT,	60 },
406    { "minute",		tMINUTE_UNIT,	1 },
407    { "min",		tMINUTE_UNIT,	1 },
408    { "second",		tSEC_UNIT,	1 },
409    { "sec",		tSEC_UNIT,	1 },
410    { NULL }
411};
412
413/* Assorted relative-time words. */
414static TABLE const OtherTable[] = {
415    { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
416    { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
417    { "today",		tMINUTE_UNIT,	0 },
418    { "now",		tMINUTE_UNIT,	0 },
419    { "last",		tUNUMBER,	-1 },
420    { "this",		tMINUTE_UNIT,	0 },
421    { "next",		tUNUMBER,	2 },
422    { "first",		tUNUMBER,	1 },
423/*  { "second",		tUNUMBER,	2 }, */
424    { "third",		tUNUMBER,	3 },
425    { "fourth",		tUNUMBER,	4 },
426    { "fifth",		tUNUMBER,	5 },
427    { "sixth",		tUNUMBER,	6 },
428    { "seventh",	tUNUMBER,	7 },
429    { "eighth",		tUNUMBER,	8 },
430    { "ninth",		tUNUMBER,	9 },
431    { "tenth",		tUNUMBER,	10 },
432    { "eleventh",	tUNUMBER,	11 },
433    { "twelfth",	tUNUMBER,	12 },
434    { "ago",		tAGO,	1 },
435    { NULL }
436};
437
438/* The timezone table. */
439/* Some of these are commented out because a time_t can't store a float. */
440static TABLE const TimezoneTable[] = {
441    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
442    { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
443    { "utc",	tZONE,     HOUR( 0) },
444    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
445    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
446    { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
447    { "at",	tZONE,     HOUR( 2) },	/* Azores */
448#if	0
449    /* For completeness.  BST is also British Summer, and GST is
450     * also Guam Standard. */
451    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
452    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
453#endif
454#if 0
455    { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
456    { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
457    { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
458#endif
459    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
460    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
461    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
462    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
463    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
464    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
465    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
466    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
467    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
468    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
469    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
470    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
471    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
472    { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
473    { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
474    { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
475    { "nt",	tZONE,     HOUR(11) },	/* Nome */
476    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
477    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
478    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
479    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
480    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
481    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
482    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
483    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
484    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
485    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
486    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
487#if 0
488    { "it",	tZONE,     -HOUR(3.5) },/* Iran */
489#endif
490    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
491    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
492#if 0
493    { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
494#endif
495    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
496#if	0
497    /* For completeness.  NST is also Newfoundland Stanard, and SST is
498     * also Swedish Summer. */
499    { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
500    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
501#endif	/* 0 */
502    { "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
503    { "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
504#if 0
505    { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
506#endif
507    { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
508    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
509#if 0
510    { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
511    { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
512#endif
513    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
514    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
515    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
516    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
517    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
518    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
519    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
520    {  NULL  }
521};
522
523/* Military timezone table. */
524static TABLE const MilitaryTable[] = {
525    { "a",	tZONE,	HOUR(  1) },
526    { "b",	tZONE,	HOUR(  2) },
527    { "c",	tZONE,	HOUR(  3) },
528    { "d",	tZONE,	HOUR(  4) },
529    { "e",	tZONE,	HOUR(  5) },
530    { "f",	tZONE,	HOUR(  6) },
531    { "g",	tZONE,	HOUR(  7) },
532    { "h",	tZONE,	HOUR(  8) },
533    { "i",	tZONE,	HOUR(  9) },
534    { "k",	tZONE,	HOUR( 10) },
535    { "l",	tZONE,	HOUR( 11) },
536    { "m",	tZONE,	HOUR( 12) },
537    { "n",	tZONE,	HOUR(- 1) },
538    { "o",	tZONE,	HOUR(- 2) },
539    { "p",	tZONE,	HOUR(- 3) },
540    { "q",	tZONE,	HOUR(- 4) },
541    { "r",	tZONE,	HOUR(- 5) },
542    { "s",	tZONE,	HOUR(- 6) },
543    { "t",	tZONE,	HOUR(- 7) },
544    { "u",	tZONE,	HOUR(- 8) },
545    { "v",	tZONE,	HOUR(- 9) },
546    { "w",	tZONE,	HOUR(-10) },
547    { "x",	tZONE,	HOUR(-11) },
548    { "y",	tZONE,	HOUR(-12) },
549    { "z",	tZONE,	HOUR(  0) },
550    { NULL }
551};
552
553
554
555
556/* ARGSUSED */
557static int
558yyerror(s)
559    char	*s;
560{
561  return 0;
562}
563
564
565static time_t
566ToSeconds(Hours, Minutes, Seconds, Meridian)
567    time_t	Hours;
568    time_t	Minutes;
569    time_t	Seconds;
570    MERIDIAN	Meridian;
571{
572    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
573	return -1;
574    switch (Meridian) {
575    case MER24:
576	if (Hours < 0 || Hours > 23)
577	    return -1;
578	return (Hours * 60L + Minutes) * 60L + Seconds;
579    case MERam:
580	if (Hours < 1 || Hours > 12)
581	    return -1;
582	if (Hours == 12)
583	    Hours = 0;
584	return (Hours * 60L + Minutes) * 60L + Seconds;
585    case MERpm:
586	if (Hours < 1 || Hours > 12)
587	    return -1;
588	if (Hours == 12)
589	    Hours = 0;
590	return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
591    default:
592	abort ();
593    }
594    /* NOTREACHED */
595}
596
597
598/* Year is either
599   * A negative number, which means to use its absolute value (why?)
600   * A number from 0 to 99, which means a year from 1900 to 1999, or
601   * The actual year (>=100).  */
602static time_t
603Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
604    time_t	Month;
605    time_t	Day;
606    time_t	Year;
607    time_t	Hours;
608    time_t	Minutes;
609    time_t	Seconds;
610    MERIDIAN	Meridian;
611    DSTMODE	DSTmode;
612{
613    static int DaysInMonth[12] = {
614	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
615    };
616    time_t	tod;
617    time_t	Julian;
618    int		i;
619
620    if (Year < 0)
621	Year = -Year;
622    if (Year < 69)
623	Year += 2000;
624    else if (Year < 100)
625	Year += 1900;
626    DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
627		    ? 29 : 28;
628    /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
629       I'm too lazy to try to check for time_t overflow in another way.  */
630    if (Year < EPOCH || Year > 2038
631     || Month < 1 || Month > 12
632     /* Lint fluff:  "conversion from long may lose accuracy" */
633     || Day < 1 || Day > DaysInMonth[(int)--Month])
634	return -1;
635
636    for (Julian = Day - 1, i = 0; i < Month; i++)
637	Julian += DaysInMonth[i];
638    for (i = EPOCH; i < Year; i++)
639	Julian += 365 + (i % 4 == 0);
640    Julian *= SECSPERDAY;
641    Julian += yyTimezone * 60L;
642    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
643	return -1;
644    Julian += tod;
645    if (DSTmode == DSTon
646     || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
647	Julian -= 60 * 60;
648    return Julian;
649}
650
651
652static time_t
653DSTcorrect(Start, Future)
654    time_t	Start;
655    time_t	Future;
656{
657    time_t	StartDay;
658    time_t	FutureDay;
659
660    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
661    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
662    return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
663}
664
665
666static time_t
667RelativeDate(Start, DayOrdinal, DayNumber)
668    time_t	Start;
669    time_t	DayOrdinal;
670    time_t	DayNumber;
671{
672    struct tm	*tm;
673    time_t	now;
674
675    now = Start;
676    tm = localtime(&now);
677    now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
678    now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
679    return DSTcorrect(Start, now);
680}
681
682
683static time_t
684RelativeMonth(Start, RelMonth)
685    time_t	Start;
686    time_t	RelMonth;
687{
688    struct tm	*tm;
689    time_t	Month;
690    time_t	Year;
691
692    if (RelMonth == 0)
693	return 0;
694    tm = localtime(&Start);
695    Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
696    Year = Month / 12;
697    Month = Month % 12 + 1;
698    return DSTcorrect(Start,
699	    Convert(Month, (time_t)tm->tm_mday, Year,
700		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
701		MER24, DSTmaybe));
702}
703
704
705static int
706LookupWord(buff)
707    char		*buff;
708{
709    register char	*p;
710    register char	*q;
711    register const TABLE	*tp;
712    int			i;
713    int			abbrev;
714
715    /* Make it lowercase. */
716    for (p = buff; *p; p++)
717	if (isupper(*p))
718	    *p = tolower(*p);
719
720    if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
721	yylval.Meridian = MERam;
722	return tMERIDIAN;
723    }
724    if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
725	yylval.Meridian = MERpm;
726	return tMERIDIAN;
727    }
728
729    /* See if we have an abbreviation for a month. */
730    if (strlen(buff) == 3)
731	abbrev = 1;
732    else if (strlen(buff) == 4 && buff[3] == '.') {
733	abbrev = 1;
734	buff[3] = '\0';
735    }
736    else
737	abbrev = 0;
738
739    for (tp = MonthDayTable; tp->name; tp++) {
740	if (abbrev) {
741	    if (strncmp(buff, tp->name, 3) == 0) {
742		yylval.Number = tp->value;
743		return tp->type;
744	    }
745	}
746	else if (strcmp(buff, tp->name) == 0) {
747	    yylval.Number = tp->value;
748	    return tp->type;
749	}
750    }
751
752    for (tp = TimezoneTable; tp->name; tp++)
753	if (strcmp(buff, tp->name) == 0) {
754	    yylval.Number = tp->value;
755	    return tp->type;
756	}
757
758    if (strcmp(buff, "dst") == 0)
759	return tDST;
760
761    for (tp = UnitsTable; tp->name; tp++)
762	if (strcmp(buff, tp->name) == 0) {
763	    yylval.Number = tp->value;
764	    return tp->type;
765	}
766
767    /* Strip off any plural and try the units table again. */
768    i = strlen(buff) - 1;
769    if (buff[i] == 's') {
770	buff[i] = '\0';
771	for (tp = UnitsTable; tp->name; tp++)
772	    if (strcmp(buff, tp->name) == 0) {
773		yylval.Number = tp->value;
774		return tp->type;
775	    }
776	buff[i] = 's';		/* Put back for "this" in OtherTable. */
777    }
778
779    for (tp = OtherTable; tp->name; tp++)
780	if (strcmp(buff, tp->name) == 0) {
781	    yylval.Number = tp->value;
782	    return tp->type;
783	}
784
785    /* Military timezones. */
786    if (buff[1] == '\0' && isalpha(*buff)) {
787	for (tp = MilitaryTable; tp->name; tp++)
788	    if (strcmp(buff, tp->name) == 0) {
789		yylval.Number = tp->value;
790		return tp->type;
791	    }
792    }
793
794    /* Drop out any periods and try the timezone table again. */
795    for (i = 0, p = q = buff; *q; q++)
796	if (*q != '.')
797	    *p++ = *q;
798	else
799	    i++;
800    *p = '\0';
801    if (i)
802	for (tp = TimezoneTable; tp->name; tp++)
803	    if (strcmp(buff, tp->name) == 0) {
804		yylval.Number = tp->value;
805		return tp->type;
806	    }
807
808    return tID;
809}
810
811
812static int
813yylex()
814{
815    register char	c;
816    register char	*p;
817    char		buff[20];
818    int			Count;
819    int			sign;
820
821    for ( ; ; ) {
822	while (isspace(*yyInput))
823	    yyInput++;
824
825	if (isdigit(c = *yyInput) || c == '-' || c == '+') {
826	    if (c == '-' || c == '+') {
827		sign = c == '-' ? -1 : 1;
828		if (!isdigit(*++yyInput))
829		    /* skip the '-' sign */
830		    continue;
831	    }
832	    else
833		sign = 0;
834	    for (yylval.Number = 0; isdigit(c = *yyInput++); )
835		yylval.Number = 10 * yylval.Number + c - '0';
836	    yyInput--;
837	    if (sign < 0)
838		yylval.Number = -yylval.Number;
839	    return sign ? tSNUMBER : tUNUMBER;
840	}
841	if (isalpha(c)) {
842	    for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
843		if (p < &buff[sizeof buff - 1])
844		    *p++ = c;
845	    *p = '\0';
846	    yyInput--;
847	    return LookupWord(buff);
848	}
849	if (c != '(')
850	    return *yyInput++;
851	Count = 0;
852	do {
853	    c = *yyInput++;
854	    if (c == '\0')
855		return c;
856	    if (c == '(')
857		Count++;
858	    else if (c == ')')
859		Count--;
860	} while (Count > 0);
861    }
862}
863
864#define TM_YEAR_ORIGIN 1900
865
866/* Yield A - B, measured in seconds.  */
867static long
868difftm (a, b)
869     struct tm *a, *b;
870{
871  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
872  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
873  int days = (
874	      /* difference in day of year */
875	      a->tm_yday - b->tm_yday
876	      /* + intervening leap days */
877	      +  ((ay >> 2) - (by >> 2))
878	      -  (ay/100 - by/100)
879	      +  ((ay/100 >> 2) - (by/100 >> 2))
880	      /* + difference in years * 365 */
881	      +  (long)(ay-by) * 365
882	      );
883  return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
884	      + (a->tm_min - b->tm_min))
885	  + (a->tm_sec - b->tm_sec));
886}
887
888time_t
889get_date(p, now)
890    char		*p;
891    struct timeb	*now;
892{
893    struct tm		*tm, gmt;
894    struct timeb	ftz;
895    time_t		Start;
896    time_t		tod;
897    time_t nowtime;
898
899    yyInput = p;
900    if (now == NULL) {
901	struct tm *gmt_ptr;
902
903        now = &ftz;
904	(void)time (&nowtime);
905
906	gmt_ptr = gmtime (&nowtime);
907	if (gmt_ptr != NULL)
908	{
909	    /* Make a copy, in case localtime modifies *tm (I think
910	       that comment now applies to *gmt_ptr, but I am too
911	       lazy to dig into how gmtime and locatime allocate the
912	       structures they return pointers to).  */
913	    gmt = *gmt_ptr;
914	}
915
916	if (! (tm = localtime (&nowtime)))
917	    return -1;
918
919	if (gmt_ptr != NULL)
920	    ftz.timezone = difftm (&gmt, tm) / 60;
921	else
922	    /* We are on a system like VMS, where the system clock is
923	       in local time and the system has no concept of timezones.
924	       Hopefully we can fake this out (for the case in which the
925	       user specifies no timezone) by just saying the timezone
926	       is zero.  */
927	    ftz.timezone = 0;
928
929	if(tm->tm_isdst)
930	    ftz.timezone += 60;
931    }
932    else
933    {
934	nowtime = now->time;
935    }
936
937    tm = localtime(&nowtime);
938    yyYear = tm->tm_year + 1900;
939    yyMonth = tm->tm_mon + 1;
940    yyDay = tm->tm_mday;
941    yyTimezone = now->timezone;
942    yyDSTmode = DSTmaybe;
943    yyHour = 0;
944    yyMinutes = 0;
945    yySeconds = 0;
946    yyMeridian = MER24;
947    yyRelSeconds = 0;
948    yyRelMonth = 0;
949    yyHaveDate = 0;
950    yyHaveDay = 0;
951    yyHaveRel = 0;
952    yyHaveTime = 0;
953    yyHaveZone = 0;
954
955    if (yyparse()
956     || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
957	return -1;
958
959    if (yyHaveDate || yyHaveTime || yyHaveDay) {
960	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
961		    yyMeridian, yyDSTmode);
962	if (Start < 0)
963	    return -1;
964    }
965    else {
966	Start = nowtime;
967	if (!yyHaveRel)
968	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
969    }
970
971    Start += yyRelSeconds;
972    Start += RelativeMonth(Start, yyRelMonth);
973
974    if (yyHaveDay && !yyHaveDate) {
975	tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
976	Start += tod;
977    }
978
979    /* Have to do *something* with a legitimate -1 so it's distinguishable
980     * from the error return value.  (Alternately could set errno on error.) */
981    return Start == -1 ? 0 : Start;
982}
983
984
985#if	defined(TEST)
986
987/* ARGSUSED */
988int
989main(ac, av)
990    int		ac;
991    char	*av[];
992{
993    char	buff[128];
994    time_t	d;
995
996    (void)printf("Enter date, or blank line to exit.\n\t> ");
997    (void)fflush(stdout);
998    while (gets(buff) && buff[0]) {
999	d = get_date(buff, (struct timeb *)NULL);
1000	if (d == -1)
1001	    (void)printf("Bad format - couldn't convert.\n");
1002	else
1003	    (void)printf("%s", ctime(&d));
1004	(void)printf("\t> ");
1005	(void)fflush(stdout);
1006    }
1007    exit(0);
1008    /* NOTREACHED */
1009}
1010#endif	/* defined(TEST) */
1011