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