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