1861c9c73c042eb7e79e77660699813d4c3823c2
[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 #define MAX_BUFF_LEN    128   /* size of buffer to read the date into */
110
111 /*
112 **  An entry in the lexical lookup table.
113 */
114 typedef struct _TABLE {
115     char        *name;
116     int         type;
117     time_t      value;
118 } TABLE;
119
120
121 /*
122 **  Daylight-savings mode:  on, off, or not yet known.
123 */
124 typedef enum _DSTMODE {
125     DSTon, DSToff, DSTmaybe
126 } DSTMODE;
127
128 /*
129 **  Meridian:  am, pm, or 24-hour style.
130 */
131 typedef enum _MERIDIAN {
132     MERam, MERpm, MER24
133 } MERIDIAN;
134
135
136 /*
137 **  Global variables.  We could get rid of most of these by using a good
138 **  union as the yacc stack.  (This routine was originally written before
139 **  yacc had the %union construct.)  Maybe someday; right now we only use
140 **  the %union very rarely.
141 */
142 static char     *yyInput;
143 static DSTMODE  yyDSTmode;
144 static time_t   yyDayOrdinal;
145 static time_t   yyDayNumber;
146 static int      yyHaveDate;
147 static int      yyHaveDay;
148 static int      yyHaveRel;
149 static int      yyHaveTime;
150 static int      yyHaveZone;
151 static time_t   yyTimezone;
152 static time_t   yyDay;
153 static time_t   yyHour;
154 static time_t   yyMinutes;
155 static time_t   yyMonth;
156 static time_t   yySeconds;
157 static time_t   yyYear;
158 static MERIDIAN yyMeridian;
159 static time_t   yyRelMonth;
160 static time_t   yyRelSeconds;
161
162 %}
163
164 %union {
165     time_t              Number;
166     enum _MERIDIAN      Meridian;
167 }
168
169 %token  tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
170 %token  tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
171
172 %type   <Number>        tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
173 %type   <Number>        tSEC_UNIT tSNUMBER tUNUMBER tZONE
174 %type   <Meridian>      tMERIDIAN o_merid
175
176 %%
177
178 spec    : /* NULL */
179         | spec item
180         ;
181
182 item    : time {
183             yyHaveTime++;
184         }
185         | zone {
186             yyHaveZone++;
187         }
188         | date {
189             yyHaveDate++;
190         }
191         | day {
192             yyHaveDay++;
193         }
194         | rel {
195             yyHaveRel++;
196         }
197         | number
198         ;
199
200 time    : tUNUMBER tMERIDIAN {
201             yyHour = $1;
202             yyMinutes = 0;
203             yySeconds = 0;
204             yyMeridian = $2;
205         }
206         | tUNUMBER ':' tUNUMBER o_merid {
207             yyHour = $1;
208             yyMinutes = $3;
209             yySeconds = 0;
210             yyMeridian = $4;
211         }
212         | tUNUMBER ':' tUNUMBER tSNUMBER {
213             yyHour = $1;
214             yyMinutes = $3;
215             yyMeridian = MER24;
216             yyDSTmode = DSToff;
217             yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
218         }
219         | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
220             yyHour = $1;
221             yyMinutes = $3;
222             yySeconds = $5;
223             yyMeridian = $6;
224         }
225         | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
226             yyHour = $1;
227             yyMinutes = $3;
228             yySeconds = $5;
229             yyMeridian = MER24;
230             yyDSTmode = DSToff;
231             yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
232         }
233         ;
234
235 zone    : tZONE {
236             yyTimezone = $1;
237             yyDSTmode = DSToff;
238         }
239         | tDAYZONE {
240             yyTimezone = $1;
241             yyDSTmode = DSTon;
242         }
243         |
244           tZONE tDST {
245             yyTimezone = $1;
246             yyDSTmode = DSTon;
247         }
248         ;
249
250 day     : tDAY {
251             yyDayOrdinal = 1;
252             yyDayNumber = $1;
253         }
254         | tDAY ',' {
255             yyDayOrdinal = 1;
256             yyDayNumber = $1;
257         }
258         | tUNUMBER tDAY {
259             yyDayOrdinal = $1;
260             yyDayNumber = $2;
261         }
262         ;
263
264 date    : tUNUMBER '/' tUNUMBER {
265             yyMonth = $1;
266             yyDay = $3;
267         }
268         | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
269             yyMonth = $1;
270             yyDay = $3;
271             yyYear = $5;
272         }
273         | tUNUMBER tSNUMBER tSNUMBER {
274             /* ISO 8601 format.  yyyy-mm-dd.  */
275             yyYear = $1;
276             yyMonth = -$2;
277             yyDay = -$3;
278         }
279         | tUNUMBER tMONTH tSNUMBER {
280             /* e.g. 17-JUN-1992.  */
281             yyDay = $1;
282             yyMonth = $2;
283             yyYear = -$3;
284         }
285         | tMONTH tUNUMBER {
286             yyMonth = $1;
287             yyDay = $2;
288         }
289         | tMONTH tUNUMBER ',' tUNUMBER {
290             yyMonth = $1;
291             yyDay = $2;
292             yyYear = $4;
293         }
294         | tUNUMBER tMONTH {
295             yyMonth = $2;
296             yyDay = $1;
297         }
298         | tUNUMBER tMONTH tUNUMBER {
299             yyMonth = $2;
300             yyDay = $1;
301             yyYear = $3;
302         }
303         ;
304
305 rel     : relunit tAGO {
306             yyRelSeconds = -yyRelSeconds;
307             yyRelMonth = -yyRelMonth;
308         }
309         | relunit
310         ;
311
312 relunit : tUNUMBER tMINUTE_UNIT {
313             yyRelSeconds += $1 * $2 * 60L;
314         }
315         | tSNUMBER tMINUTE_UNIT {
316             yyRelSeconds += $1 * $2 * 60L;
317         }
318         | tMINUTE_UNIT {
319             yyRelSeconds += $1 * 60L;
320         }
321         | tSNUMBER tSEC_UNIT {
322             yyRelSeconds += $1;
323         }
324         | tUNUMBER tSEC_UNIT {
325             yyRelSeconds += $1;
326         }
327         | tSEC_UNIT {
328             yyRelSeconds++;
329         }
330         | tSNUMBER tMONTH_UNIT {
331             yyRelMonth += $1 * $2;
332         }
333         | tUNUMBER tMONTH_UNIT {
334             yyRelMonth += $1 * $2;
335         }
336         | tMONTH_UNIT {
337             yyRelMonth += $1;
338         }
339         ;
340
341 number  : tUNUMBER {
342             if (yyHaveTime && yyHaveDate && !yyHaveRel)
343                 yyYear = $1;
344             else {
345                 if ($1>10000) {
346                     yyHaveDate++;
347                     yyDay= ($1)%100;
348                     yyMonth= ($1/100)%100;
349                     yyYear = $1/10000;
350                 }
351                 else {
352                     yyHaveTime++;
353                     if ($1 < 100) {
354                         yyHour = $1;
355                         yyMinutes = 0;
356                     }
357                     else {
358                         yyHour = $1 / 100;
359                         yyMinutes = $1 % 100;
360                     }
361                     yySeconds = 0;
362                     yyMeridian = MER24;
363                 }
364             }
365         }
366         ;
367
368 o_merid : /* NULL */ {
369             $$ = MER24;
370         }
371         | tMERIDIAN {
372             $$ = $1;
373         }
374         ;
375
376 %%
377
378 /* Month and day table. */
379 static TABLE const MonthDayTable[] = {
380     { "january",        tMONTH,  1 },
381     { "february",       tMONTH,  2 },
382     { "march",          tMONTH,  3 },
383     { "april",          tMONTH,  4 },
384     { "may",            tMONTH,  5 },
385     { "june",           tMONTH,  6 },
386     { "july",           tMONTH,  7 },
387     { "august",         tMONTH,  8 },
388     { "september",      tMONTH,  9 },
389     { "sept",           tMONTH,  9 },
390     { "october",        tMONTH, 10 },
391     { "november",       tMONTH, 11 },
392     { "december",       tMONTH, 12 },
393     { "sunday",         tDAY, 0 },
394     { "monday",         tDAY, 1 },
395     { "tuesday",        tDAY, 2 },
396     { "tues",           tDAY, 2 },
397     { "wednesday",      tDAY, 3 },
398     { "wednes",         tDAY, 3 },
399     { "thursday",       tDAY, 4 },
400     { "thur",           tDAY, 4 },
401     { "thurs",          tDAY, 4 },
402     { "friday",         tDAY, 5 },
403     { "saturday",       tDAY, 6 },
404     { NULL }
405 };
406
407 /* Time units table. */
408 static TABLE const UnitsTable[] = {
409     { "year",           tMONTH_UNIT,    12 },
410     { "month",          tMONTH_UNIT,    1 },
411     { "fortnight",      tMINUTE_UNIT,   14 * 24 * 60 },
412     { "week",           tMINUTE_UNIT,   7 * 24 * 60 },
413     { "day",            tMINUTE_UNIT,   1 * 24 * 60 },
414     { "hour",           tMINUTE_UNIT,   60 },
415     { "minute",         tMINUTE_UNIT,   1 },
416     { "min",            tMINUTE_UNIT,   1 },
417     { "second",         tSEC_UNIT,      1 },
418     { "sec",            tSEC_UNIT,      1 },
419     { NULL }
420 };
421
422 /* Assorted relative-time words. */
423 static TABLE const OtherTable[] = {
424     { "tomorrow",       tMINUTE_UNIT,   1 * 24 * 60 },
425     { "yesterday",      tMINUTE_UNIT,   -1 * 24 * 60 },
426     { "today",          tMINUTE_UNIT,   0 },
427     { "now",            tMINUTE_UNIT,   0 },
428     { "last",           tUNUMBER,       -1 },
429     { "this",           tMINUTE_UNIT,   0 },
430     { "next",           tUNUMBER,       2 },
431     { "first",          tUNUMBER,       1 },
432 /*  { "second",         tUNUMBER,       2 }, */
433     { "third",          tUNUMBER,       3 },
434     { "fourth",         tUNUMBER,       4 },
435     { "fifth",          tUNUMBER,       5 },
436     { "sixth",          tUNUMBER,       6 },
437     { "seventh",        tUNUMBER,       7 },
438     { "eighth",         tUNUMBER,       8 },
439     { "ninth",          tUNUMBER,       9 },
440     { "tenth",          tUNUMBER,       10 },
441     { "eleventh",       tUNUMBER,       11 },
442     { "twelfth",        tUNUMBER,       12 },
443     { "ago",            tAGO,   1 },
444     { NULL }
445 };
446
447 /* The timezone table. */
448 /* Some of these are commented out because a time_t can't store a float. */
449 static TABLE const TimezoneTable[] = {
450     { "gmt",    tZONE,     HOUR ( 0) }, /* Greenwich Mean */
451     { "ut",     tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
452     { "utc",    tZONE,     HOUR ( 0) },
453     { "wet",    tZONE,     HOUR ( 0) }, /* Western European */
454     { "bst",    tDAYZONE,  HOUR ( 0) }, /* British Summer */
455     { "wat",    tZONE,     HOUR ( 1) }, /* West Africa */
456     { "at",     tZONE,     HOUR ( 2) }, /* Azores */
457 #if     0
458     /* For completeness.  BST is also British Summer, and GST is
459      * also Guam Standard. */
460     { "bst",    tZONE,     HOUR ( 3) }, /* Brazil Standard */
461     { "gst",    tZONE,     HOUR ( 3) }, /* Greenland Standard */
462 #endif
463 #if 0
464     { "nft",    tZONE,     HOUR (3.5) },        /* Newfoundland */
465     { "nst",    tZONE,     HOUR (3.5) },        /* Newfoundland Standard */
466     { "ndt",    tDAYZONE,  HOUR (3.5) },        /* Newfoundland Daylight */
467 #endif
468     { "ast",    tZONE,     HOUR ( 4) }, /* Atlantic Standard */
469     { "adt",    tDAYZONE,  HOUR ( 4) }, /* Atlantic Daylight */
470     { "est",    tZONE,     HOUR ( 5) }, /* Eastern Standard */
471     { "edt",    tDAYZONE,  HOUR ( 5) }, /* Eastern Daylight */
472     { "cst",    tZONE,     HOUR ( 6) }, /* Central Standard */
473     { "cdt",    tDAYZONE,  HOUR ( 6) }, /* Central Daylight */
474     { "mst",    tZONE,     HOUR ( 7) }, /* Mountain Standard */
475     { "mdt",    tDAYZONE,  HOUR ( 7) }, /* Mountain Daylight */
476     { "pst",    tZONE,     HOUR ( 8) }, /* Pacific Standard */
477     { "pdt",    tDAYZONE,  HOUR ( 8) }, /* Pacific Daylight */
478     { "yst",    tZONE,     HOUR ( 9) }, /* Yukon Standard */
479     { "ydt",    tDAYZONE,  HOUR ( 9) }, /* Yukon Daylight */
480     { "hst",    tZONE,     HOUR (10) }, /* Hawaii Standard */
481     { "hdt",    tDAYZONE,  HOUR (10) }, /* Hawaii Daylight */
482     { "cat",    tZONE,     HOUR (10) }, /* Central Alaska */
483     { "ahst",   tZONE,     HOUR (10) }, /* Alaska-Hawaii Standard */
484     { "nt",     tZONE,     HOUR (11) }, /* Nome */
485     { "idlw",   tZONE,     HOUR (12) }, /* International Date Line West */
486     { "cet",    tZONE,     -HOUR (1) }, /* Central European */
487     { "met",    tZONE,     -HOUR (1) }, /* Middle European */
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   long 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 (fgets (buff, MAX_BUFF_LEN, stdin) && 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) */