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