xref: /minix3/lib/libutil/parsedate.y (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
10c3983b2SBen Gras %{
20c3983b2SBen Gras /*
30c3983b2SBen Gras **  Originally written by Steven M. Bellovin <smb@research.att.com> while
40c3983b2SBen Gras **  at the University of North Carolina at Chapel Hill.  Later tweaked by
50c3983b2SBen Gras **  a couple of people on Usenet.  Completely overhauled by Rich $alz
60c3983b2SBen Gras **  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
70c3983b2SBen Gras **
80c3983b2SBen Gras **  This grammar has 10 shift/reduce conflicts.
90c3983b2SBen Gras **
100c3983b2SBen Gras **  This code is in the public domain and has no copyright.
110c3983b2SBen Gras */
120c3983b2SBen Gras /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
130c3983b2SBen Gras /* SUPPRESS 288 on yyerrlab *//* Label unused */
140c3983b2SBen Gras 
1584d9c625SLionel Sambuc #include <sys/cdefs.h>
1684d9c625SLionel Sambuc #ifdef __RCSID
17*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: parsedate.y,v 1.20 2014/10/08 17:38:28 apb Exp $");
1884d9c625SLionel Sambuc #endif
1984d9c625SLionel Sambuc 
200c3983b2SBen Gras #include <stdio.h>
210c3983b2SBen Gras #include <ctype.h>
2284d9c625SLionel Sambuc #include <errno.h>
230c3983b2SBen Gras #include <string.h>
240c3983b2SBen Gras #include <time.h>
250c3983b2SBen Gras #include <util.h>
260c3983b2SBen Gras #include <stdlib.h>
270c3983b2SBen Gras 
280c3983b2SBen Gras /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
290c3983b2SBen Gras    releases):
300c3983b2SBen Gras 
310c3983b2SBen Gras    We don't want to mess with all the portability hassles of alloca.
320c3983b2SBen Gras    In particular, most (all?) versions of bison will use alloca in
330c3983b2SBen Gras    their parser.  If bison works on your system (e.g. it should work
340c3983b2SBen Gras    with gcc), then go ahead and use it, but the more general solution
350c3983b2SBen Gras    is to use byacc instead of bison, which should generate a portable
360c3983b2SBen Gras    parser.  I played with adding "#define alloca dont_use_alloca", to
370c3983b2SBen Gras    give an error if the parser generator uses alloca (and thus detect
380c3983b2SBen Gras    unportable parsedate.c's), but that seems to cause as many problems
390c3983b2SBen Gras    as it solves.  */
400c3983b2SBen Gras 
410c3983b2SBen Gras #define EPOCH		1970
420c3983b2SBen Gras #define HOUR(x)		((time_t)(x) * 60)
430c3983b2SBen Gras #define SECSPERDAY	(24L * 60L * 60L)
440c3983b2SBen Gras 
45*0a6a1f1dSLionel Sambuc #define USE_LOCAL_TIME	99999 /* special case for Convert() and yyTimezone */
460c3983b2SBen Gras 
470c3983b2SBen Gras /*
480c3983b2SBen Gras **  An entry in the lexical lookup table.
490c3983b2SBen Gras */
500c3983b2SBen Gras typedef struct _TABLE {
510c3983b2SBen Gras     const char	*name;
520c3983b2SBen Gras     int		type;
530c3983b2SBen Gras     time_t	value;
540c3983b2SBen Gras } TABLE;
550c3983b2SBen Gras 
560c3983b2SBen Gras 
570c3983b2SBen Gras /*
580c3983b2SBen Gras **  Daylight-savings mode:  on, off, or not yet known.
590c3983b2SBen Gras */
600c3983b2SBen Gras typedef enum _DSTMODE {
610c3983b2SBen Gras     DSTon, DSToff, DSTmaybe
620c3983b2SBen Gras } DSTMODE;
630c3983b2SBen Gras 
640c3983b2SBen Gras /*
650c3983b2SBen Gras **  Meridian:  am, pm, or 24-hour style.
660c3983b2SBen Gras */
670c3983b2SBen Gras typedef enum _MERIDIAN {
680c3983b2SBen Gras     MERam, MERpm, MER24
690c3983b2SBen Gras } MERIDIAN;
700c3983b2SBen Gras 
710c3983b2SBen Gras 
720c3983b2SBen Gras struct dateinfo {
73*0a6a1f1dSLionel Sambuc 	DSTMODE	yyDSTmode;	/* DST on/off/maybe */
740c3983b2SBen Gras 	time_t	yyDayOrdinal;
750c3983b2SBen Gras 	time_t	yyDayNumber;
760c3983b2SBen Gras 	int	yyHaveDate;
77*0a6a1f1dSLionel Sambuc 	int	yyHaveFullYear;	/* if true, year is not abbreviated. */
78*0a6a1f1dSLionel Sambuc 				/* if false, need to call AdjustYear(). */
790c3983b2SBen Gras 	int	yyHaveDay;
800c3983b2SBen Gras 	int	yyHaveRel;
810c3983b2SBen Gras 	int	yyHaveTime;
820c3983b2SBen Gras 	int	yyHaveZone;
83*0a6a1f1dSLionel Sambuc 	time_t	yyTimezone;	/* Timezone as minutes ahead/east of UTC */
84*0a6a1f1dSLionel Sambuc 	time_t	yyDay;		/* Day of month [1-31] */
85*0a6a1f1dSLionel Sambuc 	time_t	yyHour;		/* Hour of day [0-24] or [1-12] */
86*0a6a1f1dSLionel Sambuc 	time_t	yyMinutes;	/* Minute of hour [0-59] */
87*0a6a1f1dSLionel Sambuc 	time_t	yyMonth;	/* Month of year [1-12] */
88*0a6a1f1dSLionel Sambuc 	time_t	yySeconds;	/* Second of minute [0-60] */
89*0a6a1f1dSLionel Sambuc 	time_t	yyYear;		/* Year, see also yyHaveFullYear */
90*0a6a1f1dSLionel Sambuc 	MERIDIAN yyMeridian;	/* Interpret yyHour as AM/PM/24 hour clock */
910c3983b2SBen Gras 	time_t	yyRelMonth;
920c3983b2SBen Gras 	time_t	yyRelSeconds;
930c3983b2SBen Gras };
940c3983b2SBen Gras %}
950c3983b2SBen Gras 
960c3983b2SBen Gras %union {
970c3983b2SBen Gras     time_t		Number;
980c3983b2SBen Gras     enum _MERIDIAN	Meridian;
990c3983b2SBen Gras }
1000c3983b2SBen Gras 
1010c3983b2SBen Gras %token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
1020c3983b2SBen Gras %token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN
1030c3983b2SBen Gras 
1040c3983b2SBen Gras %type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
1050c3983b2SBen Gras %type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
1060c3983b2SBen Gras %type	<Meridian>	tMERIDIAN o_merid
1070c3983b2SBen Gras 
1080c3983b2SBen Gras %parse-param	{ struct dateinfo *param }
1090c3983b2SBen Gras %parse-param 	{ const char **yyInput }
1100c3983b2SBen Gras %lex-param	{ const char **yyInput }
1110c3983b2SBen Gras %pure-parser
1120c3983b2SBen Gras 
1130c3983b2SBen Gras %%
1140c3983b2SBen Gras 
1150c3983b2SBen Gras spec	: /* NULL */
1160c3983b2SBen Gras 	| spec item
1170c3983b2SBen Gras 	;
1180c3983b2SBen Gras 
1190c3983b2SBen Gras item	: time {
1200c3983b2SBen Gras 	    param->yyHaveTime++;
1210c3983b2SBen Gras 	}
122*0a6a1f1dSLionel Sambuc 	| time_numericzone {
123*0a6a1f1dSLionel Sambuc 	    param->yyHaveTime++;
124*0a6a1f1dSLionel Sambuc 	    param->yyHaveZone++;
125*0a6a1f1dSLionel Sambuc 	}
1260c3983b2SBen Gras 	| zone {
1270c3983b2SBen Gras 	    param->yyHaveZone++;
1280c3983b2SBen Gras 	}
1290c3983b2SBen Gras 	| date {
1300c3983b2SBen Gras 	    param->yyHaveDate++;
1310c3983b2SBen Gras 	}
1320c3983b2SBen Gras 	| day {
1330c3983b2SBen Gras 	    param->yyHaveDay++;
1340c3983b2SBen Gras 	}
1350c3983b2SBen Gras 	| rel {
1360c3983b2SBen Gras 	    param->yyHaveRel++;
1370c3983b2SBen Gras 	}
1380c3983b2SBen Gras 	| cvsstamp {
1390c3983b2SBen Gras 	    param->yyHaveTime++;
1400c3983b2SBen Gras 	    param->yyHaveDate++;
1410c3983b2SBen Gras 	    param->yyHaveZone++;
1420c3983b2SBen Gras 	}
1430c3983b2SBen Gras 	| epochdate {
1440c3983b2SBen Gras 	    param->yyHaveTime++;
1450c3983b2SBen Gras 	    param->yyHaveDate++;
1460c3983b2SBen Gras 	    param->yyHaveZone++;
1470c3983b2SBen Gras 	}
1480c3983b2SBen Gras 	| number
1490c3983b2SBen Gras 	;
1500c3983b2SBen Gras 
1510c3983b2SBen Gras cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
1520c3983b2SBen Gras 	    param->yyYear = $1;
1530c3983b2SBen Gras 	    if (param->yyYear < 100) param->yyYear += 1900;
154*0a6a1f1dSLionel Sambuc 	    param->yyHaveFullYear = 1;
1550c3983b2SBen Gras 	    param->yyMonth = $3;
1560c3983b2SBen Gras 	    param->yyDay = $5;
1570c3983b2SBen Gras 	    param->yyHour = $7;
1580c3983b2SBen Gras 	    param->yyMinutes = $9;
1590c3983b2SBen Gras 	    param->yySeconds = $11;
1600c3983b2SBen Gras 	    param->yyDSTmode = DSToff;
1610c3983b2SBen Gras 	    param->yyTimezone = 0;
1620c3983b2SBen Gras 	}
1630c3983b2SBen Gras 	;
1640c3983b2SBen Gras 
16584d9c625SLionel Sambuc epochdate: AT_SIGN at_number {
16684d9c625SLionel Sambuc             time_t    when = $<Number>2;
1670c3983b2SBen Gras             struct tm tmbuf;
1680c3983b2SBen Gras             if (gmtime_r(&when, &tmbuf) != NULL) {
1690c3983b2SBen Gras 		param->yyYear = tmbuf.tm_year + 1900;
1700c3983b2SBen Gras 		param->yyMonth = tmbuf.tm_mon + 1;
1710c3983b2SBen Gras 		param->yyDay = tmbuf.tm_mday;
1720c3983b2SBen Gras 
1730c3983b2SBen Gras 		param->yyHour = tmbuf.tm_hour;
1740c3983b2SBen Gras 		param->yyMinutes = tmbuf.tm_min;
1750c3983b2SBen Gras 		param->yySeconds = tmbuf.tm_sec;
1760c3983b2SBen Gras 	    } else {
1770c3983b2SBen Gras 		param->yyYear = EPOCH;
1780c3983b2SBen Gras 		param->yyMonth = 1;
1790c3983b2SBen Gras 		param->yyDay = 1;
1800c3983b2SBen Gras 
1810c3983b2SBen Gras 		param->yyHour = 0;
1820c3983b2SBen Gras 		param->yyMinutes = 0;
1830c3983b2SBen Gras 		param->yySeconds = 0;
1840c3983b2SBen Gras 	    }
185*0a6a1f1dSLionel Sambuc 	    param->yyHaveFullYear = 1;
1860c3983b2SBen Gras 	    param->yyDSTmode = DSToff;
1870c3983b2SBen Gras 	    param->yyTimezone = 0;
1880c3983b2SBen Gras 	}
1890c3983b2SBen Gras 	;
1900c3983b2SBen Gras 
19184d9c625SLionel Sambuc at_number : tUNUMBER | tSNUMBER ;
19284d9c625SLionel Sambuc 
1930c3983b2SBen Gras time	: tUNUMBER tMERIDIAN {
1940c3983b2SBen Gras 	    param->yyHour = $1;
1950c3983b2SBen Gras 	    param->yyMinutes = 0;
1960c3983b2SBen Gras 	    param->yySeconds = 0;
1970c3983b2SBen Gras 	    param->yyMeridian = $2;
1980c3983b2SBen Gras 	}
1990c3983b2SBen Gras 	| tUNUMBER ':' tUNUMBER o_merid {
2000c3983b2SBen Gras 	    param->yyHour = $1;
2010c3983b2SBen Gras 	    param->yyMinutes = $3;
2020c3983b2SBen Gras 	    param->yySeconds = 0;
2030c3983b2SBen Gras 	    param->yyMeridian = $4;
2040c3983b2SBen Gras 	}
2050c3983b2SBen Gras 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
2060c3983b2SBen Gras 	    param->yyHour = $1;
2070c3983b2SBen Gras 	    param->yyMinutes = $3;
2080c3983b2SBen Gras 	    param->yySeconds = $5;
2090c3983b2SBen Gras 	    param->yyMeridian = $6;
2100c3983b2SBen Gras 	}
211*0a6a1f1dSLionel Sambuc 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
212*0a6a1f1dSLionel Sambuc 	    param->yyHour = $1;
213*0a6a1f1dSLionel Sambuc 	    param->yyMinutes = $3;
214*0a6a1f1dSLionel Sambuc 	    param->yySeconds = $5;
215*0a6a1f1dSLionel Sambuc 	    param->yyMeridian = MER24;
216*0a6a1f1dSLionel Sambuc /* XXX: Do nothing with millis */
217*0a6a1f1dSLionel Sambuc 	}
218*0a6a1f1dSLionel Sambuc 	;
219*0a6a1f1dSLionel Sambuc 
220*0a6a1f1dSLionel Sambuc time_numericzone : tUNUMBER ':' tUNUMBER tSNUMBER {
221*0a6a1f1dSLionel Sambuc 	    param->yyHour = $1;
222*0a6a1f1dSLionel Sambuc 	    param->yyMinutes = $3;
223*0a6a1f1dSLionel Sambuc 	    param->yyMeridian = MER24;
224*0a6a1f1dSLionel Sambuc 	    param->yyDSTmode = DSToff;
225*0a6a1f1dSLionel Sambuc 	    param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
226*0a6a1f1dSLionel Sambuc 	}
2270c3983b2SBen Gras 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
2280c3983b2SBen Gras 	    param->yyHour = $1;
2290c3983b2SBen Gras 	    param->yyMinutes = $3;
2300c3983b2SBen Gras 	    param->yySeconds = $5;
2310c3983b2SBen Gras 	    param->yyMeridian = MER24;
2320c3983b2SBen Gras 	    param->yyDSTmode = DSToff;
2330c3983b2SBen Gras 	    param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
2340c3983b2SBen Gras 	}
2350c3983b2SBen Gras 	;
2360c3983b2SBen Gras 
2370c3983b2SBen Gras zone	: tZONE {
2380c3983b2SBen Gras 	    param->yyTimezone = $1;
2390c3983b2SBen Gras 	    param->yyDSTmode = DSToff;
2400c3983b2SBen Gras 	}
2410c3983b2SBen Gras 	| tDAYZONE {
2420c3983b2SBen Gras 	    param->yyTimezone = $1;
2430c3983b2SBen Gras 	    param->yyDSTmode = DSTon;
2440c3983b2SBen Gras 	}
2450c3983b2SBen Gras 	|
2460c3983b2SBen Gras 	  tZONE tDST {
2470c3983b2SBen Gras 	    param->yyTimezone = $1;
2480c3983b2SBen Gras 	    param->yyDSTmode = DSTon;
2490c3983b2SBen Gras 	}
2500c3983b2SBen Gras 	;
2510c3983b2SBen Gras 
2520c3983b2SBen Gras day	: tDAY {
2530c3983b2SBen Gras 	    param->yyDayOrdinal = 1;
2540c3983b2SBen Gras 	    param->yyDayNumber = $1;
2550c3983b2SBen Gras 	}
2560c3983b2SBen Gras 	| tDAY ',' {
2570c3983b2SBen Gras 	    param->yyDayOrdinal = 1;
2580c3983b2SBen Gras 	    param->yyDayNumber = $1;
2590c3983b2SBen Gras 	}
2600c3983b2SBen Gras 	| tUNUMBER tDAY {
2610c3983b2SBen Gras 	    param->yyDayOrdinal = $1;
2620c3983b2SBen Gras 	    param->yyDayNumber = $2;
2630c3983b2SBen Gras 	}
2640c3983b2SBen Gras 	;
2650c3983b2SBen Gras 
2660c3983b2SBen Gras date	: tUNUMBER '/' tUNUMBER {
2670c3983b2SBen Gras 	    param->yyMonth = $1;
2680c3983b2SBen Gras 	    param->yyDay = $3;
2690c3983b2SBen Gras 	}
2700c3983b2SBen Gras 	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
2710c3983b2SBen Gras 	    if ($1 >= 100) {
2720c3983b2SBen Gras 		param->yyYear = $1;
2730c3983b2SBen Gras 		param->yyMonth = $3;
2740c3983b2SBen Gras 		param->yyDay = $5;
2750c3983b2SBen Gras 	    } else {
2760c3983b2SBen Gras 		param->yyMonth = $1;
2770c3983b2SBen Gras 		param->yyDay = $3;
2780c3983b2SBen Gras 		param->yyYear = $5;
2790c3983b2SBen Gras 	    }
2800c3983b2SBen Gras 	}
2810c3983b2SBen Gras 	| tUNUMBER tSNUMBER tSNUMBER {
2820c3983b2SBen Gras 	    /* ISO 8601 format.  yyyy-mm-dd.  */
2830c3983b2SBen Gras 	    param->yyYear = $1;
284*0a6a1f1dSLionel Sambuc 	    param->yyHaveFullYear = 1;
2850c3983b2SBen Gras 	    param->yyMonth = -$2;
2860c3983b2SBen Gras 	    param->yyDay = -$3;
2870c3983b2SBen Gras 	}
2880c3983b2SBen Gras 	| tUNUMBER tMONTH tSNUMBER {
2890c3983b2SBen Gras 	    /* e.g. 17-JUN-1992.  */
2900c3983b2SBen Gras 	    param->yyDay = $1;
2910c3983b2SBen Gras 	    param->yyMonth = $2;
2920c3983b2SBen Gras 	    param->yyYear = -$3;
2930c3983b2SBen Gras 	}
2940c3983b2SBen Gras 	| tMONTH tUNUMBER {
2950c3983b2SBen Gras 	    param->yyMonth = $1;
2960c3983b2SBen Gras 	    param->yyDay = $2;
2970c3983b2SBen Gras 	}
2980c3983b2SBen Gras 	| tMONTH tUNUMBER ',' tUNUMBER {
2990c3983b2SBen Gras 	    param->yyMonth = $1;
3000c3983b2SBen Gras 	    param->yyDay = $2;
3010c3983b2SBen Gras 	    param->yyYear = $4;
3020c3983b2SBen Gras 	}
3030c3983b2SBen Gras 	| tUNUMBER tMONTH {
3040c3983b2SBen Gras 	    param->yyMonth = $2;
3050c3983b2SBen Gras 	    param->yyDay = $1;
3060c3983b2SBen Gras 	}
3070c3983b2SBen Gras 	| tUNUMBER tMONTH tUNUMBER {
3080c3983b2SBen Gras 	    param->yyMonth = $2;
3090c3983b2SBen Gras 	    param->yyDay = $1;
3100c3983b2SBen Gras 	    param->yyYear = $3;
3110c3983b2SBen Gras 	}
3120c3983b2SBen Gras 	;
3130c3983b2SBen Gras 
3140c3983b2SBen Gras rel	: relunit tAGO {
3150c3983b2SBen Gras 	    param->yyRelSeconds = -param->yyRelSeconds;
3160c3983b2SBen Gras 	    param->yyRelMonth = -param->yyRelMonth;
3170c3983b2SBen Gras 	}
3180c3983b2SBen Gras 	| relunit
3190c3983b2SBen Gras 	;
3200c3983b2SBen Gras 
3210c3983b2SBen Gras relunit	: tUNUMBER tMINUTE_UNIT {
3220c3983b2SBen Gras 	    param->yyRelSeconds += $1 * $2 * 60L;
3230c3983b2SBen Gras 	}
3240c3983b2SBen Gras 	| tSNUMBER tMINUTE_UNIT {
3250c3983b2SBen Gras 	    param->yyRelSeconds += $1 * $2 * 60L;
3260c3983b2SBen Gras 	}
3270c3983b2SBen Gras 	| tMINUTE_UNIT {
3280c3983b2SBen Gras 	    param->yyRelSeconds += $1 * 60L;
3290c3983b2SBen Gras 	}
3300c3983b2SBen Gras 	| tSNUMBER tSEC_UNIT {
3310c3983b2SBen Gras 	    param->yyRelSeconds += $1;
3320c3983b2SBen Gras 	}
3330c3983b2SBen Gras 	| tUNUMBER tSEC_UNIT {
3340c3983b2SBen Gras 	    param->yyRelSeconds += $1;
3350c3983b2SBen Gras 	}
3360c3983b2SBen Gras 	| tSEC_UNIT {
3370c3983b2SBen Gras 	    param->yyRelSeconds++;
3380c3983b2SBen Gras 	}
3390c3983b2SBen Gras 	| tSNUMBER tMONTH_UNIT {
3400c3983b2SBen Gras 	    param->yyRelMonth += $1 * $2;
3410c3983b2SBen Gras 	}
3420c3983b2SBen Gras 	| tUNUMBER tMONTH_UNIT {
3430c3983b2SBen Gras 	    param->yyRelMonth += $1 * $2;
3440c3983b2SBen Gras 	}
3450c3983b2SBen Gras 	| tMONTH_UNIT {
3460c3983b2SBen Gras 	    param->yyRelMonth += $1;
3470c3983b2SBen Gras 	}
3480c3983b2SBen Gras 	;
3490c3983b2SBen Gras 
3500c3983b2SBen Gras number	: tUNUMBER {
3510c3983b2SBen Gras 	    if (param->yyHaveTime && param->yyHaveDate && !param->yyHaveRel)
3520c3983b2SBen Gras 		param->yyYear = $1;
3530c3983b2SBen Gras 	    else {
3540c3983b2SBen Gras 		if($1>10000) {
3550c3983b2SBen Gras 		    param->yyHaveDate++;
3560c3983b2SBen Gras 		    param->yyDay= ($1)%100;
3570c3983b2SBen Gras 		    param->yyMonth= ($1/100)%100;
3580c3983b2SBen Gras 		    param->yyYear = $1/10000;
3590c3983b2SBen Gras 		}
3600c3983b2SBen Gras 		else {
3610c3983b2SBen Gras 		    param->yyHaveTime++;
3620c3983b2SBen Gras 		    if ($1 < 100) {
3630c3983b2SBen Gras 			param->yyHour = $1;
3640c3983b2SBen Gras 			param->yyMinutes = 0;
3650c3983b2SBen Gras 		    }
3660c3983b2SBen Gras 		    else {
3670c3983b2SBen Gras 		    	param->yyHour = $1 / 100;
3680c3983b2SBen Gras 		    	param->yyMinutes = $1 % 100;
3690c3983b2SBen Gras 		    }
3700c3983b2SBen Gras 		    param->yySeconds = 0;
3710c3983b2SBen Gras 		    param->yyMeridian = MER24;
3720c3983b2SBen Gras 	        }
3730c3983b2SBen Gras 	    }
3740c3983b2SBen Gras 	}
3750c3983b2SBen Gras 	;
3760c3983b2SBen Gras 
3770c3983b2SBen Gras o_merid	: /* NULL */ {
3780c3983b2SBen Gras 	    $$ = MER24;
3790c3983b2SBen Gras 	}
3800c3983b2SBen Gras 	| tMERIDIAN {
3810c3983b2SBen Gras 	    $$ = $1;
3820c3983b2SBen Gras 	}
3830c3983b2SBen Gras 	;
3840c3983b2SBen Gras 
3850c3983b2SBen Gras %%
3860c3983b2SBen Gras 
3870c3983b2SBen Gras /* Month and day table. */
38884d9c625SLionel Sambuc static const TABLE MonthDayTable[] = {
3890c3983b2SBen Gras     { "january",	tMONTH,  1 },
3900c3983b2SBen Gras     { "february",	tMONTH,  2 },
3910c3983b2SBen Gras     { "march",		tMONTH,  3 },
3920c3983b2SBen Gras     { "april",		tMONTH,  4 },
3930c3983b2SBen Gras     { "may",		tMONTH,  5 },
3940c3983b2SBen Gras     { "june",		tMONTH,  6 },
3950c3983b2SBen Gras     { "july",		tMONTH,  7 },
3960c3983b2SBen Gras     { "august",		tMONTH,  8 },
3970c3983b2SBen Gras     { "september",	tMONTH,  9 },
3980c3983b2SBen Gras     { "sept",		tMONTH,  9 },
3990c3983b2SBen Gras     { "october",	tMONTH, 10 },
4000c3983b2SBen Gras     { "november",	tMONTH, 11 },
4010c3983b2SBen Gras     { "december",	tMONTH, 12 },
4020c3983b2SBen Gras     { "sunday",		tDAY, 0 },
4030c3983b2SBen Gras     { "monday",		tDAY, 1 },
4040c3983b2SBen Gras     { "tuesday",	tDAY, 2 },
4050c3983b2SBen Gras     { "tues",		tDAY, 2 },
4060c3983b2SBen Gras     { "wednesday",	tDAY, 3 },
4070c3983b2SBen Gras     { "wednes",		tDAY, 3 },
4080c3983b2SBen Gras     { "thursday",	tDAY, 4 },
4090c3983b2SBen Gras     { "thur",		tDAY, 4 },
4100c3983b2SBen Gras     { "thurs",		tDAY, 4 },
4110c3983b2SBen Gras     { "friday",		tDAY, 5 },
4120c3983b2SBen Gras     { "saturday",	tDAY, 6 },
4130c3983b2SBen Gras     { NULL,		0,    0 }
4140c3983b2SBen Gras };
4150c3983b2SBen Gras 
4160c3983b2SBen Gras /* Time units table. */
41784d9c625SLionel Sambuc static const TABLE UnitsTable[] = {
4180c3983b2SBen Gras     { "year",		tMONTH_UNIT,	12 },
4190c3983b2SBen Gras     { "month",		tMONTH_UNIT,	1 },
4200c3983b2SBen Gras     { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
4210c3983b2SBen Gras     { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
4220c3983b2SBen Gras     { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
4230c3983b2SBen Gras     { "hour",		tMINUTE_UNIT,	60 },
4240c3983b2SBen Gras     { "minute",		tMINUTE_UNIT,	1 },
4250c3983b2SBen Gras     { "min",		tMINUTE_UNIT,	1 },
4260c3983b2SBen Gras     { "second",		tSEC_UNIT,	1 },
4270c3983b2SBen Gras     { "sec",		tSEC_UNIT,	1 },
4280c3983b2SBen Gras     { NULL,		0,		0 }
4290c3983b2SBen Gras };
4300c3983b2SBen Gras 
4310c3983b2SBen Gras /* Assorted relative-time words. */
43284d9c625SLionel Sambuc static const TABLE OtherTable[] = {
4330c3983b2SBen Gras     { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
4340c3983b2SBen Gras     { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
4350c3983b2SBen Gras     { "today",		tMINUTE_UNIT,	0 },
4360c3983b2SBen Gras     { "now",		tMINUTE_UNIT,	0 },
4370c3983b2SBen Gras     { "last",		tUNUMBER,	-1 },
4380c3983b2SBen Gras     { "this",		tMINUTE_UNIT,	0 },
4390c3983b2SBen Gras     { "next",		tUNUMBER,	2 },
4400c3983b2SBen Gras     { "first",		tUNUMBER,	1 },
4410c3983b2SBen Gras     { "one",		tUNUMBER,	1 },
4420c3983b2SBen Gras /*  { "second",		tUNUMBER,	2 }, */
4430c3983b2SBen Gras     { "two",		tUNUMBER,	2 },
4440c3983b2SBen Gras     { "third",		tUNUMBER,	3 },
4450c3983b2SBen Gras     { "three",		tUNUMBER,	3 },
4460c3983b2SBen Gras     { "fourth",		tUNUMBER,	4 },
4470c3983b2SBen Gras     { "four",		tUNUMBER,	4 },
4480c3983b2SBen Gras     { "fifth",		tUNUMBER,	5 },
4490c3983b2SBen Gras     { "five",		tUNUMBER,	5 },
4500c3983b2SBen Gras     { "sixth",		tUNUMBER,	6 },
4510c3983b2SBen Gras     { "six",		tUNUMBER,	6 },
4520c3983b2SBen Gras     { "seventh",	tUNUMBER,	7 },
4530c3983b2SBen Gras     { "seven",		tUNUMBER,	7 },
4540c3983b2SBen Gras     { "eighth",		tUNUMBER,	8 },
4550c3983b2SBen Gras     { "eight",		tUNUMBER,	8 },
4560c3983b2SBen Gras     { "ninth",		tUNUMBER,	9 },
4570c3983b2SBen Gras     { "nine",		tUNUMBER,	9 },
4580c3983b2SBen Gras     { "tenth",		tUNUMBER,	10 },
4590c3983b2SBen Gras     { "ten",		tUNUMBER,	10 },
4600c3983b2SBen Gras     { "eleventh",	tUNUMBER,	11 },
4610c3983b2SBen Gras     { "eleven",		tUNUMBER,	11 },
4620c3983b2SBen Gras     { "twelfth",	tUNUMBER,	12 },
4630c3983b2SBen Gras     { "twelve",		tUNUMBER,	12 },
4640c3983b2SBen Gras     { "ago",		tAGO,	1 },
4650c3983b2SBen Gras     { NULL,		0,	0 }
4660c3983b2SBen Gras };
4670c3983b2SBen Gras 
4680c3983b2SBen Gras /* The timezone table. */
4690c3983b2SBen Gras /* Some of these are commented out because a time_t can't store a float. */
47084d9c625SLionel Sambuc static const TABLE TimezoneTable[] = {
4710c3983b2SBen Gras     { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
4720c3983b2SBen Gras     { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
4730c3983b2SBen Gras     { "utc",	tZONE,     HOUR( 0) },
4740c3983b2SBen Gras     { "wet",	tZONE,     HOUR( 0) },	/* Western European */
4750c3983b2SBen Gras     { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
4760c3983b2SBen Gras     { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
4770c3983b2SBen Gras     { "at",	tZONE,     HOUR( 2) },	/* Azores */
4780c3983b2SBen Gras #if	0
4790c3983b2SBen Gras     /* For completeness.  BST is also British Summer, and GST is
4800c3983b2SBen Gras      * also Guam Standard. */
4810c3983b2SBen Gras     { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
4820c3983b2SBen Gras     { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
4830c3983b2SBen Gras #endif
4840c3983b2SBen Gras #if 0
4850c3983b2SBen Gras     { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
4860c3983b2SBen Gras     { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
4870c3983b2SBen Gras     { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
4880c3983b2SBen Gras #endif
4890c3983b2SBen Gras     { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
4900c3983b2SBen Gras     { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
4910c3983b2SBen Gras     { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
4920c3983b2SBen Gras     { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
4930c3983b2SBen Gras     { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
4940c3983b2SBen Gras     { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
4950c3983b2SBen Gras     { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
4960c3983b2SBen Gras     { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
4970c3983b2SBen Gras     { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
4980c3983b2SBen Gras     { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
4990c3983b2SBen Gras     { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
5000c3983b2SBen Gras     { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
5010c3983b2SBen Gras     { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
5020c3983b2SBen Gras     { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
5030c3983b2SBen Gras     { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
5040c3983b2SBen Gras     { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
5050c3983b2SBen Gras     { "nt",	tZONE,     HOUR(11) },	/* Nome */
5060c3983b2SBen Gras     { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
5070c3983b2SBen Gras     { "cet",	tZONE,     -HOUR(1) },	/* Central European */
5080c3983b2SBen Gras     { "met",	tZONE,     -HOUR(1) },	/* Middle European */
5090c3983b2SBen Gras     { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
5100c3983b2SBen Gras     { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
5110c3983b2SBen Gras     { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
5120c3983b2SBen Gras     { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
5130c3983b2SBen Gras     { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
5140c3983b2SBen Gras     { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
5150c3983b2SBen Gras     { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
5160c3983b2SBen Gras     { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
5170c3983b2SBen Gras #if 0
5180c3983b2SBen Gras     { "it",	tZONE,     -HOUR(3.5) },/* Iran */
5190c3983b2SBen Gras #endif
5200c3983b2SBen Gras     { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
5210c3983b2SBen Gras     { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
5220c3983b2SBen Gras #if 0
5230c3983b2SBen Gras     { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
5240c3983b2SBen Gras #endif
5250c3983b2SBen Gras     { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
5260c3983b2SBen Gras #if	0
5270c3983b2SBen Gras     /* For completeness.  NST is also Newfoundland Stanard, and SST is
5280c3983b2SBen Gras      * also Swedish Summer. */
5290c3983b2SBen Gras     { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
5300c3983b2SBen Gras     { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
5310c3983b2SBen Gras #endif	/* 0 */
5320c3983b2SBen Gras     { "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
5330c3983b2SBen Gras     { "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
5340c3983b2SBen Gras #if 0
5350c3983b2SBen Gras     { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
5360c3983b2SBen Gras #endif
5370c3983b2SBen Gras     { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
5380c3983b2SBen Gras     { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
5390c3983b2SBen Gras #if 0
5400c3983b2SBen Gras     { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
5410c3983b2SBen Gras     { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
5420c3983b2SBen Gras #endif
5430c3983b2SBen Gras     { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
5440c3983b2SBen Gras     { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
5450c3983b2SBen Gras     { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
5460c3983b2SBen Gras     { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
5470c3983b2SBen Gras     { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
5480c3983b2SBen Gras     { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
5490c3983b2SBen Gras     { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
5500c3983b2SBen Gras     {  NULL,	0,	    0 }
5510c3983b2SBen Gras };
5520c3983b2SBen Gras 
5530c3983b2SBen Gras /* Military timezone table. */
55484d9c625SLionel Sambuc static const TABLE MilitaryTable[] = {
5550c3983b2SBen Gras     { "a",	tZONE,	HOUR(  1) },
5560c3983b2SBen Gras     { "b",	tZONE,	HOUR(  2) },
5570c3983b2SBen Gras     { "c",	tZONE,	HOUR(  3) },
5580c3983b2SBen Gras     { "d",	tZONE,	HOUR(  4) },
5590c3983b2SBen Gras     { "e",	tZONE,	HOUR(  5) },
5600c3983b2SBen Gras     { "f",	tZONE,	HOUR(  6) },
5610c3983b2SBen Gras     { "g",	tZONE,	HOUR(  7) },
5620c3983b2SBen Gras     { "h",	tZONE,	HOUR(  8) },
5630c3983b2SBen Gras     { "i",	tZONE,	HOUR(  9) },
5640c3983b2SBen Gras     { "k",	tZONE,	HOUR( 10) },
5650c3983b2SBen Gras     { "l",	tZONE,	HOUR( 11) },
5660c3983b2SBen Gras     { "m",	tZONE,	HOUR( 12) },
5670c3983b2SBen Gras     { "n",	tZONE,	HOUR(- 1) },
5680c3983b2SBen Gras     { "o",	tZONE,	HOUR(- 2) },
5690c3983b2SBen Gras     { "p",	tZONE,	HOUR(- 3) },
5700c3983b2SBen Gras     { "q",	tZONE,	HOUR(- 4) },
5710c3983b2SBen Gras     { "r",	tZONE,	HOUR(- 5) },
5720c3983b2SBen Gras     { "s",	tZONE,	HOUR(- 6) },
5730c3983b2SBen Gras     { "t",	tZONE,	HOUR(- 7) },
5740c3983b2SBen Gras     { "u",	tZONE,	HOUR(- 8) },
5750c3983b2SBen Gras     { "v",	tZONE,	HOUR(- 9) },
5760c3983b2SBen Gras     { "w",	tZONE,	HOUR(-10) },
5770c3983b2SBen Gras     { "x",	tZONE,	HOUR(-11) },
5780c3983b2SBen Gras     { "y",	tZONE,	HOUR(-12) },
5790c3983b2SBen Gras     { "z",	tZONE,	HOUR(  0) },
5800c3983b2SBen Gras     { NULL,	0,	0 }
5810c3983b2SBen Gras };
5820c3983b2SBen Gras 
5830c3983b2SBen Gras 
5840c3983b2SBen Gras 
5850c3983b2SBen Gras 
5860c3983b2SBen Gras /* ARGSUSED */
5870c3983b2SBen Gras static int
5880c3983b2SBen Gras yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
5890c3983b2SBen Gras {
5900c3983b2SBen Gras   return 0;
5910c3983b2SBen Gras }
5920c3983b2SBen Gras 
5930c3983b2SBen Gras 
594*0a6a1f1dSLionel Sambuc /* Adjust year from a value that might be abbreviated, to a full value.
595*0a6a1f1dSLionel Sambuc  * e.g. convert 70 to 1970.
596*0a6a1f1dSLionel Sambuc  * Input Year is either:
597*0a6a1f1dSLionel Sambuc  *  - A negative number, which means to use its absolute value (why?)
598*0a6a1f1dSLionel Sambuc  *  - A number from 0 to 99, which means a year from 1900 to 1999, or
599*0a6a1f1dSLionel Sambuc  *  - The actual year (>=100).
600*0a6a1f1dSLionel Sambuc  * Returns the full year. */
6010c3983b2SBen Gras static time_t
602*0a6a1f1dSLionel Sambuc AdjustYear(time_t Year)
6030c3983b2SBen Gras {
6040c3983b2SBen Gras     /* XXX Y2K */
6050c3983b2SBen Gras     if (Year < 0)
6060c3983b2SBen Gras 	Year = -Year;
6070c3983b2SBen Gras     if (Year < 70)
6080c3983b2SBen Gras 	Year += 2000;
6090c3983b2SBen Gras     else if (Year < 100)
6100c3983b2SBen Gras 	Year += 1900;
611*0a6a1f1dSLionel Sambuc     return Year;
612*0a6a1f1dSLionel Sambuc }
613*0a6a1f1dSLionel Sambuc 
614*0a6a1f1dSLionel Sambuc static time_t
615*0a6a1f1dSLionel Sambuc Convert(
616*0a6a1f1dSLionel Sambuc     time_t	Month,		/* month of year [1-12] */
617*0a6a1f1dSLionel Sambuc     time_t	Day,		/* day of month [1-31] */
618*0a6a1f1dSLionel Sambuc     time_t	Year,		/* year, not abbreviated in any way */
619*0a6a1f1dSLionel Sambuc     time_t	Hours,		/* Hour of day [0-24] */
620*0a6a1f1dSLionel Sambuc     time_t	Minutes,	/* Minute of hour [0-59] */
621*0a6a1f1dSLionel Sambuc     time_t	Seconds,	/* Second of minute [0-60] */
622*0a6a1f1dSLionel Sambuc     time_t	Timezone,	/* Timezone as minutes east of UTC,
623*0a6a1f1dSLionel Sambuc 				 * or USE_LOCAL_TIME special case */
624*0a6a1f1dSLionel Sambuc     MERIDIAN	Meridian,	/* Hours are am/pm/24 hour clock */
625*0a6a1f1dSLionel Sambuc     DSTMODE	DSTmode		/* DST on/off/maybe */
626*0a6a1f1dSLionel Sambuc )
627*0a6a1f1dSLionel Sambuc {
628*0a6a1f1dSLionel Sambuc     struct tm tm = {.tm_sec = 0};
629*0a6a1f1dSLionel Sambuc     time_t result;
6300c3983b2SBen Gras 
631dba3562dSLionel Sambuc     tm.tm_sec = Seconds;
632dba3562dSLionel Sambuc     tm.tm_min = Minutes;
633dba3562dSLionel Sambuc     tm.tm_hour = Hours + (Meridian == MERpm ? 12 : 0);
634dba3562dSLionel Sambuc     tm.tm_mday = Day;
635dba3562dSLionel Sambuc     tm.tm_mon = Month - 1;
636dba3562dSLionel Sambuc     tm.tm_year = Year - 1900;
637dba3562dSLionel Sambuc     switch (DSTmode) {
638dba3562dSLionel Sambuc     case DSTon:  tm.tm_isdst = 1; break;
639dba3562dSLionel Sambuc     case DSToff: tm.tm_isdst = 0; break;
640dba3562dSLionel Sambuc     default:     tm.tm_isdst = -1; break;
6410c3983b2SBen Gras     }
6420c3983b2SBen Gras 
643*0a6a1f1dSLionel Sambuc     if (Timezone == USE_LOCAL_TIME) {
644*0a6a1f1dSLionel Sambuc 	    result = mktime(&tm);
645*0a6a1f1dSLionel Sambuc     } else {
646*0a6a1f1dSLionel Sambuc 	    /* We rely on mktime_z(NULL, ...) working in UTC */
64784d9c625SLionel Sambuc 	    result = mktime_z(NULL, &tm);
64884d9c625SLionel Sambuc 	    result += Timezone * 60;
649*0a6a1f1dSLionel Sambuc     }
650*0a6a1f1dSLionel Sambuc 
651*0a6a1f1dSLionel Sambuc #if PARSEDATE_DEBUG
652*0a6a1f1dSLionel Sambuc     fprintf(stderr, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd"
653*0a6a1f1dSLionel Sambuc 		    " mer=%d DST=%d)",
654*0a6a1f1dSLionel Sambuc 	__func__,
655*0a6a1f1dSLionel Sambuc 	(intmax_t)Month, (intmax_t)Day, (intmax_t)Year,
656*0a6a1f1dSLionel Sambuc 	(intmax_t)Hours, (intmax_t)Minutes, (intmax_t)Seconds,
657*0a6a1f1dSLionel Sambuc 	(intmax_t)Timezone, (int)Meridian, (int)DSTmode);
658*0a6a1f1dSLionel Sambuc     fprintf(stderr, " -> %jd", (intmax_t)result);
659*0a6a1f1dSLionel Sambuc     fprintf(stderr, " %s", ctime(&result));
660*0a6a1f1dSLionel Sambuc #endif
661*0a6a1f1dSLionel Sambuc 
66284d9c625SLionel Sambuc     return result;
6630c3983b2SBen Gras }
6640c3983b2SBen Gras 
6650c3983b2SBen Gras 
6660c3983b2SBen Gras static time_t
6670c3983b2SBen Gras DSTcorrect(
6680c3983b2SBen Gras     time_t	Start,
6690c3983b2SBen Gras     time_t	Future
6700c3983b2SBen Gras )
6710c3983b2SBen Gras {
6720c3983b2SBen Gras     time_t	StartDay;
6730c3983b2SBen Gras     time_t	FutureDay;
6740c3983b2SBen Gras     struct tm  *tm;
6750c3983b2SBen Gras 
6760c3983b2SBen Gras     if ((tm = localtime(&Start)) == NULL)
6770c3983b2SBen Gras 	return -1;
6780c3983b2SBen Gras     StartDay = (tm->tm_hour + 1) % 24;
6790c3983b2SBen Gras 
6800c3983b2SBen Gras     if ((tm = localtime(&Future)) == NULL)
6810c3983b2SBen Gras 	return -1;
6820c3983b2SBen Gras     FutureDay = (tm->tm_hour + 1) % 24;
6830c3983b2SBen Gras 
6840c3983b2SBen Gras     return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
6850c3983b2SBen Gras }
6860c3983b2SBen Gras 
6870c3983b2SBen Gras 
6880c3983b2SBen Gras static time_t
6890c3983b2SBen Gras RelativeDate(
6900c3983b2SBen Gras     time_t	Start,
6910c3983b2SBen Gras     time_t	DayOrdinal,
6920c3983b2SBen Gras     time_t	DayNumber
6930c3983b2SBen Gras )
6940c3983b2SBen Gras {
6950c3983b2SBen Gras     struct tm	*tm;
6960c3983b2SBen Gras     time_t	now;
6970c3983b2SBen Gras 
6980c3983b2SBen Gras     now = Start;
6990c3983b2SBen Gras     tm = localtime(&now);
7000c3983b2SBen Gras     now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
7010c3983b2SBen Gras     now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
7020c3983b2SBen Gras     return DSTcorrect(Start, now);
7030c3983b2SBen Gras }
7040c3983b2SBen Gras 
7050c3983b2SBen Gras 
7060c3983b2SBen Gras static time_t
7070c3983b2SBen Gras RelativeMonth(
7080c3983b2SBen Gras     time_t	Start,
7090c3983b2SBen Gras     time_t	RelMonth,
7100c3983b2SBen Gras     time_t	Timezone
7110c3983b2SBen Gras )
7120c3983b2SBen Gras {
7130c3983b2SBen Gras     struct tm	*tm;
7140c3983b2SBen Gras     time_t	Month;
7150c3983b2SBen Gras     time_t	Year;
7160c3983b2SBen Gras 
7170c3983b2SBen Gras     if (RelMonth == 0)
7180c3983b2SBen Gras 	return 0;
7190c3983b2SBen Gras     tm = localtime(&Start);
7200c3983b2SBen Gras     if (tm == NULL)
7210c3983b2SBen Gras 	return -1;
7220c3983b2SBen Gras     Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
7230c3983b2SBen Gras     Year = Month / 12;
7240c3983b2SBen Gras     Month = Month % 12 + 1;
7250c3983b2SBen Gras     return DSTcorrect(Start,
7260c3983b2SBen Gras 	    Convert(Month, (time_t)tm->tm_mday, Year,
7270c3983b2SBen Gras 		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
7280c3983b2SBen Gras 		Timezone, MER24, DSTmaybe));
7290c3983b2SBen Gras }
7300c3983b2SBen Gras 
7310c3983b2SBen Gras 
7320c3983b2SBen Gras static int
7330c3983b2SBen Gras LookupWord(YYSTYPE *yylval, char *buff)
7340c3983b2SBen Gras {
7350c3983b2SBen Gras     register char	*p;
7360c3983b2SBen Gras     register char	*q;
7370c3983b2SBen Gras     register const TABLE	*tp;
7380c3983b2SBen Gras     int			i;
7390c3983b2SBen Gras     int			abbrev;
7400c3983b2SBen Gras 
7410c3983b2SBen Gras     /* Make it lowercase. */
7420c3983b2SBen Gras     for (p = buff; *p; p++)
7430c3983b2SBen Gras 	if (isupper((unsigned char)*p))
7440c3983b2SBen Gras 	    *p = tolower((unsigned char)*p);
7450c3983b2SBen Gras 
7460c3983b2SBen Gras     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
7470c3983b2SBen Gras 	yylval->Meridian = MERam;
7480c3983b2SBen Gras 	return tMERIDIAN;
7490c3983b2SBen Gras     }
7500c3983b2SBen Gras     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
7510c3983b2SBen Gras 	yylval->Meridian = MERpm;
7520c3983b2SBen Gras 	return tMERIDIAN;
7530c3983b2SBen Gras     }
7540c3983b2SBen Gras 
7550c3983b2SBen Gras     /* See if we have an abbreviation for a month. */
7560c3983b2SBen Gras     if (strlen(buff) == 3)
7570c3983b2SBen Gras 	abbrev = 1;
7580c3983b2SBen Gras     else if (strlen(buff) == 4 && buff[3] == '.') {
7590c3983b2SBen Gras 	abbrev = 1;
7600c3983b2SBen Gras 	buff[3] = '\0';
7610c3983b2SBen Gras     }
7620c3983b2SBen Gras     else
7630c3983b2SBen Gras 	abbrev = 0;
7640c3983b2SBen Gras 
7650c3983b2SBen Gras     for (tp = MonthDayTable; tp->name; tp++) {
7660c3983b2SBen Gras 	if (abbrev) {
7670c3983b2SBen Gras 	    if (strncmp(buff, tp->name, 3) == 0) {
7680c3983b2SBen Gras 		yylval->Number = tp->value;
7690c3983b2SBen Gras 		return tp->type;
7700c3983b2SBen Gras 	    }
7710c3983b2SBen Gras 	}
7720c3983b2SBen Gras 	else if (strcmp(buff, tp->name) == 0) {
7730c3983b2SBen Gras 	    yylval->Number = tp->value;
7740c3983b2SBen Gras 	    return tp->type;
7750c3983b2SBen Gras 	}
7760c3983b2SBen Gras     }
7770c3983b2SBen Gras 
7780c3983b2SBen Gras     for (tp = TimezoneTable; tp->name; tp++)
7790c3983b2SBen Gras 	if (strcmp(buff, tp->name) == 0) {
7800c3983b2SBen Gras 	    yylval->Number = tp->value;
7810c3983b2SBen Gras 	    return tp->type;
7820c3983b2SBen Gras 	}
7830c3983b2SBen Gras 
7840c3983b2SBen Gras     if (strcmp(buff, "dst") == 0)
7850c3983b2SBen Gras 	return tDST;
7860c3983b2SBen Gras 
7870c3983b2SBen Gras     for (tp = UnitsTable; tp->name; tp++)
7880c3983b2SBen Gras 	if (strcmp(buff, tp->name) == 0) {
7890c3983b2SBen Gras 	    yylval->Number = tp->value;
7900c3983b2SBen Gras 	    return tp->type;
7910c3983b2SBen Gras 	}
7920c3983b2SBen Gras 
7930c3983b2SBen Gras     /* Strip off any plural and try the units table again. */
7940c3983b2SBen Gras     i = strlen(buff) - 1;
7950c3983b2SBen Gras     if (buff[i] == 's') {
7960c3983b2SBen Gras 	buff[i] = '\0';
7970c3983b2SBen Gras 	for (tp = UnitsTable; tp->name; tp++)
7980c3983b2SBen Gras 	    if (strcmp(buff, tp->name) == 0) {
7990c3983b2SBen Gras 		yylval->Number = tp->value;
8000c3983b2SBen Gras 		return tp->type;
8010c3983b2SBen Gras 	    }
8020c3983b2SBen Gras 	buff[i] = 's';		/* Put back for "this" in OtherTable. */
8030c3983b2SBen Gras     }
8040c3983b2SBen Gras 
8050c3983b2SBen Gras     for (tp = OtherTable; tp->name; tp++)
8060c3983b2SBen Gras 	if (strcmp(buff, tp->name) == 0) {
8070c3983b2SBen Gras 	    yylval->Number = tp->value;
8080c3983b2SBen Gras 	    return tp->type;
8090c3983b2SBen Gras 	}
8100c3983b2SBen Gras 
8110c3983b2SBen Gras     /* Military timezones. */
8120c3983b2SBen Gras     if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
8130c3983b2SBen Gras 	for (tp = MilitaryTable; tp->name; tp++)
8140c3983b2SBen Gras 	    if (strcmp(buff, tp->name) == 0) {
8150c3983b2SBen Gras 		yylval->Number = tp->value;
8160c3983b2SBen Gras 		return tp->type;
8170c3983b2SBen Gras 	    }
8180c3983b2SBen Gras     }
8190c3983b2SBen Gras 
8200c3983b2SBen Gras     /* Drop out any periods and try the timezone table again. */
8210c3983b2SBen Gras     for (i = 0, p = q = buff; *q; q++)
8220c3983b2SBen Gras 	if (*q != '.')
8230c3983b2SBen Gras 	    *p++ = *q;
8240c3983b2SBen Gras 	else
8250c3983b2SBen Gras 	    i++;
8260c3983b2SBen Gras     *p = '\0';
8270c3983b2SBen Gras     if (i)
8280c3983b2SBen Gras 	for (tp = TimezoneTable; tp->name; tp++)
8290c3983b2SBen Gras 	    if (strcmp(buff, tp->name) == 0) {
8300c3983b2SBen Gras 		yylval->Number = tp->value;
8310c3983b2SBen Gras 		return tp->type;
8320c3983b2SBen Gras 	    }
8330c3983b2SBen Gras 
8340c3983b2SBen Gras     return tID;
8350c3983b2SBen Gras }
8360c3983b2SBen Gras 
8370c3983b2SBen Gras 
8380c3983b2SBen Gras static int
8390c3983b2SBen Gras yylex(YYSTYPE *yylval, const char **yyInput)
8400c3983b2SBen Gras {
8410c3983b2SBen Gras     register char	c;
8420c3983b2SBen Gras     register char	*p;
8430c3983b2SBen Gras     char		buff[20];
8440c3983b2SBen Gras     int			Count;
8450c3983b2SBen Gras     int			sign;
8460c3983b2SBen Gras     const char		*inp = *yyInput;
8470c3983b2SBen Gras 
8480c3983b2SBen Gras     for ( ; ; ) {
8490c3983b2SBen Gras 	while (isspace((unsigned char)*inp))
8500c3983b2SBen Gras 	    inp++;
8510c3983b2SBen Gras 
8520c3983b2SBen Gras 	if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') {
8530c3983b2SBen Gras 	    if (c == '-' || c == '+') {
8540c3983b2SBen Gras 		sign = c == '-' ? -1 : 1;
8550c3983b2SBen Gras 		if (!isdigit((unsigned char)*++inp))
8560c3983b2SBen Gras 		    /* skip the '-' sign */
8570c3983b2SBen Gras 		    continue;
8580c3983b2SBen Gras 	    }
8590c3983b2SBen Gras 	    else
8600c3983b2SBen Gras 		sign = 0;
8610c3983b2SBen Gras 	    for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); )
8620c3983b2SBen Gras 		yylval->Number = 10 * yylval->Number + c - '0';
8630c3983b2SBen Gras 	    if (sign < 0)
8640c3983b2SBen Gras 		yylval->Number = -yylval->Number;
8650c3983b2SBen Gras 	    *yyInput = --inp;
8660c3983b2SBen Gras 	    return sign ? tSNUMBER : tUNUMBER;
8670c3983b2SBen Gras 	}
8680c3983b2SBen Gras 	if (isalpha((unsigned char)c)) {
8690c3983b2SBen Gras 	    for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; )
8700c3983b2SBen Gras 		if (p < &buff[sizeof buff - 1])
8710c3983b2SBen Gras 		    *p++ = c;
8720c3983b2SBen Gras 	    *p = '\0';
8730c3983b2SBen Gras 	    *yyInput = --inp;
8740c3983b2SBen Gras 	    return LookupWord(yylval, buff);
8750c3983b2SBen Gras 	}
8760c3983b2SBen Gras 	if (c == '@') {
8770c3983b2SBen Gras 	    *yyInput = ++inp;
8780c3983b2SBen Gras 	    return AT_SIGN;
8790c3983b2SBen Gras 	}
8800c3983b2SBen Gras 	if (c != '(') {
8810c3983b2SBen Gras 	    *yyInput = ++inp;
8820c3983b2SBen Gras 	    return c;
8830c3983b2SBen Gras 	}
8840c3983b2SBen Gras 	Count = 0;
8850c3983b2SBen Gras 	do {
8860c3983b2SBen Gras 	    c = *inp++;
8870c3983b2SBen Gras 	    if (c == '\0')
8880c3983b2SBen Gras 		return c;
8890c3983b2SBen Gras 	    if (c == '(')
8900c3983b2SBen Gras 		Count++;
8910c3983b2SBen Gras 	    else if (c == ')')
8920c3983b2SBen Gras 		Count--;
8930c3983b2SBen Gras 	} while (Count > 0);
8940c3983b2SBen Gras     }
8950c3983b2SBen Gras }
8960c3983b2SBen Gras 
8970c3983b2SBen Gras #define TM_YEAR_ORIGIN 1900
8980c3983b2SBen Gras 
8990c3983b2SBen Gras time_t
9000c3983b2SBen Gras parsedate(const char *p, const time_t *now, const int *zone)
9010c3983b2SBen Gras {
902*0a6a1f1dSLionel Sambuc     struct tm		local, *tm;
9030c3983b2SBen Gras     time_t		nowt;
9040c3983b2SBen Gras     int			zonet;
9050c3983b2SBen Gras     time_t		Start;
9060c3983b2SBen Gras     time_t		tod, rm;
9070c3983b2SBen Gras     struct dateinfo	param;
90884d9c625SLionel Sambuc     int			saved_errno;
90984d9c625SLionel Sambuc 
91084d9c625SLionel Sambuc     saved_errno = errno;
91184d9c625SLionel Sambuc     errno = 0;
9120c3983b2SBen Gras 
913*0a6a1f1dSLionel Sambuc     if (now == NULL) {
9140c3983b2SBen Gras         now = &nowt;
9150c3983b2SBen Gras 	(void)time(&nowt);
916*0a6a1f1dSLionel Sambuc     }
917*0a6a1f1dSLionel Sambuc     if (zone == NULL) {
918*0a6a1f1dSLionel Sambuc 	zone = &zonet;
919*0a6a1f1dSLionel Sambuc 	zonet = USE_LOCAL_TIME;
9200c3983b2SBen Gras 	if ((tm = localtime_r(now, &local)) == NULL)
9210c3983b2SBen Gras 	    return -1;
9220c3983b2SBen Gras     } else {
923*0a6a1f1dSLionel Sambuc 	/*
924*0a6a1f1dSLionel Sambuc 	 * Should use the specified zone, not localtime.
925*0a6a1f1dSLionel Sambuc 	 * Fake it using gmtime and arithmetic.
926*0a6a1f1dSLionel Sambuc 	 * This is good enough because we use only the year/month/day,
927*0a6a1f1dSLionel Sambuc 	 * not other fields of struct tm.
928*0a6a1f1dSLionel Sambuc 	 */
929*0a6a1f1dSLionel Sambuc 	time_t fake = *now + (*zone * 60);
930*0a6a1f1dSLionel Sambuc 	if ((tm = gmtime_r(&fake, &local)) == NULL)
9310c3983b2SBen Gras 	    return -1;
9320c3983b2SBen Gras     }
9330c3983b2SBen Gras     param.yyYear = tm->tm_year + 1900;
9340c3983b2SBen Gras     param.yyMonth = tm->tm_mon + 1;
9350c3983b2SBen Gras     param.yyDay = tm->tm_mday;
9360c3983b2SBen Gras     param.yyTimezone = *zone;
9370c3983b2SBen Gras     param.yyDSTmode = DSTmaybe;
9380c3983b2SBen Gras     param.yyHour = 0;
9390c3983b2SBen Gras     param.yyMinutes = 0;
9400c3983b2SBen Gras     param.yySeconds = 0;
9410c3983b2SBen Gras     param.yyMeridian = MER24;
9420c3983b2SBen Gras     param.yyRelSeconds = 0;
9430c3983b2SBen Gras     param.yyRelMonth = 0;
9440c3983b2SBen Gras     param.yyHaveDate = 0;
945*0a6a1f1dSLionel Sambuc     param.yyHaveFullYear = 0;
9460c3983b2SBen Gras     param.yyHaveDay = 0;
9470c3983b2SBen Gras     param.yyHaveRel = 0;
9480c3983b2SBen Gras     param.yyHaveTime = 0;
9490c3983b2SBen Gras     param.yyHaveZone = 0;
9500c3983b2SBen Gras 
9510c3983b2SBen Gras     if (yyparse(&param, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 ||
95284d9c625SLionel Sambuc 	param.yyHaveDate > 1 || param.yyHaveDay > 1) {
95384d9c625SLionel Sambuc 	errno = EINVAL;
9540c3983b2SBen Gras 	return -1;
95584d9c625SLionel Sambuc     }
9560c3983b2SBen Gras 
9570c3983b2SBen Gras     if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) {
958*0a6a1f1dSLionel Sambuc 	if (! param.yyHaveFullYear) {
959*0a6a1f1dSLionel Sambuc 		param.yyYear = AdjustYear(param.yyYear);
960*0a6a1f1dSLionel Sambuc 		param.yyHaveFullYear = 1;
961*0a6a1f1dSLionel Sambuc 	}
9620c3983b2SBen Gras 	Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
9630c3983b2SBen Gras 	    param.yyMinutes, param.yySeconds, param.yyTimezone,
9640c3983b2SBen Gras 	    param.yyMeridian, param.yyDSTmode);
96584d9c625SLionel Sambuc 	if (Start == -1 && errno != 0)
9660c3983b2SBen Gras 	    return -1;
9670c3983b2SBen Gras     }
9680c3983b2SBen Gras     else {
9690c3983b2SBen Gras 	Start = *now;
9700c3983b2SBen Gras 	if (!param.yyHaveRel)
9710c3983b2SBen Gras 	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
9720c3983b2SBen Gras     }
9730c3983b2SBen Gras 
9740c3983b2SBen Gras     Start += param.yyRelSeconds;
9750c3983b2SBen Gras     rm = RelativeMonth(Start, param.yyRelMonth, param.yyTimezone);
97684d9c625SLionel Sambuc     if (rm == -1 && errno != 0)
9770c3983b2SBen Gras 	return -1;
9780c3983b2SBen Gras     Start += rm;
9790c3983b2SBen Gras 
9800c3983b2SBen Gras     if (param.yyHaveDay && !param.yyHaveDate) {
9810c3983b2SBen Gras 	tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber);
9820c3983b2SBen Gras 	Start += tod;
9830c3983b2SBen Gras     }
9840c3983b2SBen Gras 
98584d9c625SLionel Sambuc     if (errno == 0)
98684d9c625SLionel Sambuc 	errno = saved_errno;
9870c3983b2SBen Gras     return Start;
9880c3983b2SBen Gras }
9890c3983b2SBen Gras 
9900c3983b2SBen Gras 
9910c3983b2SBen Gras #if	defined(TEST)
9920c3983b2SBen Gras 
9930c3983b2SBen Gras /* ARGSUSED */
9940c3983b2SBen Gras int
99584d9c625SLionel Sambuc main(int ac, char *av[])
9960c3983b2SBen Gras {
9970c3983b2SBen Gras     char	buff[128];
9980c3983b2SBen Gras     time_t	d;
9990c3983b2SBen Gras 
10000c3983b2SBen Gras     (void)printf("Enter date, or blank line to exit.\n\t> ");
10010c3983b2SBen Gras     (void)fflush(stdout);
100284d9c625SLionel Sambuc     while (fgets(buff, sizeof(buff), stdin) && buff[0] != '\n') {
100384d9c625SLionel Sambuc 	errno = 0;
10040c3983b2SBen Gras 	d = parsedate(buff, NULL, NULL);
100584d9c625SLionel Sambuc 	if (d == -1 && errno != 0)
100684d9c625SLionel Sambuc 	    (void)printf("Bad format - couldn't convert: %s\n",
100784d9c625SLionel Sambuc 	        strerror(errno));
10080c3983b2SBen Gras 	else
100984d9c625SLionel Sambuc 	    (void)printf("%jd\t%s", (intmax_t)d, ctime(&d));
10100c3983b2SBen Gras 	(void)printf("\t> ");
10110c3983b2SBen Gras 	(void)fflush(stdout);
10120c3983b2SBen Gras     }
10130c3983b2SBen Gras     exit(0);
10140c3983b2SBen Gras     /* NOTREACHED */
10150c3983b2SBen Gras }
10160c3983b2SBen Gras #endif	/* defined(TEST) */
1017