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