xref: /openbsd/gnu/usr.bin/cvs/lib/getdate.y (revision 404b540a)
1 %{
2 /*
3 **  Originally written by Steven M. Bellovin <smb@research.att.com> while
4 **  at the University of North Carolina at Chapel Hill.  Later tweaked by
5 **  a couple of people on Usenet.  Completely overhauled by Rich $alz
6 **  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
7 **
8 **  This grammar has 10 shift/reduce conflicts.
9 **
10 **  This code is in the public domain and has no copyright.
11 */
12 /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13 /* SUPPRESS 288 on yyerrlab *//* Label unused */
14 
15 #ifdef HAVE_CONFIG_H
16 #if defined (emacs) || defined (CONFIG_BROKETS)
17 #include <config.h>
18 #else
19 #include "config.h"
20 #endif
21 #endif
22 
23 /* Since the code of getdate.y is not included in the Emacs executable
24    itself, there is no need to #define static in this file.  Even if
25    the code were included in the Emacs executable, it probably
26    wouldn't do any harm to #undef it here; this will only cause
27    problems if we try to write to a static variable, which I don't
28    think this code needs to do.  */
29 #ifdef emacs
30 #undef static
31 #endif
32 
33 #include <stdio.h>
34 #include <ctype.h>
35 
36 /* The code at the top of get_date which figures out the offset of the
37    current time zone checks various CPP symbols to see if special
38    tricks are need, but defaults to using the gettimeofday system call.
39    Include <sys/time.h> if that will be used.  */
40 
41 #if	defined(vms)
42 # include <types.h>
43 #else /* defined(vms) */
44 # include <sys/types.h>
45 # include "xtime.h"
46 #endif	/* !defined(vms) */
47 
48 #if defined (STDC_HEADERS) || defined (USG)
49 #include <string.h>
50 #endif
51 
52 /* Some old versions of bison generate parsers that use bcopy.
53    That loses on systems that don't provide the function, so we have
54    to redefine it here.  */
55 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
56 #define bcopy(from, to, len) memcpy ((to), (from), (len))
57 #endif
58 
59 #if defined (STDC_HEADERS)
60 #include <stdlib.h>
61 #endif
62 
63 /* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
64    releases):
65 
66    We don't want to mess with all the portability hassles of alloca.
67    In particular, most (all?) versions of bison will use alloca in
68    their parser.  If bison works on your system (e.g. it should work
69    with gcc), then go ahead and use it, but the more general solution
70    is to use byacc instead of bison, which should generate a portable
71    parser.  I played with adding "#define alloca dont_use_alloca", to
72    give an error if the parser generator uses alloca (and thus detect
73    unportable getdate.c's), but that seems to cause as many problems
74    as it solves.  */
75 
76 extern struct tm	*gmtime();
77 extern struct tm	*localtime();
78 
79 #define yyparse getdate_yyparse
80 #define yylex getdate_yylex
81 #define yyerror getdate_yyerror
82 
83 static int yyparse ();
84 static int yylex ();
85 static int yyerror ();
86 
87 #define EPOCH		1970
88 #define HOUR(x)		((time_t)(x) * 60)
89 #define SECSPERDAY	(24L * 60L * 60L)
90 
91 
92 /*
93 **  An entry in the lexical lookup table.
94 */
95 typedef struct _TABLE {
96     char	*name;
97     int		type;
98     time_t	value;
99 } TABLE;
100 
101 
102 /*
103 **  Daylight-savings mode:  on, off, or not yet known.
104 */
105 typedef enum _DSTMODE {
106     DSTon, DSToff, DSTmaybe
107 } DSTMODE;
108 
109 /*
110 **  Meridian:  am, pm, or 24-hour style.
111 */
112 typedef enum _MERIDIAN {
113     MERam, MERpm, MER24
114 } MERIDIAN;
115 
116 
117 /*
118 **  Global variables.  We could get rid of most of these by using a good
119 **  union as the yacc stack.  (This routine was originally written before
120 **  yacc had the %union construct.)  Maybe someday; right now we only use
121 **  the %union very rarely.
122 */
123 static char	*yyInput;
124 static DSTMODE	yyDSTmode;
125 static time_t	yyDayOrdinal;
126 static time_t	yyDayNumber;
127 static int	yyHaveDate;
128 static int	yyHaveDay;
129 static int	yyHaveRel;
130 static int	yyHaveTime;
131 static int	yyHaveZone;
132 static time_t	yyTimezone;
133 static time_t	yyDay;
134 static time_t	yyHour;
135 static time_t	yyMinutes;
136 static time_t	yyMonth;
137 static time_t	yySeconds;
138 static time_t	yyYear;
139 static MERIDIAN	yyMeridian;
140 static time_t	yyRelMonth;
141 static time_t	yyRelSeconds;
142 
143 %}
144 
145 %union {
146     time_t		Number;
147     enum _MERIDIAN	Meridian;
148 }
149 
150 %token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
151 %token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
152 
153 %type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
154 %type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
155 %type	<Meridian>	tMERIDIAN o_merid
156 
157 %%
158 
159 spec	: /* NULL */
160 	| spec item
161 	;
162 
163 item	: time {
164 	    yyHaveTime++;
165 	}
166 	| zone {
167 	    yyHaveZone++;
168 	}
169 	| date {
170 	    yyHaveDate++;
171 	}
172 	| day {
173 	    yyHaveDay++;
174 	}
175 	| rel {
176 	    yyHaveRel++;
177 	}
178 	| number
179 	;
180 
181 time	: tUNUMBER tMERIDIAN {
182 	    yyHour = $1;
183 	    yyMinutes = 0;
184 	    yySeconds = 0;
185 	    yyMeridian = $2;
186 	}
187 	| tUNUMBER ':' tUNUMBER o_merid {
188 	    yyHour = $1;
189 	    yyMinutes = $3;
190 	    yySeconds = 0;
191 	    yyMeridian = $4;
192 	}
193 	| tUNUMBER ':' tUNUMBER tSNUMBER {
194 	    yyHour = $1;
195 	    yyMinutes = $3;
196 	    yyMeridian = MER24;
197 	    yyDSTmode = DSToff;
198 	    yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
199 	}
200 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
201 	    yyHour = $1;
202 	    yyMinutes = $3;
203 	    yySeconds = $5;
204 	    yyMeridian = $6;
205 	}
206 	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
207 	    yyHour = $1;
208 	    yyMinutes = $3;
209 	    yySeconds = $5;
210 	    yyMeridian = MER24;
211 	    yyDSTmode = DSToff;
212 	    yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
213 	}
214 	;
215 
216 zone	: tZONE {
217 	    yyTimezone = $1;
218 	    yyDSTmode = DSToff;
219 	}
220 	| tDAYZONE {
221 	    yyTimezone = $1;
222 	    yyDSTmode = DSTon;
223 	}
224 	|
225 	  tZONE tDST {
226 	    yyTimezone = $1;
227 	    yyDSTmode = DSTon;
228 	}
229 	;
230 
231 day	: tDAY {
232 	    yyDayOrdinal = 1;
233 	    yyDayNumber = $1;
234 	}
235 	| tDAY ',' {
236 	    yyDayOrdinal = 1;
237 	    yyDayNumber = $1;
238 	}
239 	| tUNUMBER tDAY {
240 	    yyDayOrdinal = $1;
241 	    yyDayNumber = $2;
242 	}
243 	;
244 
245 date	: tUNUMBER '/' tUNUMBER {
246 	    yyMonth = $1;
247 	    yyDay = $3;
248 	}
249 	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
250 	    if ($1 >= 100) {
251 		yyYear = $1;
252 		yyMonth = $3;
253 		yyDay = $5;
254 	    } else {
255 		yyMonth = $1;
256 		yyDay = $3;
257 		yyYear = $5;
258 	    }
259 	}
260 	| tUNUMBER tSNUMBER tSNUMBER {
261 	    /* ISO 8601 format.  yyyy-mm-dd.  */
262 	    yyYear = $1;
263 	    yyMonth = -$2;
264 	    yyDay = -$3;
265 	}
266 	| tUNUMBER tMONTH tSNUMBER {
267 	    /* e.g. 17-JUN-1992.  */
268 	    yyDay = $1;
269 	    yyMonth = $2;
270 	    yyYear = -$3;
271 	}
272 	| tMONTH tUNUMBER {
273 	    yyMonth = $1;
274 	    yyDay = $2;
275 	}
276 	| tMONTH tUNUMBER ',' tUNUMBER {
277 	    yyMonth = $1;
278 	    yyDay = $2;
279 	    yyYear = $4;
280 	}
281 	| tUNUMBER tMONTH {
282 	    yyMonth = $2;
283 	    yyDay = $1;
284 	}
285 	| tUNUMBER tMONTH tUNUMBER {
286 	    yyMonth = $2;
287 	    yyDay = $1;
288 	    yyYear = $3;
289 	}
290 	;
291 
292 rel	: relunit tAGO {
293 	    yyRelSeconds = -yyRelSeconds;
294 	    yyRelMonth = -yyRelMonth;
295 	}
296 	| relunit
297 	;
298 
299 relunit	: tUNUMBER tMINUTE_UNIT {
300 	    yyRelSeconds += $1 * $2 * 60L;
301 	}
302 	| tSNUMBER tMINUTE_UNIT {
303 	    yyRelSeconds += $1 * $2 * 60L;
304 	}
305 	| tMINUTE_UNIT {
306 	    yyRelSeconds += $1 * 60L;
307 	}
308 	| tSNUMBER tSEC_UNIT {
309 	    yyRelSeconds += $1;
310 	}
311 	| tUNUMBER tSEC_UNIT {
312 	    yyRelSeconds += $1;
313 	}
314 	| tSEC_UNIT {
315 	    yyRelSeconds++;
316 	}
317 	| tSNUMBER tMONTH_UNIT {
318 	    yyRelMonth += $1 * $2;
319 	}
320 	| tUNUMBER tMONTH_UNIT {
321 	    yyRelMonth += $1 * $2;
322 	}
323 	| tMONTH_UNIT {
324 	    yyRelMonth += $1;
325 	}
326 	;
327 
328 number	: tUNUMBER {
329 	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
330 		yyYear = $1;
331 	    else {
332 		if($1>10000) {
333 		    yyHaveDate++;
334 		    yyDay= ($1)%100;
335 		    yyMonth= ($1/100)%100;
336 		    yyYear = $1/10000;
337 		}
338 		else {
339 		    yyHaveTime++;
340 		    if ($1 < 100) {
341 			yyHour = $1;
342 			yyMinutes = 0;
343 		    }
344 		    else {
345 		    	yyHour = $1 / 100;
346 		    	yyMinutes = $1 % 100;
347 		    }
348 		    yySeconds = 0;
349 		    yyMeridian = MER24;
350 	        }
351 	    }
352 	}
353 	;
354 
355 o_merid	: /* NULL */ {
356 	    $$ = MER24;
357 	}
358 	| tMERIDIAN {
359 	    $$ = $1;
360 	}
361 	;
362 
363 %%
364 
365 /* Month and day table. */
366 static TABLE const MonthDayTable[] = {
367     { "january",	tMONTH,  1 },
368     { "february",	tMONTH,  2 },
369     { "march",		tMONTH,  3 },
370     { "april",		tMONTH,  4 },
371     { "may",		tMONTH,  5 },
372     { "june",		tMONTH,  6 },
373     { "july",		tMONTH,  7 },
374     { "august",		tMONTH,  8 },
375     { "september",	tMONTH,  9 },
376     { "sept",		tMONTH,  9 },
377     { "october",	tMONTH, 10 },
378     { "november",	tMONTH, 11 },
379     { "december",	tMONTH, 12 },
380     { "sunday",		tDAY, 0 },
381     { "monday",		tDAY, 1 },
382     { "tuesday",	tDAY, 2 },
383     { "tues",		tDAY, 2 },
384     { "wednesday",	tDAY, 3 },
385     { "wednes",		tDAY, 3 },
386     { "thursday",	tDAY, 4 },
387     { "thur",		tDAY, 4 },
388     { "thurs",		tDAY, 4 },
389     { "friday",		tDAY, 5 },
390     { "saturday",	tDAY, 6 },
391     { NULL }
392 };
393 
394 /* Time units table. */
395 static TABLE const UnitsTable[] = {
396     { "year",		tMONTH_UNIT,	12 },
397     { "month",		tMONTH_UNIT,	1 },
398     { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
399     { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
400     { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
401     { "hour",		tMINUTE_UNIT,	60 },
402     { "minute",		tMINUTE_UNIT,	1 },
403     { "min",		tMINUTE_UNIT,	1 },
404     { "second",		tSEC_UNIT,	1 },
405     { "sec",		tSEC_UNIT,	1 },
406     { NULL }
407 };
408 
409 /* Assorted relative-time words. */
410 static TABLE const OtherTable[] = {
411     { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
412     { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
413     { "today",		tMINUTE_UNIT,	0 },
414     { "now",		tMINUTE_UNIT,	0 },
415     { "last",		tUNUMBER,	-1 },
416     { "this",		tMINUTE_UNIT,	0 },
417     { "next",		tUNUMBER,	2 },
418     { "first",		tUNUMBER,	1 },
419 /*  { "second",		tUNUMBER,	2 }, */
420     { "third",		tUNUMBER,	3 },
421     { "fourth",		tUNUMBER,	4 },
422     { "fifth",		tUNUMBER,	5 },
423     { "sixth",		tUNUMBER,	6 },
424     { "seventh",	tUNUMBER,	7 },
425     { "eighth",		tUNUMBER,	8 },
426     { "ninth",		tUNUMBER,	9 },
427     { "tenth",		tUNUMBER,	10 },
428     { "eleventh",	tUNUMBER,	11 },
429     { "twelfth",	tUNUMBER,	12 },
430     { "ago",		tAGO,	1 },
431     { NULL }
432 };
433 
434 /* The timezone table. */
435 /* Some of these are commented out because a time_t can't store a float. */
436 static TABLE const TimezoneTable[] = {
437     { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
438     { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
439     { "utc",	tZONE,     HOUR( 0) },
440     { "wet",	tZONE,     HOUR( 0) },	/* Western European */
441     { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
442     { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
443     { "at",	tZONE,     HOUR( 2) },	/* Azores */
444 #if	0
445     /* For completeness.  BST is also British Summer, and GST is
446      * also Guam Standard. */
447     { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
448     { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
449 #endif
450 #if 0
451     { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
452     { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
453     { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
454 #endif
455     { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
456     { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
457     { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
458     { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
459     { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
460     { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
461     { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
462     { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
463     { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
464     { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
465     { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
466     { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
467     { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
468     { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
469     { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
470     { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
471     { "nt",	tZONE,     HOUR(11) },	/* Nome */
472     { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
473     { "cet",	tZONE,     -HOUR(1) },	/* Central European */
474     { "met",	tZONE,     -HOUR(1) },	/* Middle European */
475     { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
476     { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
477     { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
478     { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
479     { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
480     { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
481     { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
482     { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
483 #if 0
484     { "it",	tZONE,     -HOUR(3.5) },/* Iran */
485 #endif
486     { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
487     { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
488 #if 0
489     { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
490 #endif
491     { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
492 #if	0
493     /* For completeness.  NST is also Newfoundland Stanard, and SST is
494      * also Swedish Summer. */
495     { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
496     { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
497 #endif	/* 0 */
498     { "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
499     { "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
500 #if 0
501     { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
502 #endif
503     { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
504     { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
505 #if 0
506     { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
507     { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
508 #endif
509     { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
510     { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
511     { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
512     { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
513     { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
514     { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
515     { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
516     {  NULL  }
517 };
518 
519 /* Military timezone table. */
520 static TABLE const MilitaryTable[] = {
521     { "a",	tZONE,	HOUR(  1) },
522     { "b",	tZONE,	HOUR(  2) },
523     { "c",	tZONE,	HOUR(  3) },
524     { "d",	tZONE,	HOUR(  4) },
525     { "e",	tZONE,	HOUR(  5) },
526     { "f",	tZONE,	HOUR(  6) },
527     { "g",	tZONE,	HOUR(  7) },
528     { "h",	tZONE,	HOUR(  8) },
529     { "i",	tZONE,	HOUR(  9) },
530     { "k",	tZONE,	HOUR( 10) },
531     { "l",	tZONE,	HOUR( 11) },
532     { "m",	tZONE,	HOUR( 12) },
533     { "n",	tZONE,	HOUR(- 1) },
534     { "o",	tZONE,	HOUR(- 2) },
535     { "p",	tZONE,	HOUR(- 3) },
536     { "q",	tZONE,	HOUR(- 4) },
537     { "r",	tZONE,	HOUR(- 5) },
538     { "s",	tZONE,	HOUR(- 6) },
539     { "t",	tZONE,	HOUR(- 7) },
540     { "u",	tZONE,	HOUR(- 8) },
541     { "v",	tZONE,	HOUR(- 9) },
542     { "w",	tZONE,	HOUR(-10) },
543     { "x",	tZONE,	HOUR(-11) },
544     { "y",	tZONE,	HOUR(-12) },
545     { "z",	tZONE,	HOUR(  0) },
546     { NULL }
547 };
548 
549 
550 
551 
552 /* ARGSUSED */
553 static int
554 yyerror(s)
555     char	*s;
556 {
557   return 0;
558 }
559 
560 
561 static time_t
562 ToSeconds(Hours, Minutes, Seconds, Meridian)
563     time_t	Hours;
564     time_t	Minutes;
565     time_t	Seconds;
566     MERIDIAN	Meridian;
567 {
568     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
569 	return -1;
570     switch (Meridian) {
571     case MER24:
572 	if (Hours < 0 || Hours > 23)
573 	    return -1;
574 	return (Hours * 60L + Minutes) * 60L + Seconds;
575     case MERam:
576 	if (Hours < 1 || Hours > 12)
577 	    return -1;
578 	if (Hours == 12)
579 	    Hours = 0;
580 	return (Hours * 60L + Minutes) * 60L + Seconds;
581     case MERpm:
582 	if (Hours < 1 || Hours > 12)
583 	    return -1;
584 	if (Hours == 12)
585 	    Hours = 0;
586 	return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
587     default:
588 	abort ();
589     }
590     /* NOTREACHED */
591 }
592 
593 
594 /* Year is either
595    * A negative number, which means to use its absolute value (why?)
596    * A number from 0 to 99, which means a year from 1900 to 1999, or
597    * The actual year (>=100).  */
598 static time_t
599 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
600     time_t	Month;
601     time_t	Day;
602     time_t	Year;
603     time_t	Hours;
604     time_t	Minutes;
605     time_t	Seconds;
606     MERIDIAN	Meridian;
607     DSTMODE	DSTmode;
608 {
609     static int DaysInMonth[12] = {
610 	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
611     };
612     time_t	tod;
613     time_t	Julian;
614     int		i;
615 
616     if (Year < 0)
617 	Year = -Year;
618     if (Year < 69)
619 	Year += 2000;
620     else if (Year < 100) {
621 	Year += 1900;
622 	if (Year < EPOCH)
623 		Year += 100;
624     }
625     DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
626 		    ? 29 : 28;
627     /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
628        I'm too lazy to try to check for time_t overflow in another way.  */
629     if (Year < EPOCH || Year > 2038
630      || Month < 1 || Month > 12
631      /* Lint fluff:  "conversion from long may lose accuracy" */
632      || Day < 1 || Day > DaysInMonth[(int)--Month])
633 	return -1;
634 
635     for (Julian = Day - 1, i = 0; i < Month; i++)
636 	Julian += DaysInMonth[i];
637     for (i = EPOCH; i < Year; i++)
638 	Julian += 365 + (i % 4 == 0);
639     Julian *= SECSPERDAY;
640     Julian += yyTimezone * 60L;
641     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
642 	return -1;
643     Julian += tod;
644     if (DSTmode == DSTon
645      || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
646 	Julian -= 60 * 60;
647     return Julian;
648 }
649 
650 
651 static time_t
652 DSTcorrect(Start, Future)
653     time_t	Start;
654     time_t	Future;
655 {
656     time_t	StartDay;
657     time_t	FutureDay;
658 
659     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
660     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
661     return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
662 }
663 
664 
665 static time_t
666 RelativeDate(Start, DayOrdinal, DayNumber)
667     time_t	Start;
668     time_t	DayOrdinal;
669     time_t	DayNumber;
670 {
671     struct tm	*tm;
672     time_t	now;
673 
674     now = Start;
675     tm = localtime(&now);
676     now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
677     now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
678     return DSTcorrect(Start, now);
679 }
680 
681 
682 static time_t
683 RelativeMonth(Start, RelMonth)
684     time_t	Start;
685     time_t	RelMonth;
686 {
687     struct tm	*tm;
688     time_t	Month;
689     time_t	Year;
690 
691     if (RelMonth == 0)
692 	return 0;
693     tm = localtime(&Start);
694     Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
695     Year = Month / 12;
696     Month = Month % 12 + 1;
697     return DSTcorrect(Start,
698 	    Convert(Month, (time_t)tm->tm_mday, Year,
699 		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
700 		MER24, DSTmaybe));
701 }
702 
703 
704 static int
705 LookupWord(buff)
706     char		*buff;
707 {
708     register char	*p;
709     register char	*q;
710     register const TABLE	*tp;
711     int			i;
712     int			abbrev;
713 
714     /* Make it lowercase. */
715     for (p = buff; *p; p++)
716 	if (isupper(*p))
717 	    *p = tolower(*p);
718 
719     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
720 	yylval.Meridian = MERam;
721 	return tMERIDIAN;
722     }
723     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
724 	yylval.Meridian = MERpm;
725 	return tMERIDIAN;
726     }
727 
728     /* See if we have an abbreviation for a month. */
729     if (strlen(buff) == 3)
730 	abbrev = 1;
731     else if (strlen(buff) == 4 && buff[3] == '.') {
732 	abbrev = 1;
733 	buff[3] = '\0';
734     }
735     else
736 	abbrev = 0;
737 
738     for (tp = MonthDayTable; tp->name; tp++) {
739 	if (abbrev) {
740 	    if (strncmp(buff, tp->name, 3) == 0) {
741 		yylval.Number = tp->value;
742 		return tp->type;
743 	    }
744 	}
745 	else if (strcmp(buff, tp->name) == 0) {
746 	    yylval.Number = tp->value;
747 	    return tp->type;
748 	}
749     }
750 
751     for (tp = TimezoneTable; tp->name; tp++)
752 	if (strcmp(buff, tp->name) == 0) {
753 	    yylval.Number = tp->value;
754 	    return tp->type;
755 	}
756 
757     if (strcmp(buff, "dst") == 0)
758 	return tDST;
759 
760     for (tp = UnitsTable; tp->name; tp++)
761 	if (strcmp(buff, tp->name) == 0) {
762 	    yylval.Number = tp->value;
763 	    return tp->type;
764 	}
765 
766     /* Strip off any plural and try the units table again. */
767     i = strlen(buff) - 1;
768     if (buff[i] == 's') {
769 	buff[i] = '\0';
770 	for (tp = UnitsTable; tp->name; tp++)
771 	    if (strcmp(buff, tp->name) == 0) {
772 		yylval.Number = tp->value;
773 		return tp->type;
774 	    }
775 	buff[i] = 's';		/* Put back for "this" in OtherTable. */
776     }
777 
778     for (tp = OtherTable; tp->name; tp++)
779 	if (strcmp(buff, tp->name) == 0) {
780 	    yylval.Number = tp->value;
781 	    return tp->type;
782 	}
783 
784     /* Military timezones. */
785     if (buff[1] == '\0' && isalpha(*buff)) {
786 	for (tp = MilitaryTable; tp->name; tp++)
787 	    if (strcmp(buff, tp->name) == 0) {
788 		yylval.Number = tp->value;
789 		return tp->type;
790 	    }
791     }
792 
793     /* Drop out any periods and try the timezone table again. */
794     for (i = 0, p = q = buff; *q; q++)
795 	if (*q != '.')
796 	    *p++ = *q;
797 	else
798 	    i++;
799     *p = '\0';
800     if (i)
801 	for (tp = TimezoneTable; tp->name; tp++)
802 	    if (strcmp(buff, tp->name) == 0) {
803 		yylval.Number = tp->value;
804 		return tp->type;
805 	    }
806 
807     return tID;
808 }
809 
810 
811 static int
812 yylex()
813 {
814     register char	c;
815     register char	*p;
816     char		buff[20];
817     int			Count;
818     int			sign;
819 
820     for ( ; ; ) {
821 	while (isspace(*yyInput))
822 	    yyInput++;
823 
824 	if (isdigit(c = *yyInput) || c == '-' || c == '+') {
825 	    if (c == '-' || c == '+') {
826 		sign = c == '-' ? -1 : 1;
827 		if (!isdigit(*++yyInput))
828 		    /* skip the '-' sign */
829 		    continue;
830 	    }
831 	    else
832 		sign = 0;
833 	    for (yylval.Number = 0; isdigit(c = *yyInput++); )
834 		yylval.Number = 10 * yylval.Number + c - '0';
835 	    yyInput--;
836 	    if (sign < 0)
837 		yylval.Number = -yylval.Number;
838 	    return sign ? tSNUMBER : tUNUMBER;
839 	}
840 	if (isalpha(c)) {
841 	    for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
842 		if (p < &buff[sizeof buff - 1])
843 		    *p++ = c;
844 	    *p = '\0';
845 	    yyInput--;
846 	    return LookupWord(buff);
847 	}
848 	if (c != '(')
849 	    return *yyInput++;
850 	Count = 0;
851 	do {
852 	    c = *yyInput++;
853 	    if (c == '\0')
854 		return c;
855 	    if (c == '(')
856 		Count++;
857 	    else if (c == ')')
858 		Count--;
859 	} while (Count > 0);
860     }
861 }
862 
863 #define TM_YEAR_ORIGIN 1900
864 
865 /* Yield A - B, measured in seconds.  */
866 static long
867 difftm (a, b)
868      struct tm *a, *b;
869 {
870   int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
871   int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
872   int days = (
873 	      /* difference in day of year */
874 	      a->tm_yday - b->tm_yday
875 	      /* + intervening leap days */
876 	      +  ((ay >> 2) - (by >> 2))
877 	      -  (ay/100 - by/100)
878 	      +  ((ay/100 >> 2) - (by/100 >> 2))
879 	      /* + difference in years * 365 */
880 	      +  (long)(ay-by) * 365
881 	      );
882   return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
883 	      + (a->tm_min - b->tm_min))
884 	  + (a->tm_sec - b->tm_sec));
885 }
886 
887 time_t
888 get_date(p, now)
889     char		*p;
890     struct timeb	*now;
891 {
892     struct tm		*tm, gmt;
893     struct timeb	ftz;
894     time_t		Start;
895     time_t		tod;
896     time_t nowtime;
897 
898     yyInput = p;
899     if (now == NULL) {
900 	struct tm *gmt_ptr;
901 
902         now = &ftz;
903 	(void)time (&nowtime);
904 
905 	gmt_ptr = gmtime (&nowtime);
906 	if (gmt_ptr != NULL)
907 	{
908 	    /* Make a copy, in case localtime modifies *tm (I think
909 	       that comment now applies to *gmt_ptr, but I am too
910 	       lazy to dig into how gmtime and locatime allocate the
911 	       structures they return pointers to).  */
912 	    gmt = *gmt_ptr;
913 	}
914 
915 	if (! (tm = localtime (&nowtime)))
916 	    return -1;
917 
918 	if (gmt_ptr != NULL)
919 	    ftz.timezone = difftm (&gmt, tm) / 60;
920 	else
921 	    /* We are on a system like VMS, where the system clock is
922 	       in local time and the system has no concept of timezones.
923 	       Hopefully we can fake this out (for the case in which the
924 	       user specifies no timezone) by just saying the timezone
925 	       is zero.  */
926 	    ftz.timezone = 0;
927 
928 	if(tm->tm_isdst)
929 	    ftz.timezone += 60;
930     }
931     else
932     {
933 	nowtime = now->time;
934     }
935 
936     tm = localtime(&nowtime);
937     yyYear = tm->tm_year + 1900;
938     yyMonth = tm->tm_mon + 1;
939     yyDay = tm->tm_mday;
940     yyTimezone = now->timezone;
941     yyDSTmode = DSTmaybe;
942     yyHour = 0;
943     yyMinutes = 0;
944     yySeconds = 0;
945     yyMeridian = MER24;
946     yyRelSeconds = 0;
947     yyRelMonth = 0;
948     yyHaveDate = 0;
949     yyHaveDay = 0;
950     yyHaveRel = 0;
951     yyHaveTime = 0;
952     yyHaveZone = 0;
953 
954     if (yyparse()
955      || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
956 	return -1;
957 
958     if (yyHaveDate || yyHaveTime || yyHaveDay) {
959 	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
960 		    yyMeridian, yyDSTmode);
961 	if (Start < 0)
962 	    return -1;
963     }
964     else {
965 	Start = nowtime;
966 	if (!yyHaveRel)
967 	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
968     }
969 
970     Start += yyRelSeconds;
971     Start += RelativeMonth(Start, yyRelMonth);
972 
973     if (yyHaveDay && !yyHaveDate) {
974 	tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
975 	Start += tod;
976     }
977 
978     /* Have to do *something* with a legitimate -1 so it's distinguishable
979      * from the error return value.  (Alternately could set errno on error.) */
980     return Start == -1 ? 0 : Start;
981 }
982 
983 
984 #if	defined(TEST)
985 
986 /* ARGSUSED */
987 int
988 main(ac, av)
989     int		ac;
990     char	*av[];
991 {
992     char	buff[128];
993     time_t	d;
994 
995     (void)printf("Enter date, or blank line to exit.\n\t> ");
996     (void)fflush(stdout);
997     while (gets(buff) && buff[0]) {
998 	d = get_date(buff, (struct timeb *)NULL);
999 	if (d == -1)
1000 	    (void)printf("Bad format - couldn't convert.\n");
1001 	else
1002 	    (void)printf("%s", ctime(&d));
1003 	(void)printf("\t> ");
1004 	(void)fflush(stdout);
1005     }
1006     exit(0);
1007     /* NOTREACHED */
1008 }
1009 #endif	/* defined(TEST) */
1010