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