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