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