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