194630Sobrien%{
294630Sobrien/*
394630Sobrien**  Originally written by Steven M. Bellovin <smb@research.att.com> while
494630Sobrien**  at the University of North Carolina at Chapel Hill.  Later tweaked by
594630Sobrien**  a couple of people on Usenet.  Completely overhauled by Rich $alz
694630Sobrien**  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
794630Sobrien**
894630Sobrien**  This grammar has 10 shift/reduce conflicts.
994630Sobrien**
1094630Sobrien**  This code is in the public domain and has no copyright.
1194630Sobrien*/
1294630Sobrien/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
1394630Sobrien/* SUPPRESS 288 on yyerrlab *//* Label unused */
1494630Sobrien
15116333Smarkm#include <sys/cdefs.h>
16116333Smarkm__FBSDID("$FreeBSD$");
1794630Sobrien
18200462Sdelphij#include <stdio.h>
1994630Sobrien#include <ctype.h>
2094630Sobrien
2194630Sobrien/* The code at the top of get_date which figures out the offset of the
2294630Sobrien   current time zone checks various CPP symbols to see if special
2394630Sobrien   tricks are need, but defaults to using the gettimeofday system call.
2494630Sobrien   Include <sys/time.h> if that will be used.  */
2594630Sobrien
2694630Sobrien#if	defined(vms)
2794630Sobrien# include <types.h>
2894630Sobrien#else /* defined(vms) */
2994630Sobrien# include <sys/types.h>
3094630Sobrien# include <sys/time.h>
3194630Sobrien#endif	/* !defined(vms) */
3294630Sobrien
3394792Sobrien#if defined (__STDC__) || defined (USG)
3494630Sobrien#include <string.h>
3594630Sobrien#endif
3694630Sobrien
3794630Sobrien/* Some old versions of bison generate parsers that use bcopy.
3894630Sobrien   That loses on systems that don't provide the function, so we have
3994630Sobrien   to redefine it here.  */
4094630Sobrien#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
4194630Sobrien#define bcopy(from, to, len) memcpy ((to), (from), (len))
4294630Sobrien#endif
4394630Sobrien
4494792Sobrien#if defined (__STDC__)
4594630Sobrien#include <stdlib.h>
4694630Sobrien#endif
4794630Sobrien
4894630Sobrien/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
4994630Sobrien   releases):
5094630Sobrien
5194630Sobrien   We don't want to mess with all the portability hassles of alloca.
5294630Sobrien   In particular, most (all?) versions of bison will use alloca in
5394630Sobrien   their parser.  If bison works on your system (e.g. it should work
5494630Sobrien   with gcc), then go ahead and use it, but the more general solution
5594630Sobrien   is to use byacc instead of bison, which should generate a portable
5694630Sobrien   parser.  I played with adding "#define alloca dont_use_alloca", to
5794630Sobrien   give an error if the parser generator uses alloca (and thus detect
5894630Sobrien   unportable getdate.c's), but that seems to cause as many problems
5994630Sobrien   as it solves.  */
6094630Sobrien
61116333Smarkm#include <time.h>
6294630Sobrien
6394630Sobrien#define yylex getdate_yylex
6494630Sobrien#define yyerror getdate_yyerror
6594630Sobrien
66116333Smarkmstatic int yylex(void);
67116333Smarkmstatic int yyerror(const char *);
6894630Sobrien
69203723Sedtime_t get_date(char *);
70116333Smarkm
7194630Sobrien#define EPOCH		1970
7294630Sobrien#define HOUR(x)		((time_t)(x) * 60)
7394630Sobrien#define SECSPERDAY	(24L * 60L * 60L)
7494630Sobrien
7594630Sobrien
7694630Sobrien/*
7794630Sobrien**  An entry in the lexical lookup table.
7894630Sobrien*/
7994630Sobrientypedef struct _TABLE {
80116333Smarkm    const char	*name;
8194630Sobrien    int		type;
8294630Sobrien    time_t	value;
8394630Sobrien} TABLE;
8494630Sobrien
8594630Sobrien
8694630Sobrien/*
8794630Sobrien**  Daylight-savings mode:  on, off, or not yet known.
8894630Sobrien*/
8994630Sobrientypedef enum _DSTMODE {
9094630Sobrien    DSTon, DSToff, DSTmaybe
9194630Sobrien} DSTMODE;
9294630Sobrien
9394630Sobrien/*
9494630Sobrien**  Meridian:  am, pm, or 24-hour style.
9594630Sobrien*/
9694630Sobrientypedef enum _MERIDIAN {
9794630Sobrien    MERam, MERpm, MER24
9894630Sobrien} MERIDIAN;
9994630Sobrien
10094630Sobrien
10194630Sobrien/*
10294630Sobrien**  Global variables.  We could get rid of most of these by using a good
10394630Sobrien**  union as the yacc stack.  (This routine was originally written before
10494630Sobrien**  yacc had the %union construct.)  Maybe someday; right now we only use
10594630Sobrien**  the %union very rarely.
10694630Sobrien*/
10794630Sobrienstatic char	*yyInput;
10894630Sobrienstatic DSTMODE	yyDSTmode;
10994630Sobrienstatic time_t	yyDayOrdinal;
11094630Sobrienstatic time_t	yyDayNumber;
11194630Sobrienstatic int	yyHaveDate;
11294630Sobrienstatic int	yyHaveDay;
11394630Sobrienstatic int	yyHaveRel;
11494630Sobrienstatic int	yyHaveTime;
11594630Sobrienstatic int	yyHaveZone;
11694630Sobrienstatic time_t	yyTimezone;
11794630Sobrienstatic time_t	yyDay;
11894630Sobrienstatic time_t	yyHour;
11994630Sobrienstatic time_t	yyMinutes;
12094630Sobrienstatic time_t	yyMonth;
12194630Sobrienstatic time_t	yySeconds;
12294630Sobrienstatic time_t	yyYear;
12394630Sobrienstatic MERIDIAN	yyMeridian;
12494630Sobrienstatic time_t	yyRelMonth;
12594630Sobrienstatic time_t	yyRelSeconds;
12694630Sobrien
12794630Sobrien%}
12894630Sobrien
12994630Sobrien%union {
13094630Sobrien    time_t		Number;
13194630Sobrien    enum _MERIDIAN	Meridian;
13294630Sobrien}
13394630Sobrien
13494630Sobrien%token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
13594630Sobrien%token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
13694630Sobrien
13794630Sobrien%type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
13894630Sobrien%type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
13994630Sobrien%type	<Meridian>	tMERIDIAN o_merid
14094630Sobrien
14194630Sobrien%%
14294630Sobrien
14394630Sobrienspec	: /* NULL */
14494630Sobrien	| spec item
14594630Sobrien	;
14694630Sobrien
14794630Sobrienitem	: time {
14894630Sobrien	    yyHaveTime++;
14994630Sobrien	}
15094630Sobrien	| zone {
15194630Sobrien	    yyHaveZone++;
15294630Sobrien	}
15394630Sobrien	| date {
15494630Sobrien	    yyHaveDate++;
15594630Sobrien	}
15694630Sobrien	| day {
15794630Sobrien	    yyHaveDay++;
15894630Sobrien	}
15994630Sobrien	| rel {
16094630Sobrien	    yyHaveRel++;
16194630Sobrien	}
16294630Sobrien	| number
16394630Sobrien	;
16494630Sobrien
16594630Sobrientime	: tUNUMBER tMERIDIAN {
16694630Sobrien	    yyHour = $1;
16794630Sobrien	    yyMinutes = 0;
16894630Sobrien	    yySeconds = 0;
16994630Sobrien	    yyMeridian = $2;
17094630Sobrien	}
17194630Sobrien	| tUNUMBER ':' tUNUMBER o_merid {
17294630Sobrien	    yyHour = $1;
17394630Sobrien	    yyMinutes = $3;
17494630Sobrien	    yySeconds = 0;
17594630Sobrien	    yyMeridian = $4;
17694630Sobrien	}
17794630Sobrien	| tUNUMBER ':' tUNUMBER tSNUMBER {
17894630Sobrien	    yyHour = $1;
17994630Sobrien	    yyMinutes = $3;
18094630Sobrien	    yyMeridian = MER24;
18194630Sobrien	    yyDSTmode = DSToff;
18294630Sobrien	    yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
18394630Sobrien	}
18494630Sobrien	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
18594630Sobrien	    yyHour = $1;
18694630Sobrien	    yyMinutes = $3;
18794630Sobrien	    yySeconds = $5;
18894630Sobrien	    yyMeridian = $6;
18994630Sobrien	}
19094630Sobrien	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
19194630Sobrien	    yyHour = $1;
19294630Sobrien	    yyMinutes = $3;
19394630Sobrien	    yySeconds = $5;
19494630Sobrien	    yyMeridian = MER24;
19594630Sobrien	    yyDSTmode = DSToff;
19694630Sobrien	    yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
19794630Sobrien	}
19894630Sobrien	;
19994630Sobrien
20094630Sobrienzone	: tZONE {
20194630Sobrien	    yyTimezone = $1;
20294630Sobrien	    yyDSTmode = DSToff;
20394630Sobrien	}
20494630Sobrien	| tDAYZONE {
20594630Sobrien	    yyTimezone = $1;
20694630Sobrien	    yyDSTmode = DSTon;
20794630Sobrien	}
20894630Sobrien	|
20994630Sobrien	  tZONE tDST {
21094630Sobrien	    yyTimezone = $1;
21194630Sobrien	    yyDSTmode = DSTon;
21294630Sobrien	}
21394630Sobrien	;
21494630Sobrien
21594630Sobrienday	: tDAY {
21694630Sobrien	    yyDayOrdinal = 1;
21794630Sobrien	    yyDayNumber = $1;
21894630Sobrien	}
21994630Sobrien	| tDAY ',' {
22094630Sobrien	    yyDayOrdinal = 1;
22194630Sobrien	    yyDayNumber = $1;
22294630Sobrien	}
22394630Sobrien	| tUNUMBER tDAY {
22494630Sobrien	    yyDayOrdinal = $1;
22594630Sobrien	    yyDayNumber = $2;
22694630Sobrien	}
22794630Sobrien	;
22894630Sobrien
22994630Sobriendate	: tUNUMBER '/' tUNUMBER {
23094630Sobrien	    yyMonth = $1;
23194630Sobrien	    yyDay = $3;
23294630Sobrien	}
23394630Sobrien	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
23494630Sobrien	    if ($1 >= 100) {
23594630Sobrien		yyYear = $1;
23694630Sobrien		yyMonth = $3;
23794630Sobrien		yyDay = $5;
23894630Sobrien	    } else {
23994630Sobrien		yyMonth = $1;
24094630Sobrien		yyDay = $3;
24194630Sobrien		yyYear = $5;
24294630Sobrien	    }
24394630Sobrien	}
24494630Sobrien	| tUNUMBER tSNUMBER tSNUMBER {
24594630Sobrien	    /* ISO 8601 format.  yyyy-mm-dd.  */
24694630Sobrien	    yyYear = $1;
24794630Sobrien	    yyMonth = -$2;
24894630Sobrien	    yyDay = -$3;
24994630Sobrien	}
25094630Sobrien	| tUNUMBER tMONTH tSNUMBER {
25194630Sobrien	    /* e.g. 17-JUN-1992.  */
25294630Sobrien	    yyDay = $1;
25394630Sobrien	    yyMonth = $2;
25494630Sobrien	    yyYear = -$3;
25594630Sobrien	}
25694630Sobrien	| tMONTH tUNUMBER {
25794630Sobrien	    yyMonth = $1;
25894630Sobrien	    yyDay = $2;
25994630Sobrien	}
26094630Sobrien	| tMONTH tUNUMBER ',' tUNUMBER {
26194630Sobrien	    yyMonth = $1;
26294630Sobrien	    yyDay = $2;
26394630Sobrien	    yyYear = $4;
26494630Sobrien	}
26594630Sobrien	| tUNUMBER tMONTH {
26694630Sobrien	    yyMonth = $2;
26794630Sobrien	    yyDay = $1;
26894630Sobrien	}
26994630Sobrien	| tUNUMBER tMONTH tUNUMBER {
27094630Sobrien	    yyMonth = $2;
27194630Sobrien	    yyDay = $1;
27294630Sobrien	    yyYear = $3;
27394630Sobrien	}
27494630Sobrien	;
27594630Sobrien
27694630Sobrienrel	: relunit tAGO {
27794630Sobrien	    yyRelSeconds = -yyRelSeconds;
27894630Sobrien	    yyRelMonth = -yyRelMonth;
27994630Sobrien	}
28094630Sobrien	| relunit
28194630Sobrien	;
28294630Sobrien
28394630Sobrienrelunit	: tUNUMBER tMINUTE_UNIT {
28494630Sobrien	    yyRelSeconds += $1 * $2 * 60L;
28594630Sobrien	}
28694630Sobrien	| tSNUMBER tMINUTE_UNIT {
28794630Sobrien	    yyRelSeconds += $1 * $2 * 60L;
28894630Sobrien	}
28994630Sobrien	| tMINUTE_UNIT {
29094630Sobrien	    yyRelSeconds += $1 * 60L;
29194630Sobrien	}
29294630Sobrien	| tSNUMBER tSEC_UNIT {
29394630Sobrien	    yyRelSeconds += $1;
29494630Sobrien	}
29594630Sobrien	| tUNUMBER tSEC_UNIT {
29694630Sobrien	    yyRelSeconds += $1;
29794630Sobrien	}
29894630Sobrien	| tSEC_UNIT {
29994630Sobrien	    yyRelSeconds++;
30094630Sobrien	}
30194630Sobrien	| tSNUMBER tMONTH_UNIT {
30294630Sobrien	    yyRelMonth += $1 * $2;
30394630Sobrien	}
30494630Sobrien	| tUNUMBER tMONTH_UNIT {
30594630Sobrien	    yyRelMonth += $1 * $2;
30694630Sobrien	}
30794630Sobrien	| tMONTH_UNIT {
30894630Sobrien	    yyRelMonth += $1;
30994630Sobrien	}
31094630Sobrien	;
31194630Sobrien
31294630Sobriennumber	: tUNUMBER {
31394630Sobrien	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
31494630Sobrien		yyYear = $1;
31594630Sobrien	    else {
31694630Sobrien		if($1>10000) {
31794630Sobrien		    yyHaveDate++;
31894630Sobrien		    yyDay= ($1)%100;
31994630Sobrien		    yyMonth= ($1/100)%100;
32094630Sobrien		    yyYear = $1/10000;
32194630Sobrien		}
32294630Sobrien		else {
32394630Sobrien		    yyHaveTime++;
32494630Sobrien		    if ($1 < 100) {
32594630Sobrien			yyHour = $1;
32694630Sobrien			yyMinutes = 0;
32794630Sobrien		    }
32894630Sobrien		    else {
32994630Sobrien		    	yyHour = $1 / 100;
33094630Sobrien		    	yyMinutes = $1 % 100;
33194630Sobrien		    }
33294630Sobrien		    yySeconds = 0;
33394630Sobrien		    yyMeridian = MER24;
33494630Sobrien	        }
33594630Sobrien	    }
33694630Sobrien	}
33794630Sobrien	;
33894630Sobrien
33994630Sobrieno_merid	: /* NULL */ {
34094630Sobrien	    $$ = MER24;
34194630Sobrien	}
34294630Sobrien	| tMERIDIAN {
34394630Sobrien	    $$ = $1;
34494630Sobrien	}
34594630Sobrien	;
34694630Sobrien
34794630Sobrien%%
34894630Sobrien
34994630Sobrien/* Month and day table. */
35094630Sobrienstatic TABLE const MonthDayTable[] = {
35194630Sobrien    { "january",	tMONTH,  1 },
35294630Sobrien    { "february",	tMONTH,  2 },
35394630Sobrien    { "march",		tMONTH,  3 },
35494630Sobrien    { "april",		tMONTH,  4 },
35594630Sobrien    { "may",		tMONTH,  5 },
35694630Sobrien    { "june",		tMONTH,  6 },
35794630Sobrien    { "july",		tMONTH,  7 },
35894630Sobrien    { "august",		tMONTH,  8 },
35994630Sobrien    { "september",	tMONTH,  9 },
36094630Sobrien    { "sept",		tMONTH,  9 },
36194630Sobrien    { "october",	tMONTH, 10 },
36294630Sobrien    { "november",	tMONTH, 11 },
36394630Sobrien    { "december",	tMONTH, 12 },
36494630Sobrien    { "sunday",		tDAY, 0 },
36594630Sobrien    { "monday",		tDAY, 1 },
36694630Sobrien    { "tuesday",	tDAY, 2 },
36794630Sobrien    { "tues",		tDAY, 2 },
36894630Sobrien    { "wednesday",	tDAY, 3 },
36994630Sobrien    { "wednes",		tDAY, 3 },
37094630Sobrien    { "thursday",	tDAY, 4 },
37194630Sobrien    { "thur",		tDAY, 4 },
37294630Sobrien    { "thurs",		tDAY, 4 },
37394630Sobrien    { "friday",		tDAY, 5 },
37494630Sobrien    { "saturday",	tDAY, 6 },
375116333Smarkm    { NULL,		0, 0 }
37694630Sobrien};
37794630Sobrien
37894630Sobrien/* Time units table. */
37994630Sobrienstatic TABLE const UnitsTable[] = {
38094630Sobrien    { "year",		tMONTH_UNIT,	12 },
38194630Sobrien    { "month",		tMONTH_UNIT,	1 },
38294630Sobrien    { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
38394630Sobrien    { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
38494630Sobrien    { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
38594630Sobrien    { "hour",		tMINUTE_UNIT,	60 },
38694630Sobrien    { "minute",		tMINUTE_UNIT,	1 },
38794630Sobrien    { "min",		tMINUTE_UNIT,	1 },
38894630Sobrien    { "second",		tSEC_UNIT,	1 },
38994630Sobrien    { "sec",		tSEC_UNIT,	1 },
390116333Smarkm    { NULL,		0,		0 }
39194630Sobrien};
39294630Sobrien
39394630Sobrien/* Assorted relative-time words. */
39494630Sobrienstatic TABLE const OtherTable[] = {
39594630Sobrien    { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
39694630Sobrien    { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
39794630Sobrien    { "today",		tMINUTE_UNIT,	0 },
39894630Sobrien    { "now",		tMINUTE_UNIT,	0 },
39994630Sobrien    { "last",		tUNUMBER,	-1 },
40094630Sobrien    { "this",		tMINUTE_UNIT,	0 },
40194630Sobrien    { "next",		tUNUMBER,	2 },
40294630Sobrien    { "first",		tUNUMBER,	1 },
40394630Sobrien/*  { "second",		tUNUMBER,	2 }, */
40494630Sobrien    { "third",		tUNUMBER,	3 },
40594630Sobrien    { "fourth",		tUNUMBER,	4 },
40694630Sobrien    { "fifth",		tUNUMBER,	5 },
40794630Sobrien    { "sixth",		tUNUMBER,	6 },
40894630Sobrien    { "seventh",	tUNUMBER,	7 },
40994630Sobrien    { "eighth",		tUNUMBER,	8 },
41094630Sobrien    { "ninth",		tUNUMBER,	9 },
41194630Sobrien    { "tenth",		tUNUMBER,	10 },
41294630Sobrien    { "eleventh",	tUNUMBER,	11 },
41394630Sobrien    { "twelfth",	tUNUMBER,	12 },
414116333Smarkm    { "ago",		tAGO,		1 },
415116333Smarkm    { NULL,		0,		0 }
41694630Sobrien};
41794630Sobrien
41894630Sobrien/* The timezone table. */
41994630Sobrien/* Some of these are commented out because a time_t can't store a float. */
42094630Sobrienstatic TABLE const TimezoneTable[] = {
42194630Sobrien    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
42294630Sobrien    { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
42394630Sobrien    { "utc",	tZONE,     HOUR( 0) },
42494630Sobrien    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
42594630Sobrien    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
42694630Sobrien    { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
42794630Sobrien    { "at",	tZONE,     HOUR( 2) },	/* Azores */
42894630Sobrien#if	0
42994630Sobrien    /* For completeness.  BST is also British Summer, and GST is
43094630Sobrien     * also Guam Standard. */
43194630Sobrien    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
43294630Sobrien    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
43394630Sobrien#endif
43494630Sobrien#if 0
43594630Sobrien    { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
43694630Sobrien    { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
43794630Sobrien    { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
43894630Sobrien#endif
43994630Sobrien    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
44094630Sobrien    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
44194630Sobrien    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
44294630Sobrien    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
44394630Sobrien    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
44494630Sobrien    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
44594630Sobrien    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
44694630Sobrien    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
44794630Sobrien    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
44894630Sobrien    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
44994630Sobrien    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
45094630Sobrien    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
45194630Sobrien    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
45294630Sobrien    { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
45394630Sobrien    { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
45494630Sobrien    { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
45594630Sobrien    { "nt",	tZONE,     HOUR(11) },	/* Nome */
45694630Sobrien    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
45794630Sobrien    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
45894630Sobrien    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
45994630Sobrien    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
46094630Sobrien    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
46194630Sobrien    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
46294630Sobrien    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
46394630Sobrien    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
46494630Sobrien    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
46594630Sobrien    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
46694630Sobrien    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
46794630Sobrien#if 0
46894630Sobrien    { "it",	tZONE,     -HOUR(3.5) },/* Iran */
46994630Sobrien#endif
47094630Sobrien    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
47194630Sobrien    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
47294630Sobrien#if 0
47394630Sobrien    { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
47494630Sobrien#endif
47594630Sobrien    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
47694630Sobrien#if	0
47794630Sobrien    /* For completeness.  NST is also Newfoundland Stanard, and SST is
47894630Sobrien     * also Swedish Summer. */
47994630Sobrien    { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
48094630Sobrien    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
48194630Sobrien#endif	/* 0 */
48294630Sobrien    { "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
48394630Sobrien    { "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
48494630Sobrien#if 0
48594630Sobrien    { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
48694630Sobrien#endif
48794630Sobrien    { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
48894630Sobrien    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
48994630Sobrien#if 0
49094630Sobrien    { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
49194630Sobrien    { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
49294630Sobrien#endif
49394630Sobrien    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
49494630Sobrien    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
49594630Sobrien    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
49694630Sobrien    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
49794630Sobrien    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
49894630Sobrien    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
49994630Sobrien    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
500116333Smarkm    {  NULL,	0,	   0 }
50194630Sobrien};
50294630Sobrien
50394630Sobrien/* Military timezone table. */
50494630Sobrienstatic TABLE const MilitaryTable[] = {
50594630Sobrien    { "a",	tZONE,	HOUR(  1) },
50694630Sobrien    { "b",	tZONE,	HOUR(  2) },
50794630Sobrien    { "c",	tZONE,	HOUR(  3) },
50894630Sobrien    { "d",	tZONE,	HOUR(  4) },
50994630Sobrien    { "e",	tZONE,	HOUR(  5) },
51094630Sobrien    { "f",	tZONE,	HOUR(  6) },
51194630Sobrien    { "g",	tZONE,	HOUR(  7) },
51294630Sobrien    { "h",	tZONE,	HOUR(  8) },
51394630Sobrien    { "i",	tZONE,	HOUR(  9) },
51494630Sobrien    { "k",	tZONE,	HOUR( 10) },
51594630Sobrien    { "l",	tZONE,	HOUR( 11) },
51694630Sobrien    { "m",	tZONE,	HOUR( 12) },
51794630Sobrien    { "n",	tZONE,	HOUR(- 1) },
51894630Sobrien    { "o",	tZONE,	HOUR(- 2) },
51994630Sobrien    { "p",	tZONE,	HOUR(- 3) },
52094630Sobrien    { "q",	tZONE,	HOUR(- 4) },
52194630Sobrien    { "r",	tZONE,	HOUR(- 5) },
52294630Sobrien    { "s",	tZONE,	HOUR(- 6) },
52394630Sobrien    { "t",	tZONE,	HOUR(- 7) },
52494630Sobrien    { "u",	tZONE,	HOUR(- 8) },
52594630Sobrien    { "v",	tZONE,	HOUR(- 9) },
52694630Sobrien    { "w",	tZONE,	HOUR(-10) },
52794630Sobrien    { "x",	tZONE,	HOUR(-11) },
52894630Sobrien    { "y",	tZONE,	HOUR(-12) },
52994630Sobrien    { "z",	tZONE,	HOUR(  0) },
530116333Smarkm    { NULL,	0,	0 }
53194630Sobrien};
53294630Sobrien
53394630Sobrien
53494630Sobrien
53594630Sobrien
53694630Sobrien/* ARGSUSED */
53794630Sobrienstatic int
538116333Smarkmyyerror(const char *s __unused)
53994630Sobrien{
54094630Sobrien  return 0;
54194630Sobrien}
54294630Sobrien
54394630Sobrien
54494630Sobrienstatic time_t
545116333SmarkmToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
54694630Sobrien{
54794630Sobrien    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
54894630Sobrien	return -1;
54994630Sobrien    switch (Meridian) {
55094630Sobrien    case MER24:
55194630Sobrien	if (Hours < 0 || Hours > 23)
55294630Sobrien	    return -1;
55394630Sobrien	return (Hours * 60L + Minutes) * 60L + Seconds;
55494630Sobrien    case MERam:
55594630Sobrien	if (Hours < 1 || Hours > 12)
55694630Sobrien	    return -1;
55794630Sobrien	if (Hours == 12)
55894630Sobrien	    Hours = 0;
55994630Sobrien	return (Hours * 60L + Minutes) * 60L + Seconds;
56094630Sobrien    case MERpm:
56194630Sobrien	if (Hours < 1 || Hours > 12)
56294630Sobrien	    return -1;
56394630Sobrien	if (Hours == 12)
56494630Sobrien	    Hours = 0;
56594630Sobrien	return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
56694630Sobrien    default:
56794630Sobrien	abort ();
56894630Sobrien    }
56994630Sobrien    /* NOTREACHED */
57094630Sobrien}
57194630Sobrien
57294630Sobrien
57394630Sobrien/* Year is either
57494630Sobrien   * A negative number, which means to use its absolute value (why?)
57594630Sobrien   * A number from 0 to 99, which means a year from 1900 to 1999, or
57694630Sobrien   * The actual year (>=100).  */
57794630Sobrienstatic time_t
578116333SmarkmConvert(time_t Month, time_t Day, time_t Year,
579116333Smarkm	time_t Hours, time_t Minutes, time_t Seconds,
580116333Smarkm	MERIDIAN Meridian, DSTMODE DSTmode)
58194630Sobrien{
58294630Sobrien    static int DaysInMonth[12] = {
58394630Sobrien	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
58494630Sobrien    };
58594630Sobrien    time_t	tod;
58694630Sobrien    time_t	Julian;
58794630Sobrien    int		i;
58894630Sobrien
58994630Sobrien    if (Year < 0)
59094630Sobrien	Year = -Year;
59194630Sobrien    if (Year < 69)
59294630Sobrien	Year += 2000;
59394630Sobrien    else if (Year < 100)
59494630Sobrien	Year += 1900;
59594630Sobrien    DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
59694630Sobrien		    ? 29 : 28;
59794630Sobrien    /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
59894630Sobrien       I'm too lazy to try to check for time_t overflow in another way.  */
59994630Sobrien    if (Year < EPOCH || Year > 2038
60094630Sobrien     || Month < 1 || Month > 12
60194630Sobrien     /* Lint fluff:  "conversion from long may lose accuracy" */
60294630Sobrien     || Day < 1 || Day > DaysInMonth[(int)--Month])
60394630Sobrien	return -1;
60494630Sobrien
60594630Sobrien    for (Julian = Day - 1, i = 0; i < Month; i++)
60694630Sobrien	Julian += DaysInMonth[i];
60794630Sobrien    for (i = EPOCH; i < Year; i++)
60894630Sobrien	Julian += 365 + (i % 4 == 0);
60994630Sobrien    Julian *= SECSPERDAY;
61094630Sobrien    Julian += yyTimezone * 60L;
61194630Sobrien    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
61294630Sobrien	return -1;
61394630Sobrien    Julian += tod;
61494630Sobrien    if (DSTmode == DSTon
61594630Sobrien     || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
61694630Sobrien	Julian -= 60 * 60;
61794630Sobrien    return Julian;
61894630Sobrien}
61994630Sobrien
62094630Sobrien
62194630Sobrienstatic time_t
622116333SmarkmDSTcorrect(time_t Start, time_t Future)
62394630Sobrien{
62494630Sobrien    time_t	StartDay;
62594630Sobrien    time_t	FutureDay;
62694630Sobrien
62794630Sobrien    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
62894630Sobrien    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
62994630Sobrien    return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
63094630Sobrien}
63194630Sobrien
63294630Sobrien
63394630Sobrienstatic time_t
634116333SmarkmRelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
63594630Sobrien{
63694630Sobrien    struct tm	*tm;
63794630Sobrien    time_t	now;
63894630Sobrien
63994630Sobrien    now = Start;
64094630Sobrien    tm = localtime(&now);
64194630Sobrien    now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
64294630Sobrien    now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
64394630Sobrien    return DSTcorrect(Start, now);
64494630Sobrien}
64594630Sobrien
64694630Sobrien
64794630Sobrienstatic time_t
648116333SmarkmRelativeMonth(time_t Start, time_t RelMonth)
64994630Sobrien{
65094630Sobrien    struct tm	*tm;
65194630Sobrien    time_t	Month;
65294630Sobrien    time_t	Year;
65394630Sobrien
65494630Sobrien    if (RelMonth == 0)
65594630Sobrien	return 0;
65694630Sobrien    tm = localtime(&Start);
65794630Sobrien    Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
65894630Sobrien    Year = Month / 12;
65994630Sobrien    Month = Month % 12 + 1;
66094630Sobrien    return DSTcorrect(Start,
66194630Sobrien	    Convert(Month, (time_t)tm->tm_mday, Year,
66294630Sobrien		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
66394630Sobrien		MER24, DSTmaybe));
66494630Sobrien}
66594630Sobrien
66694630Sobrien
66794630Sobrienstatic int
668116333SmarkmLookupWord(char *buff)
66994630Sobrien{
670116333Smarkm    char	*p;
671116333Smarkm    char	*q;
672116333Smarkm    const TABLE	*tp;
673116333Smarkm    int		i;
674116333Smarkm    int		abbrev;
67594630Sobrien
67694630Sobrien    /* Make it lowercase. */
67794630Sobrien    for (p = buff; *p; p++)
67894630Sobrien	if (isupper(*p))
67994630Sobrien	    *p = tolower(*p);
68094630Sobrien
68194630Sobrien    if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
68294630Sobrien	yylval.Meridian = MERam;
68394630Sobrien	return tMERIDIAN;
68494630Sobrien    }
68594630Sobrien    if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
68694630Sobrien	yylval.Meridian = MERpm;
68794630Sobrien	return tMERIDIAN;
68894630Sobrien    }
68994630Sobrien
69094630Sobrien    /* See if we have an abbreviation for a month. */
69194630Sobrien    if (strlen(buff) == 3)
69294630Sobrien	abbrev = 1;
69394630Sobrien    else if (strlen(buff) == 4 && buff[3] == '.') {
69494630Sobrien	abbrev = 1;
69594630Sobrien	buff[3] = '\0';
69694630Sobrien    }
69794630Sobrien    else
69894630Sobrien	abbrev = 0;
69994630Sobrien
70094630Sobrien    for (tp = MonthDayTable; tp->name; tp++) {
70194630Sobrien	if (abbrev) {
70294630Sobrien	    if (strncmp(buff, tp->name, 3) == 0) {
70394630Sobrien		yylval.Number = tp->value;
70494630Sobrien		return tp->type;
70594630Sobrien	    }
70694630Sobrien	}
70794630Sobrien	else if (strcmp(buff, tp->name) == 0) {
70894630Sobrien	    yylval.Number = tp->value;
70994630Sobrien	    return tp->type;
71094630Sobrien	}
71194630Sobrien    }
71294630Sobrien
71394630Sobrien    for (tp = TimezoneTable; tp->name; tp++)
71494630Sobrien	if (strcmp(buff, tp->name) == 0) {
71594630Sobrien	    yylval.Number = tp->value;
71694630Sobrien	    return tp->type;
71794630Sobrien	}
71894630Sobrien
71994630Sobrien    if (strcmp(buff, "dst") == 0)
72094630Sobrien	return tDST;
72194630Sobrien
72294630Sobrien    for (tp = UnitsTable; tp->name; tp++)
72394630Sobrien	if (strcmp(buff, tp->name) == 0) {
72494630Sobrien	    yylval.Number = tp->value;
72594630Sobrien	    return tp->type;
72694630Sobrien	}
72794630Sobrien
72894630Sobrien    /* Strip off any plural and try the units table again. */
72994630Sobrien    i = strlen(buff) - 1;
73094630Sobrien    if (buff[i] == 's') {
73194630Sobrien	buff[i] = '\0';
73294630Sobrien	for (tp = UnitsTable; tp->name; tp++)
73394630Sobrien	    if (strcmp(buff, tp->name) == 0) {
73494630Sobrien		yylval.Number = tp->value;
73594630Sobrien		return tp->type;
73694630Sobrien	    }
73794630Sobrien	buff[i] = 's';		/* Put back for "this" in OtherTable. */
73894630Sobrien    }
73994630Sobrien
74094630Sobrien    for (tp = OtherTable; tp->name; tp++)
74194630Sobrien	if (strcmp(buff, tp->name) == 0) {
74294630Sobrien	    yylval.Number = tp->value;
74394630Sobrien	    return tp->type;
74494630Sobrien	}
74594630Sobrien
74694630Sobrien    /* Military timezones. */
74794630Sobrien    if (buff[1] == '\0' && isalpha(*buff)) {
74894630Sobrien	for (tp = MilitaryTable; tp->name; tp++)
74994630Sobrien	    if (strcmp(buff, tp->name) == 0) {
75094630Sobrien		yylval.Number = tp->value;
75194630Sobrien		return tp->type;
75294630Sobrien	    }
75394630Sobrien    }
75494630Sobrien
75594630Sobrien    /* Drop out any periods and try the timezone table again. */
75694630Sobrien    for (i = 0, p = q = buff; *q; q++)
75794630Sobrien	if (*q != '.')
75894630Sobrien	    *p++ = *q;
75994630Sobrien	else
76094630Sobrien	    i++;
76194630Sobrien    *p = '\0';
76294630Sobrien    if (i)
76394630Sobrien	for (tp = TimezoneTable; tp->name; tp++)
76494630Sobrien	    if (strcmp(buff, tp->name) == 0) {
76594630Sobrien		yylval.Number = tp->value;
76694630Sobrien		return tp->type;
76794630Sobrien	    }
76894630Sobrien
76994630Sobrien    return tID;
77094630Sobrien}
77194630Sobrien
77294630Sobrien
77394630Sobrienstatic int
774116333Smarkmyylex(void)
77594630Sobrien{
776116333Smarkm    char	c;
777116333Smarkm    char	*p;
778116333Smarkm    char	buff[20];
779116333Smarkm    int		Count;
780116333Smarkm    int		sign;
78194630Sobrien
78294630Sobrien    for ( ; ; ) {
78394630Sobrien	while (isspace(*yyInput))
78494630Sobrien	    yyInput++;
78594630Sobrien
78694630Sobrien	if (isdigit(c = *yyInput) || c == '-' || c == '+') {
78794630Sobrien	    if (c == '-' || c == '+') {
78894630Sobrien		sign = c == '-' ? -1 : 1;
78994630Sobrien		if (!isdigit(*++yyInput))
79094630Sobrien		    /* skip the '-' sign */
79194630Sobrien		    continue;
79294630Sobrien	    }
79394630Sobrien	    else
79494630Sobrien		sign = 0;
79594630Sobrien	    for (yylval.Number = 0; isdigit(c = *yyInput++); )
79694630Sobrien		yylval.Number = 10 * yylval.Number + c - '0';
79794630Sobrien	    yyInput--;
79894630Sobrien	    if (sign < 0)
79994630Sobrien		yylval.Number = -yylval.Number;
80094630Sobrien	    return sign ? tSNUMBER : tUNUMBER;
80194630Sobrien	}
80294630Sobrien	if (isalpha(c)) {
80394630Sobrien	    for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
80494630Sobrien		if (p < &buff[sizeof buff - 1])
80594630Sobrien		    *p++ = c;
80694630Sobrien	    *p = '\0';
80794630Sobrien	    yyInput--;
80894630Sobrien	    return LookupWord(buff);
80994630Sobrien	}
81094630Sobrien	if (c != '(')
81194630Sobrien	    return *yyInput++;
81294630Sobrien	Count = 0;
81394630Sobrien	do {
81494630Sobrien	    c = *yyInput++;
81594630Sobrien	    if (c == '\0')
81694630Sobrien		return c;
81794630Sobrien	    if (c == '(')
81894630Sobrien		Count++;
81994630Sobrien	    else if (c == ')')
82094630Sobrien		Count--;
82194630Sobrien	} while (Count > 0);
82294630Sobrien    }
82394630Sobrien}
82494630Sobrien
82594630Sobrien#define TM_YEAR_ORIGIN 1900
82694630Sobrien
82794630Sobrien/* Yield A - B, measured in seconds.  */
82894630Sobrienstatic long
829116333Smarkmdifftm (struct tm *a, struct tm *b)
83094630Sobrien{
83194630Sobrien  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
83294630Sobrien  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
83394630Sobrien  int days = (
83494630Sobrien	      /* difference in day of year */
83594630Sobrien	      a->tm_yday - b->tm_yday
83694630Sobrien	      /* + intervening leap days */
83794630Sobrien	      +  ((ay >> 2) - (by >> 2))
83894630Sobrien	      -  (ay/100 - by/100)
83994630Sobrien	      +  ((ay/100 >> 2) - (by/100 >> 2))
84094630Sobrien	      /* + difference in years * 365 */
84194630Sobrien	      +  (long)(ay-by) * 365
84294630Sobrien	      );
84394630Sobrien  return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
84494630Sobrien	      + (a->tm_min - b->tm_min))
84594630Sobrien	  + (a->tm_sec - b->tm_sec));
84694630Sobrien}
84794630Sobrien
84894630Sobrientime_t
849203723Sedget_date(char *p)
85094630Sobrien{
851203723Sed    struct tm		*tm, *gmt_ptr, gmt;
852203723Sed    int			tzoff;
85394630Sobrien    time_t		Start;
85494630Sobrien    time_t		tod;
85594630Sobrien    time_t nowtime;
85694630Sobrien
857149453Sroberto    bzero (&gmt, sizeof(struct tm));
85894630Sobrien    yyInput = p;
85994630Sobrien
860203723Sed    (void)time (&nowtime);
86194630Sobrien
862203723Sed    gmt_ptr = gmtime (&nowtime);
863203723Sed    if (gmt_ptr != NULL)
864203723Sed    {
865203723Sed	/* Make a copy, in case localtime modifies *tm (I think
866203723Sed	   that comment now applies to *gmt_ptr, but I am too
867203723Sed	   lazy to dig into how gmtime and locatime allocate the
868203723Sed	   structures they return pointers to).  */
869203723Sed	gmt = *gmt_ptr;
870203723Sed    }
87194630Sobrien
872203723Sed    if (! (tm = localtime (&nowtime)))
873203723Sed	return -1;
87494630Sobrien
875203723Sed    if (gmt_ptr != NULL)
876203723Sed	tzoff = difftm (&gmt, tm) / 60;
87794630Sobrien    else
878203723Sed	/* We are on a system like VMS, where the system clock is
879203723Sed	   in local time and the system has no concept of timezones.
880203723Sed	   Hopefully we can fake this out (for the case in which the
881203723Sed	   user specifies no timezone) by just saying the timezone
882203723Sed	   is zero.  */
883203723Sed	tzoff = 0;
88494630Sobrien
885203723Sed    if(tm->tm_isdst)
886203723Sed	tzoff += 60;
887203723Sed
88894630Sobrien    tm = localtime(&nowtime);
88994630Sobrien    yyYear = tm->tm_year + 1900;
89094630Sobrien    yyMonth = tm->tm_mon + 1;
89194630Sobrien    yyDay = tm->tm_mday;
892203723Sed    yyTimezone = tzoff;
89394630Sobrien    yyDSTmode = DSTmaybe;
89494630Sobrien    yyHour = 0;
89594630Sobrien    yyMinutes = 0;
89694630Sobrien    yySeconds = 0;
89794630Sobrien    yyMeridian = MER24;
89894630Sobrien    yyRelSeconds = 0;
89994630Sobrien    yyRelMonth = 0;
90094630Sobrien    yyHaveDate = 0;
90194630Sobrien    yyHaveDay = 0;
90294630Sobrien    yyHaveRel = 0;
90394630Sobrien    yyHaveTime = 0;
90494630Sobrien    yyHaveZone = 0;
90594630Sobrien
90694630Sobrien    if (yyparse()
90794630Sobrien     || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
90894630Sobrien	return -1;
90994630Sobrien
91094630Sobrien    if (yyHaveDate || yyHaveTime || yyHaveDay) {
91194630Sobrien	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
91294630Sobrien		    yyMeridian, yyDSTmode);
91394630Sobrien	if (Start < 0)
91494630Sobrien	    return -1;
91594630Sobrien    }
91694630Sobrien    else {
91794630Sobrien	Start = nowtime;
91894630Sobrien	if (!yyHaveRel)
91994630Sobrien	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
92094630Sobrien    }
92194630Sobrien
92294630Sobrien    Start += yyRelSeconds;
92394630Sobrien    Start += RelativeMonth(Start, yyRelMonth);
92494630Sobrien
92594630Sobrien    if (yyHaveDay && !yyHaveDate) {
92694630Sobrien	tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
92794630Sobrien	Start += tod;
92894630Sobrien    }
92994630Sobrien
93094630Sobrien    /* Have to do *something* with a legitimate -1 so it's distinguishable
93194630Sobrien     * from the error return value.  (Alternately could set errno on error.) */
93294630Sobrien    return Start == -1 ? 0 : Start;
93394630Sobrien}
93494630Sobrien
93594630Sobrien
93694630Sobrien#if	defined(TEST)
93794630Sobrien
93894630Sobrien/* ARGSUSED */
93994630Sobrienint
940116333Smarkmmain(int ac, char *av[])
94194630Sobrien{
94294630Sobrien    char	buff[128];
94394630Sobrien    time_t	d;
94494630Sobrien
94594630Sobrien    (void)printf("Enter date, or blank line to exit.\n\t> ");
94694630Sobrien    (void)fflush(stdout);
94794630Sobrien    while (gets(buff) && buff[0]) {
948203723Sed	d = get_date(buff);
94994630Sobrien	if (d == -1)
95094630Sobrien	    (void)printf("Bad format - couldn't convert.\n");
95194630Sobrien	else
95294630Sobrien	    (void)printf("%s", ctime(&d));
95394630Sobrien	(void)printf("\t> ");
95494630Sobrien	(void)fflush(stdout);
95594630Sobrien    }
95694630Sobrien    exit(0);
95794630Sobrien    /* NOTREACHED */
95894630Sobrien}
95994630Sobrien#endif	/* defined(TEST) */
960