1204a1de21e9ace4444adbd2d64eb6c1a0ce2016
[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 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     { "mewt",   tZONE,     -HOUR(1) },  /* Middle European Winter */
488     { "mest",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
489     { "mesz",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
490     { "swt",    tZONE,     -HOUR(1) },  /* Swedish Winter */
491     { "sst",    tDAYZONE,  -HOUR(1) },  /* Swedish Summer */
492     { "fwt",    tZONE,     -HOUR(1) },  /* French Winter */
493     { "fst",    tDAYZONE,  -HOUR(1) },  /* French Summer */
494     { "eet",    tZONE,     -HOUR(2) },  /* Eastern Europe, USSR Zone 1 */
495     { "bt",     tZONE,     -HOUR(3) },  /* Baghdad, USSR Zone 2 */
496 #if 0
497     { "it",     tZONE,     -HOUR(3.5) },/* Iran */
498 #endif
499     { "zp4",    tZONE,     -HOUR(4) },  /* USSR Zone 3 */
500     { "zp5",    tZONE,     -HOUR(5) },  /* USSR Zone 4 */
501 #if 0
502     { "ist",    tZONE,     -HOUR(5.5) },/* Indian Standard */
503 #endif
504     { "zp6",    tZONE,     -HOUR(6) },  /* USSR Zone 5 */
505 #if     0
506     /* For completeness.  NST is also Newfoundland Standard, and SST is
507      * also Swedish Summer. */
508     { "nst",    tZONE,     -HOUR(6.5) },/* North Sumatra */
509     { "sst",    tZONE,     -HOUR(7) },  /* South Sumatra, USSR Zone 6 */
510 #endif  /* 0 */
511     { "wast",   tZONE,     -HOUR(7) },  /* West Australian Standard */
512     { "wadt",   tDAYZONE,  -HOUR(7) },  /* West Australian Daylight */
513 #if 0
514     { "jt",     tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
515 #endif
516     { "cct",    tZONE,     -HOUR(8) },  /* China Coast, USSR Zone 7 */
517     { "jst",    tZONE,     -HOUR(9) },  /* Japan Standard, USSR Zone 8 */
518 #if 0
519     { "cast",   tZONE,     -HOUR(9.5) },/* Central Australian Standard */
520     { "cadt",   tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
521 #endif
522     { "east",   tZONE,     -HOUR(10) }, /* Eastern Australian Standard */
523     { "eadt",   tDAYZONE,  -HOUR(10) }, /* Eastern Australian Daylight */
524     { "gst",    tZONE,     -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
525     { "nzt",    tZONE,     -HOUR(12) }, /* New Zealand */
526     { "nzst",   tZONE,     -HOUR(12) }, /* New Zealand Standard */
527     { "nzdt",   tDAYZONE,  -HOUR(12) }, /* New Zealand Daylight */
528     { "idle",   tZONE,     -HOUR(12) }, /* International Date Line East */
529     {  NULL  }
530 };
531
532 /* Military timezone table. */
533 static TABLE const MilitaryTable[] = {
534     { "a",      tZONE,  HOUR(  1) },
535     { "b",      tZONE,  HOUR(  2) },
536     { "c",      tZONE,  HOUR(  3) },
537     { "d",      tZONE,  HOUR(  4) },
538     { "e",      tZONE,  HOUR(  5) },
539     { "f",      tZONE,  HOUR(  6) },
540     { "g",      tZONE,  HOUR(  7) },
541     { "h",      tZONE,  HOUR(  8) },
542     { "i",      tZONE,  HOUR(  9) },
543     { "k",      tZONE,  HOUR( 10) },
544     { "l",      tZONE,  HOUR( 11) },
545     { "m",      tZONE,  HOUR( 12) },
546     { "n",      tZONE,  HOUR(- 1) },
547     { "o",      tZONE,  HOUR(- 2) },
548     { "p",      tZONE,  HOUR(- 3) },
549     { "q",      tZONE,  HOUR(- 4) },
550     { "r",      tZONE,  HOUR(- 5) },
551     { "s",      tZONE,  HOUR(- 6) },
552     { "t",      tZONE,  HOUR(- 7) },
553     { "u",      tZONE,  HOUR(- 8) },
554     { "v",      tZONE,  HOUR(- 9) },
555     { "w",      tZONE,  HOUR(-10) },
556     { "x",      tZONE,  HOUR(-11) },
557     { "y",      tZONE,  HOUR(-12) },
558     { "z",      tZONE,  HOUR(  0) },
559     { NULL }
560 };
561
562 \f
563
564
565 /* ARGSUSED */
566 static int
567 yyerror(s)
568     char        *s;
569 {
570   return 0;
571 }
572
573
574 static time_t
575 ToSeconds(Hours, Minutes, Seconds, Meridian)
576     time_t      Hours;
577     time_t      Minutes;
578     time_t      Seconds;
579     MERIDIAN    Meridian;
580 {
581     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
582         return -1;
583     switch (Meridian) {
584     case MER24:
585         if (Hours < 0 || Hours > 23)
586             return -1;
587         return (Hours * 60L + Minutes) * 60L + Seconds;
588     case MERam:
589         if (Hours < 1 || Hours > 12)
590             return -1;
591         return (Hours * 60L + Minutes) * 60L + Seconds;
592     case MERpm:
593         if (Hours < 1 || Hours > 12)
594             return -1;
595         return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
596     default:
597         abort ();
598     }
599     /* NOTREACHED */
600 }
601
602
603 static time_t
604 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
605     time_t      Month;
606     time_t      Day;
607     time_t      Year;
608     time_t      Hours;
609     time_t      Minutes;
610     time_t      Seconds;
611     MERIDIAN    Meridian;
612     DSTMODE     DSTmode;
613 {
614     static int DaysInMonth[12] = {
615         31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
616     };
617     time_t      tod;
618     time_t      Julian;
619     int         i;
620
621     if (Year < 0)
622         Year = -Year;
623     if (Year < 100)
624         Year += 1900;
625     DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
626                     ? 29 : 28;
627     if (Year < EPOCH || Year > 1999
628      || Month < 1 || Month > 12
629      /* Lint fluff:  "conversion from long may lose accuracy" */
630      || Day < 1 || Day > DaysInMonth[(int)--Month])
631         return -1;
632
633     for (Julian = Day - 1, i = 0; i < Month; i++)
634         Julian += DaysInMonth[i];
635     for (i = EPOCH; i < Year; i++)
636         Julian += 365 + (i % 4 == 0);
637     Julian *= SECSPERDAY;
638     Julian += yyTimezone * 60L;
639     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
640         return -1;
641     Julian += tod;
642     if (DSTmode == DSTon
643      || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
644         Julian -= 60 * 60;
645     return Julian;
646 }
647
648
649 static time_t
650 DSTcorrect(Start, Future)
651     time_t      Start;
652     time_t      Future;
653 {
654     time_t      StartDay;
655     time_t      FutureDay;
656
657     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
658     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
659     return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
660 }
661
662
663 static time_t
664 RelativeDate(Start, DayOrdinal, DayNumber)
665     time_t      Start;
666     time_t      DayOrdinal;
667     time_t      DayNumber;
668 {
669     struct tm   *tm;
670     time_t      now;
671
672     now = Start;
673     tm = localtime(&now);
674     now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
675     now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
676     return DSTcorrect(Start, now);
677 }
678
679
680 static time_t
681 RelativeMonth(Start, RelMonth)
682     time_t      Start;
683     time_t      RelMonth;
684 {
685     struct tm   *tm;
686     time_t      Month;
687     time_t      Year;
688
689     if (RelMonth == 0)
690         return 0;
691     tm = localtime(&Start);
692     Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
693     Year = Month / 12;
694     Month = Month % 12 + 1;
695     return DSTcorrect(Start,
696             Convert(Month, (time_t)tm->tm_mday, Year,
697                 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
698                 MER24, DSTmaybe));
699 }
700
701
702 static int
703 LookupWord(buff)
704     char                *buff;
705 {
706     register char       *p;
707     register char       *q;
708     register const TABLE        *tp;
709     int                 i;
710     int                 abbrev;
711
712     /* Make it lowercase. */
713     for (p = buff; *p; p++)
714         if (isupper(*p))
715             *p = tolower(*p);
716
717     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
718         yylval.Meridian = MERam;
719         return tMERIDIAN;
720     }
721     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
722         yylval.Meridian = MERpm;
723         return tMERIDIAN;
724     }
725
726     /* See if we have an abbreviation for a month. */
727     if (strlen(buff) == 3)
728         abbrev = 1;
729     else if (strlen(buff) == 4 && buff[3] == '.') {
730         abbrev = 1;
731         buff[3] = '\0';
732     }
733     else
734         abbrev = 0;
735
736     for (tp = MonthDayTable; tp->name; tp++) {
737         if (abbrev) {
738             if (strncmp(buff, tp->name, 3) == 0) {
739                 yylval.Number = tp->value;
740                 return tp->type;
741             }
742         }
743         else if (strcmp(buff, tp->name) == 0) {
744             yylval.Number = tp->value;
745             return tp->type;
746         }
747     }
748
749     for (tp = TimezoneTable; tp->name; tp++)
750         if (strcmp(buff, tp->name) == 0) {
751             yylval.Number = tp->value;
752             return tp->type;
753         }
754
755     if (strcmp(buff, "dst") == 0) 
756         return tDST;
757
758     for (tp = UnitsTable; tp->name; tp++)
759         if (strcmp(buff, tp->name) == 0) {
760             yylval.Number = tp->value;
761             return tp->type;
762         }
763
764     /* Strip off any plural and try the units table again. */
765     i = strlen(buff) - 1;
766     if (buff[i] == 's') {
767         buff[i] = '\0';
768         for (tp = UnitsTable; tp->name; tp++)
769             if (strcmp(buff, tp->name) == 0) {
770                 yylval.Number = tp->value;
771                 return tp->type;
772             }
773         buff[i] = 's';          /* Put back for "this" in OtherTable. */
774     }
775
776     for (tp = OtherTable; tp->name; tp++)
777         if (strcmp(buff, tp->name) == 0) {
778             yylval.Number = tp->value;
779             return tp->type;
780         }
781
782     /* Military timezones. */
783     if (buff[1] == '\0' && isalpha(*buff)) {
784         for (tp = MilitaryTable; tp->name; tp++)
785             if (strcmp(buff, tp->name) == 0) {
786                 yylval.Number = tp->value;
787                 return tp->type;
788             }
789     }
790
791     /* Drop out any periods and try the timezone table again. */
792     for (i = 0, p = q = buff; *q; q++)
793         if (*q != '.')
794             *p++ = *q;
795         else
796             i++;
797     *p = '\0';
798     if (i)
799         for (tp = TimezoneTable; tp->name; tp++)
800             if (strcmp(buff, tp->name) == 0) {
801                 yylval.Number = tp->value;
802                 return tp->type;
803             }
804
805     return tID;
806 }
807
808
809 static int
810 yylex()
811 {
812     register char       c;
813     register char       *p;
814     char                buff[20];
815     int                 Count;
816     int                 sign;
817
818     for ( ; ; ) {
819         while (isspace(*yyInput))
820             yyInput++;
821
822         if (isdigit(c = *yyInput) || c == '-' || c == '+') {
823             if (c == '-' || c == '+') {
824                 sign = c == '-' ? -1 : 1;
825                 if (!isdigit(*++yyInput))
826                     /* skip the '-' sign */
827                     continue;
828             }
829             else
830                 sign = 0;
831             for (yylval.Number = 0; isdigit(c = *yyInput++); )
832                 yylval.Number = 10 * yylval.Number + c - '0';
833             yyInput--;
834             if (sign < 0)
835                 yylval.Number = -yylval.Number;
836             return sign ? tSNUMBER : tUNUMBER;
837         }
838         if (isalpha(c)) {
839             for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
840                 if (p < &buff[sizeof buff - 1])
841                     *p++ = c;
842             *p = '\0';
843             yyInput--;
844             return LookupWord(buff);
845         }
846         if (c != '(')
847             return *yyInput++;
848         Count = 0;
849         do {
850             c = *yyInput++;
851             if (c == '\0')
852                 return c;
853             if (c == '(')
854                 Count++;
855             else if (c == ')')
856                 Count--;
857         } while (Count > 0);
858     }
859 }
860
861 #define TM_YEAR_ORIGIN 1900
862
863 /* Yield A - B, measured in seconds.  */
864 static long
865 difftm (a, b)
866      struct tm *a, *b;
867 {
868   int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
869   int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
870   long days = (
871                /* difference in day of year */
872                a->tm_yday - b->tm_yday
873                /* + intervening leap days */
874                +  ((ay >> 2) - (by >> 2))
875                -  (ay/100 - by/100)
876                +  ((ay/100 >> 2) - (by/100 >> 2))
877                /* + difference in years * 365 */
878                +  (long)(ay-by) * 365
879                );
880   return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
881               + (a->tm_min - b->tm_min))
882           + (a->tm_sec - b->tm_sec));
883 }
884
885 time_t
886 get_date(p, now)
887     char                *p;
888     struct timeb        *now;
889 {
890     struct tm           *tm, gmt;
891     struct timeb        ftz;
892     time_t              Start;
893     time_t              tod;
894
895     yyInput = p;
896     if (now == NULL) {
897         now = &ftz;
898         (void)time(&ftz.time);
899
900         if (! (tm = gmtime (&ftz.time)))
901             return -1;
902         gmt = *tm;      /* Make a copy, in case localtime modifies *tm.  */
903
904         if (! (tm = localtime (&ftz.time)))
905             return -1;
906         
907         ftz.timezone = difftm (&gmt, tm) / 60;
908         if(tm->tm_isdst)
909             ftz.timezone += 60;
910     }
911
912     tm = localtime(&now->time);
913     yyYear = tm->tm_year;
914     yyMonth = tm->tm_mon + 1;
915     yyDay = tm->tm_mday;
916     yyTimezone = now->timezone;
917     yyDSTmode = DSTmaybe;
918     yyHour = 0;
919     yyMinutes = 0;
920     yySeconds = 0;
921     yyMeridian = MER24;
922     yyRelSeconds = 0;
923     yyRelMonth = 0;
924     yyHaveDate = 0;
925     yyHaveDay = 0;
926     yyHaveRel = 0;
927     yyHaveTime = 0;
928     yyHaveZone = 0;
929
930     if (yyparse()
931      || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
932         return -1;
933
934     if (yyHaveDate || yyHaveTime || yyHaveDay) {
935         Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
936                     yyMeridian, yyDSTmode);
937         if (Start < 0)
938             return -1;
939     }
940     else {
941         Start = now->time;
942         if (!yyHaveRel)
943             Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
944     }
945
946     Start += yyRelSeconds;
947     Start += RelativeMonth(Start, yyRelMonth);
948
949     if (yyHaveDay && !yyHaveDate) {
950         tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
951         Start += tod;
952     }
953
954     /* Have to do *something* with a legitimate -1 so it's distinguishable
955      * from the error return value.  (Alternately could set errno on error.) */
956     return Start == -1 ? 0 : Start;
957 }
958
959
960 #if     defined(TEST)
961
962 /* ARGSUSED */
963 int
964 main(ac, av)
965     int         ac;
966     char        *av[];
967 {
968     char        buff[128];
969     time_t      d;
970
971     (void)printf("Enter date, or blank line to exit.\n\t> ");
972     (void)fflush(stdout);
973     while (gets(buff) && buff[0]) {
974         d = get_date(buff, (struct timeb *)NULL);
975         if (d == -1)
976             (void)printf("Bad format - couldn't convert.\n");
977         else
978             (void)printf("%s", ctime(&d));
979         (void)printf("\t> ");
980         (void)fflush(stdout);
981     }
982     exit(0);
983     /* NOTREACHED */
984 }
985 #endif  /* defined(TEST) */