OSDN Git Service

Initial revision
[pf3gnuchains/sourceware.git] / tcl / generic / tclGetDate.y
1 /* 
2  * tclGetDate.y --
3  *
4  *      Contains yacc grammar for parsing date and time strings.
5  *      The output of this file should be the file tclDate.c which
6  *      is used directly in the Tcl sources.
7  *
8  * Copyright (c) 1992-1995 Karl Lehenbauer and Mark Diekhans.
9  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  *
14  * RCS: @(#) $Id$
15  */
16
17 %{
18 /* 
19  * tclDate.c --
20  *
21  *      This file is generated from a yacc grammar defined in
22  *      the file tclGetDate.y.  It should not be edited directly.
23  *
24  * Copyright (c) 1992-1995 Karl Lehenbauer and Mark Diekhans.
25  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
26  *
27  * See the file "license.terms" for information on usage and redistribution
28  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
29  *
30  * SCCSID
31  */
32
33 #include "tclInt.h"
34 #include "tclPort.h"
35
36 #ifdef MAC_TCL
37 #   define EPOCH           1904
38 #   define START_OF_TIME   1904
39 #   define END_OF_TIME     2039
40 #else
41 #   define EPOCH           1970
42 #   define START_OF_TIME   1902
43 #   define END_OF_TIME     2037
44 #endif
45
46 /*
47  * The offset of tm_year of struct tm returned by localtime, gmtime, etc.
48  * I don't know how universal this is; K&R II, the NetBSD manpages, and
49  * ../compat/strftime.c all agree that tm_year is the year-1900.  However,
50  * some systems may have a different value.  This #define should be the
51  * same as in ../compat/strftime.c.
52  */
53 #define TM_YEAR_BASE 1900
54
55 #define HOUR(x)         ((int) (60 * x))
56 #define SECSPERDAY      (24L * 60L * 60L)
57
58
59 /*
60  *  An entry in the lexical lookup table.
61  */
62 typedef struct _TABLE {
63     char        *name;
64     int         type;
65     time_t      value;
66 } TABLE;
67
68
69 /*
70  *  Daylight-savings mode:  on, off, or not yet known.
71  */
72 typedef enum _DSTMODE {
73     DSTon, DSToff, DSTmaybe
74 } DSTMODE;
75
76 /*
77  *  Meridian:  am, pm, or 24-hour style.
78  */
79 typedef enum _MERIDIAN {
80     MERam, MERpm, MER24
81 } MERIDIAN;
82
83
84 /*
85  *  Global variables.  We could get rid of most of these by using a good
86  *  union as the yacc stack.  (This routine was originally written before
87  *  yacc had the %union construct.)  Maybe someday; right now we only use
88  *  the %union very rarely.
89  */
90 static char     *yyInput;
91 static DSTMODE  yyDSTmode;
92 static time_t   yyDayOrdinal;
93 static time_t   yyDayNumber;
94 static int      yyHaveDate;
95 static int      yyHaveDay;
96 static int      yyHaveRel;
97 static int      yyHaveTime;
98 static int      yyHaveZone;
99 static time_t   yyTimezone;
100 static time_t   yyDay;
101 static time_t   yyHour;
102 static time_t   yyMinutes;
103 static time_t   yyMonth;
104 static time_t   yySeconds;
105 static time_t   yyYear;
106 static MERIDIAN yyMeridian;
107 static time_t   yyRelMonth;
108 static time_t   yyRelSeconds;
109
110
111 /*
112  * Prototypes of internal functions.
113  */
114 static void     yyerror _ANSI_ARGS_((char *s));
115 static time_t   ToSeconds _ANSI_ARGS_((time_t Hours, time_t Minutes,
116                     time_t Seconds, MERIDIAN Meridian));
117 static int      Convert _ANSI_ARGS_((time_t Month, time_t Day, time_t Year,
118                     time_t Hours, time_t Minutes, time_t Seconds,
119                     MERIDIAN Meridia, DSTMODE DSTmode, time_t *TimePtr));
120 static time_t   DSTcorrect _ANSI_ARGS_((time_t Start, time_t Future));
121 static time_t   RelativeDate _ANSI_ARGS_((time_t Start, time_t DayOrdinal,
122                     time_t DayNumber));
123 static int      RelativeMonth _ANSI_ARGS_((time_t Start, time_t RelMonth,
124                     time_t *TimePtr));
125 static int      LookupWord _ANSI_ARGS_((char *buff));
126 static int      yylex _ANSI_ARGS_((void));
127
128 int
129 yyparse _ANSI_ARGS_((void));
130 %}
131
132 %union {
133     time_t              Number;
134     enum _MERIDIAN      Meridian;
135 }
136
137 %token  tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
138 %token  tSEC_UNIT tSNUMBER tUNUMBER tZONE tEPOCH tDST
139
140 %type   <Number>        tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT tDST
141 %type   <Number>        tSEC_UNIT tSNUMBER tUNUMBER tZONE
142 %type   <Meridian>      tMERIDIAN o_merid
143
144 %%
145
146 spec    : /* NULL */
147         | spec item
148         ;
149
150 item    : time {
151             yyHaveTime++;
152         }
153         | zone {
154             yyHaveZone++;
155         }
156         | date {
157             yyHaveDate++;
158         }
159         | day {
160             yyHaveDay++;
161         }
162         | rel {
163             yyHaveRel++;
164         }
165         | number
166         ;
167
168 time    : tUNUMBER tMERIDIAN {
169             yyHour = $1;
170             yyMinutes = 0;
171             yySeconds = 0;
172             yyMeridian = $2;
173         }
174         | tUNUMBER ':' tUNUMBER o_merid {
175             yyHour = $1;
176             yyMinutes = $3;
177             yySeconds = 0;
178             yyMeridian = $4;
179         }
180         | tUNUMBER ':' tUNUMBER tSNUMBER {
181             yyHour = $1;
182             yyMinutes = $3;
183             yyMeridian = MER24;
184             yyDSTmode = DSToff;
185             yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
186         }
187         | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
188             yyHour = $1;
189             yyMinutes = $3;
190             yySeconds = $5;
191             yyMeridian = $6;
192         }
193         | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
194             yyHour = $1;
195             yyMinutes = $3;
196             yySeconds = $5;
197             yyMeridian = MER24;
198             yyDSTmode = DSToff;
199             yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
200         }
201         ;
202
203 zone    : tZONE tDST {
204             yyTimezone = $1;
205             yyDSTmode = DSTon;
206         }
207         | tZONE {
208             yyTimezone = $1;
209             yyDSTmode = DSToff;
210         }
211         | tDAYZONE {
212             yyTimezone = $1;
213             yyDSTmode = DSTon;
214         }
215         ;
216
217 day     : tDAY {
218             yyDayOrdinal = 1;
219             yyDayNumber = $1;
220         }
221         | tDAY ',' {
222             yyDayOrdinal = 1;
223             yyDayNumber = $1;
224         }
225         | tUNUMBER tDAY {
226             yyDayOrdinal = $1;
227             yyDayNumber = $2;
228         }
229         ;
230
231 date    : tUNUMBER '/' tUNUMBER {
232             yyMonth = $1;
233             yyDay = $3;
234         }
235         | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
236             yyMonth = $1;
237             yyDay = $3;
238             yyYear = $5;
239         }
240         | tMONTH tUNUMBER {
241             yyMonth = $1;
242             yyDay = $2;
243         }
244         | tMONTH tUNUMBER ',' tUNUMBER {
245             yyMonth = $1;
246             yyDay = $2;
247             yyYear = $4;
248         }
249         | tUNUMBER tMONTH {
250             yyMonth = $2;
251             yyDay = $1;
252         }
253                   | tEPOCH {
254                                 yyMonth = 1;
255                                 yyDay = 1;
256                                 yyYear = EPOCH;
257                   }
258         | tUNUMBER tMONTH tUNUMBER {
259             yyMonth = $2;
260             yyDay = $1;
261             yyYear = $3;
262         }
263         ;
264
265 rel     : relunit tAGO {
266             yyRelSeconds = -yyRelSeconds;
267             yyRelMonth = -yyRelMonth;
268         }
269         | relunit
270         ;
271
272 relunit : tUNUMBER tMINUTE_UNIT {
273             yyRelSeconds += $1 * $2 * 60L;
274         }
275         | tSNUMBER tMINUTE_UNIT {
276             yyRelSeconds += $1 * $2 * 60L;
277         }
278         | tMINUTE_UNIT {
279             yyRelSeconds += $1 * 60L;
280         }
281         | tSNUMBER tSEC_UNIT {
282             yyRelSeconds += $1;
283         }
284         | tUNUMBER tSEC_UNIT {
285             yyRelSeconds += $1;
286         }
287         | tSEC_UNIT {
288             yyRelSeconds++;
289         }
290         | tSNUMBER tMONTH_UNIT {
291             yyRelMonth += $1 * $2;
292         }
293         | tUNUMBER tMONTH_UNIT {
294             yyRelMonth += $1 * $2;
295         }
296         | tMONTH_UNIT {
297             yyRelMonth += $1;
298         }
299         ;
300
301 number  : tUNUMBER
302     {
303         if (yyHaveTime && yyHaveDate && !yyHaveRel) {
304             yyYear = $1;
305         } else {
306             yyHaveTime++;
307             if ($1 < 100) {
308                 yyHour = 0;
309                 yyMinutes = $1;
310             } else {
311                 yyHour = $1 / 100;
312                 yyMinutes = $1 % 100;
313             }
314             yySeconds = 0;
315             yyMeridian = MER24;
316         }
317     }
318 ;
319
320 o_merid : /* NULL */ {
321             $$ = MER24;
322         }
323         | tMERIDIAN {
324             $$ = $1;
325         }
326         ;
327
328 %%
329
330 /*
331  * Month and day table.
332  */
333 static TABLE    MonthDayTable[] = {
334     { "january",        tMONTH,  1 },
335     { "february",       tMONTH,  2 },
336     { "march",          tMONTH,  3 },
337     { "april",          tMONTH,  4 },
338     { "may",            tMONTH,  5 },
339     { "june",           tMONTH,  6 },
340     { "july",           tMONTH,  7 },
341     { "august",         tMONTH,  8 },
342     { "september",      tMONTH,  9 },
343     { "sept",           tMONTH,  9 },
344     { "october",        tMONTH, 10 },
345     { "november",       tMONTH, 11 },
346     { "december",       tMONTH, 12 },
347     { "sunday",         tDAY, 0 },
348     { "monday",         tDAY, 1 },
349     { "tuesday",        tDAY, 2 },
350     { "tues",           tDAY, 2 },
351     { "wednesday",      tDAY, 3 },
352     { "wednes",         tDAY, 3 },
353     { "thursday",       tDAY, 4 },
354     { "thur",           tDAY, 4 },
355     { "thurs",          tDAY, 4 },
356     { "friday",         tDAY, 5 },
357     { "saturday",       tDAY, 6 },
358     { NULL }
359 };
360
361 /*
362  * Time units table.
363  */
364 static TABLE    UnitsTable[] = {
365     { "year",           tMONTH_UNIT,    12 },
366     { "month",          tMONTH_UNIT,    1 },
367     { "fortnight",      tMINUTE_UNIT,   14 * 24 * 60 },
368     { "week",           tMINUTE_UNIT,   7 * 24 * 60 },
369     { "day",            tMINUTE_UNIT,   1 * 24 * 60 },
370     { "hour",           tMINUTE_UNIT,   60 },
371     { "minute",         tMINUTE_UNIT,   1 },
372     { "min",            tMINUTE_UNIT,   1 },
373     { "second",         tSEC_UNIT,      1 },
374     { "sec",            tSEC_UNIT,      1 },
375     { NULL }
376 };
377
378 /*
379  * Assorted relative-time words.
380  */
381 static TABLE    OtherTable[] = {
382     { "tomorrow",       tMINUTE_UNIT,   1 * 24 * 60 },
383     { "yesterday",      tMINUTE_UNIT,   -1 * 24 * 60 },
384     { "today",          tMINUTE_UNIT,   0 },
385     { "now",            tMINUTE_UNIT,   0 },
386     { "last",           tUNUMBER,       -1 },
387     { "this",           tMINUTE_UNIT,   0 },
388     { "next",           tUNUMBER,       2 },
389 #if 0
390     { "first",          tUNUMBER,       1 },
391 /*  { "second",         tUNUMBER,       2 }, */
392     { "third",          tUNUMBER,       3 },
393     { "fourth",         tUNUMBER,       4 },
394     { "fifth",          tUNUMBER,       5 },
395     { "sixth",          tUNUMBER,       6 },
396     { "seventh",        tUNUMBER,       7 },
397     { "eighth",         tUNUMBER,       8 },
398     { "ninth",          tUNUMBER,       9 },
399     { "tenth",          tUNUMBER,       10 },
400     { "eleventh",       tUNUMBER,       11 },
401     { "twelfth",        tUNUMBER,       12 },
402 #endif
403     { "ago",            tAGO,   1 },
404     { "epoch",          tEPOCH,   0 },
405     { NULL }
406 };
407
408 /*
409  * The timezone table.  (Note: This table was modified to not use any floating
410  * point constants to work around an SGI compiler bug).
411  */
412 static TABLE    TimezoneTable[] = {
413     { "gmt",    tZONE,     HOUR( 0) },      /* Greenwich Mean */
414     { "ut",     tZONE,     HOUR( 0) },      /* Universal (Coordinated) */
415     { "utc",    tZONE,     HOUR( 0) },
416     { "wet",    tZONE,     HOUR( 0) } ,     /* Western European */
417     { "bst",    tDAYZONE,  HOUR( 0) },      /* British Summer */
418     { "wat",    tZONE,     HOUR( 1) },      /* West Africa */
419     { "at",     tZONE,     HOUR( 2) },      /* Azores */
420 #if     0
421     /* For completeness.  BST is also British Summer, and GST is
422      * also Guam Standard. */
423     { "bst",    tZONE,     HOUR( 3) },      /* Brazil Standard */
424     { "gst",    tZONE,     HOUR( 3) },      /* Greenland Standard */
425 #endif
426     { "nft",    tZONE,     HOUR( 7/2) },    /* Newfoundland */
427     { "nst",    tZONE,     HOUR( 7/2) },    /* Newfoundland Standard */
428     { "ndt",    tDAYZONE,  HOUR( 7/2) },    /* Newfoundland Daylight */
429     { "ast",    tZONE,     HOUR( 4) },      /* Atlantic Standard */
430     { "adt",    tDAYZONE,  HOUR( 4) },      /* Atlantic Daylight */
431     { "est",    tZONE,     HOUR( 5) },      /* Eastern Standard */
432     { "edt",    tDAYZONE,  HOUR( 5) },      /* Eastern Daylight */
433     { "cst",    tZONE,     HOUR( 6) },      /* Central Standard */
434     { "cdt",    tDAYZONE,  HOUR( 6) },      /* Central Daylight */
435     { "mst",    tZONE,     HOUR( 7) },      /* Mountain Standard */
436     { "mdt",    tDAYZONE,  HOUR( 7) },      /* Mountain Daylight */
437     { "pst",    tZONE,     HOUR( 8) },      /* Pacific Standard */
438     { "pdt",    tDAYZONE,  HOUR( 8) },      /* Pacific Daylight */
439     { "yst",    tZONE,     HOUR( 9) },      /* Yukon Standard */
440     { "ydt",    tDAYZONE,  HOUR( 9) },      /* Yukon Daylight */
441     { "hst",    tZONE,     HOUR(10) },      /* Hawaii Standard */
442     { "hdt",    tDAYZONE,  HOUR(10) },      /* Hawaii Daylight */
443     { "cat",    tZONE,     HOUR(10) },      /* Central Alaska */
444     { "ahst",   tZONE,     HOUR(10) },      /* Alaska-Hawaii Standard */
445     { "nt",     tZONE,     HOUR(11) },      /* Nome */
446     { "idlw",   tZONE,     HOUR(12) },      /* International Date Line West */
447     { "cet",    tZONE,    -HOUR( 1) },      /* Central European */
448     { "met",    tZONE,    -HOUR( 1) },      /* Middle European */
449     { "mewt",   tZONE,    -HOUR( 1) },      /* Middle European Winter */
450     { "mest",   tDAYZONE, -HOUR( 1) },      /* Middle European Summer */
451     { "swt",    tZONE,    -HOUR( 1) },      /* Swedish Winter */
452     { "sst",    tDAYZONE, -HOUR( 1) },      /* Swedish Summer */
453     { "fwt",    tZONE,    -HOUR( 1) },      /* French Winter */
454     { "fst",    tDAYZONE, -HOUR( 1) },      /* French Summer */
455     { "eet",    tZONE,    -HOUR( 2) },      /* Eastern Europe, USSR Zone 1 */
456     { "bt",     tZONE,    -HOUR( 3) },      /* Baghdad, USSR Zone 2 */
457     { "it",     tZONE,    -HOUR( 7/2) },    /* Iran */
458     { "zp4",    tZONE,    -HOUR( 4) },      /* USSR Zone 3 */
459     { "zp5",    tZONE,    -HOUR( 5) },      /* USSR Zone 4 */
460     { "ist",    tZONE,    -HOUR(11/2) },    /* Indian Standard */
461     { "zp6",    tZONE,    -HOUR( 6) },      /* USSR Zone 5 */
462 #if     0
463     /* For completeness.  NST is also Newfoundland Stanard, nad SST is
464      * also Swedish Summer. */
465     { "nst",    tZONE,    -HOUR(13/2) },    /* North Sumatra */
466     { "sst",    tZONE,    -HOUR( 7) },      /* South Sumatra, USSR Zone 6 */
467 #endif  /* 0 */
468     { "wast",   tZONE,    -HOUR( 7) },      /* West Australian Standard */
469     { "wadt",   tDAYZONE, -HOUR( 7) },      /* West Australian Daylight */
470     { "jt",     tZONE,    -HOUR(15/2) },    /* Java (3pm in Cronusland!) */
471     { "cct",    tZONE,    -HOUR( 8) },      /* China Coast, USSR Zone 7 */
472     { "jst",    tZONE,    -HOUR( 9) },      /* Japan Standard, USSR Zone 8 */
473     { "cast",   tZONE,    -HOUR(19/2) },    /* Central Australian Standard */
474     { "cadt",   tDAYZONE, -HOUR(19/2) },    /* Central Australian Daylight */
475     { "east",   tZONE,    -HOUR(10) },      /* Eastern Australian Standard */
476     { "eadt",   tDAYZONE, -HOUR(10) },      /* Eastern Australian Daylight */
477     { "gst",    tZONE,    -HOUR(10) },      /* Guam Standard, USSR Zone 9 */
478     { "nzt",    tZONE,    -HOUR(12) },      /* New Zealand */
479     { "nzst",   tZONE,    -HOUR(12) },      /* New Zealand Standard */
480     { "nzdt",   tDAYZONE, -HOUR(12) },      /* New Zealand Daylight */
481     { "idle",   tZONE,    -HOUR(12) },      /* International Date Line East */
482     /* ADDED BY Marco Nijdam */
483     { "dst",    tDST,     HOUR( 0) },       /* DST on (hour is ignored) */
484     /* End ADDED */
485     {  NULL  }
486 };
487
488 /*
489  * Military timezone table.
490  */
491 static TABLE    MilitaryTable[] = {
492     { "a",      tZONE,  HOUR(  1) },
493     { "b",      tZONE,  HOUR(  2) },
494     { "c",      tZONE,  HOUR(  3) },
495     { "d",      tZONE,  HOUR(  4) },
496     { "e",      tZONE,  HOUR(  5) },
497     { "f",      tZONE,  HOUR(  6) },
498     { "g",      tZONE,  HOUR(  7) },
499     { "h",      tZONE,  HOUR(  8) },
500     { "i",      tZONE,  HOUR(  9) },
501     { "k",      tZONE,  HOUR( 10) },
502     { "l",      tZONE,  HOUR( 11) },
503     { "m",      tZONE,  HOUR( 12) },
504     { "n",      tZONE,  HOUR(- 1) },
505     { "o",      tZONE,  HOUR(- 2) },
506     { "p",      tZONE,  HOUR(- 3) },
507     { "q",      tZONE,  HOUR(- 4) },
508     { "r",      tZONE,  HOUR(- 5) },
509     { "s",      tZONE,  HOUR(- 6) },
510     { "t",      tZONE,  HOUR(- 7) },
511     { "u",      tZONE,  HOUR(- 8) },
512     { "v",      tZONE,  HOUR(- 9) },
513     { "w",      tZONE,  HOUR(-10) },
514     { "x",      tZONE,  HOUR(-11) },
515     { "y",      tZONE,  HOUR(-12) },
516     { "z",      tZONE,  HOUR(  0) },
517     { NULL }
518 };
519
520
521 /*
522  * Dump error messages in the bit bucket.
523  */
524 static void
525 yyerror(s)
526     char  *s;
527 {
528 }
529
530
531 static time_t
532 ToSeconds(Hours, Minutes, Seconds, Meridian)
533     time_t      Hours;
534     time_t      Minutes;
535     time_t      Seconds;
536     MERIDIAN    Meridian;
537 {
538     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
539         return -1;
540     switch (Meridian) {
541     case MER24:
542         if (Hours < 0 || Hours > 23)
543             return -1;
544         return (Hours * 60L + Minutes) * 60L + Seconds;
545     case MERam:
546         if (Hours < 1 || Hours > 12)
547             return -1;
548         return ((Hours % 12) * 60L + Minutes) * 60L + Seconds;
549     case MERpm:
550         if (Hours < 1 || Hours > 12)
551             return -1;
552         return (((Hours % 12) + 12) * 60L + Minutes) * 60L + Seconds;
553     }
554     return -1;  /* Should never be reached */
555 }
556
557
558 static int
559 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode, TimePtr)
560     time_t      Month;
561     time_t      Day;
562     time_t      Year;
563     time_t      Hours;
564     time_t      Minutes;
565     time_t      Seconds;
566     MERIDIAN    Meridian;
567     DSTMODE     DSTmode;
568     time_t     *TimePtr;
569 {
570     static int  DaysInMonth[12] = {
571         31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
572     };
573     time_t tod;
574     time_t Julian;
575     int i;
576
577     DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
578                     ? 29 : 28;
579     if (Month < 1 || Month > 12
580      || Year < START_OF_TIME || Year > END_OF_TIME
581      || Day < 1 || Day > DaysInMonth[(int)--Month])
582         return -1;
583
584     for (Julian = Day - 1, i = 0; i < Month; i++)
585         Julian += DaysInMonth[i];
586     if (Year >= EPOCH) {
587         for (i = EPOCH; i < Year; i++)
588             Julian += 365 + (i % 4 == 0);
589     } else {
590         for (i = Year; i < EPOCH; i++)
591             Julian -= 365 + (i % 4 == 0);
592     }
593     Julian *= SECSPERDAY;
594     Julian += yyTimezone * 60L;
595     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
596         return -1;
597     Julian += tod;
598     if (DSTmode == DSTon
599      || (DSTmode == DSTmaybe && TclpGetDate(&Julian, 0)->tm_isdst))
600         Julian -= 60 * 60;
601     *TimePtr = Julian;
602     return 0;
603 }
604
605
606 static time_t
607 DSTcorrect(Start, Future)
608     time_t      Start;
609     time_t      Future;
610 {
611     time_t      StartDay;
612     time_t      FutureDay;
613
614     StartDay = (TclpGetDate(&Start, 0)->tm_hour + 1) % 24;
615     FutureDay = (TclpGetDate(&Future, 0)->tm_hour + 1) % 24;
616     return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
617 }
618
619
620 static time_t
621 RelativeDate(Start, DayOrdinal, DayNumber)
622     time_t      Start;
623     time_t      DayOrdinal;
624     time_t      DayNumber;
625 {
626     struct tm   *tm;
627     time_t      now;
628
629     now = Start;
630     tm = TclpGetDate(&now, 0);
631     now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
632     now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
633     return DSTcorrect(Start, now);
634 }
635
636
637 static int
638 RelativeMonth(Start, RelMonth, TimePtr)
639     time_t Start;
640     time_t RelMonth;
641     time_t *TimePtr;
642 {
643     struct tm *tm;
644     time_t Month;
645     time_t Year;
646     time_t Julian;
647     int result;
648
649     if (RelMonth == 0) {
650         *TimePtr = 0;
651         return 0;
652     }
653     tm = TclpGetDate(&Start, 0);
654     Month = 12 * (tm->tm_year + TM_YEAR_BASE) + tm->tm_mon + RelMonth;
655     Year = Month / 12;
656     Month = Month % 12 + 1;
657     result = Convert(Month, (time_t) tm->tm_mday, Year,
658             (time_t) tm->tm_hour, (time_t) tm->tm_min, (time_t) tm->tm_sec,
659             MER24, DSTmaybe, &Julian);
660     /*
661      * The following iteration takes into account the case were we jump
662      * into a "short month".  Far example, "one month from Jan 31" will
663      * fail because there is no Feb 31.  The code below will reduce the
664      * day and try converting the date until we succed or the date equals
665      * 28 (which always works unless the date is bad in another way).
666      */
667
668     while ((result != 0) && (tm->tm_mday > 28)) {
669         tm->tm_mday--;
670         result = Convert(Month, (time_t) tm->tm_mday, Year,
671                 (time_t) tm->tm_hour, (time_t) tm->tm_min, (time_t) tm->tm_sec,
672                 MER24, DSTmaybe, &Julian);
673     }
674     if (result != 0) {
675         return -1;
676     }
677     *TimePtr = DSTcorrect(Start, Julian);
678     return 0;
679 }
680
681
682 static int
683 LookupWord(buff)
684     char                *buff;
685 {
686     register char *p;
687     register char *q;
688     register TABLE *tp;
689     int i;
690     int abbrev;
691
692     /*
693      * Make it lowercase.
694      */
695     for (p = buff; *p; p++) {
696         if (isupper(UCHAR(*p))) {
697             *p = (char) tolower(UCHAR(*p));
698         }
699     }
700
701     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
702         yylval.Meridian = MERam;
703         return tMERIDIAN;
704     }
705     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
706         yylval.Meridian = MERpm;
707         return tMERIDIAN;
708     }
709
710     /*
711      * See if we have an abbreviation for a month.
712      */
713     if (strlen(buff) == 3) {
714         abbrev = 1;
715     } else if (strlen(buff) == 4 && buff[3] == '.') {
716         abbrev = 1;
717         buff[3] = '\0';
718     } else {
719         abbrev = 0;
720     }
721
722     for (tp = MonthDayTable; tp->name; tp++) {
723         if (abbrev) {
724             if (strncmp(buff, tp->name, 3) == 0) {
725                 yylval.Number = tp->value;
726                 return tp->type;
727             }
728         } else if (strcmp(buff, tp->name) == 0) {
729             yylval.Number = tp->value;
730             return tp->type;
731         }
732     }
733
734     for (tp = TimezoneTable; tp->name; tp++) {
735         if (strcmp(buff, tp->name) == 0) {
736             yylval.Number = tp->value;
737             return tp->type;
738         }
739     }
740
741     for (tp = UnitsTable; tp->name; tp++) {
742         if (strcmp(buff, tp->name) == 0) {
743             yylval.Number = tp->value;
744             return tp->type;
745         }
746     }
747
748     /*
749      * Strip off any plural and try the units table again.
750      */
751     i = strlen(buff) - 1;
752     if (buff[i] == 's') {
753         buff[i] = '\0';
754         for (tp = UnitsTable; tp->name; tp++) {
755             if (strcmp(buff, tp->name) == 0) {
756                 yylval.Number = tp->value;
757                 return tp->type;
758             }
759         }
760     }
761
762     for (tp = OtherTable; tp->name; tp++) {
763         if (strcmp(buff, tp->name) == 0) {
764             yylval.Number = tp->value;
765             return tp->type;
766         }
767     }
768
769     /*
770      * Military timezones.
771      */
772     if (buff[1] == '\0' && isalpha(UCHAR(*buff))) {
773         for (tp = MilitaryTable; tp->name; tp++) {
774             if (strcmp(buff, tp->name) == 0) {
775                 yylval.Number = tp->value;
776                 return tp->type;
777             }
778         }
779     }
780
781     /*
782      * Drop out any periods and try the timezone table again.
783      */
784     for (i = 0, p = q = buff; *q; q++)
785         if (*q != '.') {
786             *p++ = *q;
787         } else {
788             i++;
789         }
790     *p = '\0';
791     if (i) {
792         for (tp = TimezoneTable; tp->name; tp++) {
793             if (strcmp(buff, tp->name) == 0) {
794                 yylval.Number = tp->value;
795                 return tp->type;
796             }
797         }
798     }
799     
800     return tID;
801 }
802
803
804 static int
805 yylex()
806 {
807     register char       c;
808     register char       *p;
809     char                buff[20];
810     int                 Count;
811     int                 sign;
812
813     for ( ; ; ) {
814         while (isspace((unsigned char) (*yyInput))) {
815             yyInput++;
816         }
817
818         if (isdigit(c = *yyInput) || c == '-' || c == '+') {
819             if (c == '-' || c == '+') {
820                 sign = c == '-' ? -1 : 1;
821                 if (!isdigit(*++yyInput)) {
822                     /*
823                      * skip the '-' sign
824                      */
825                     continue;
826                 }
827             } else {
828                 sign = 0;
829             }
830             for (yylval.Number = 0; isdigit(c = *yyInput++); ) {
831                 yylval.Number = 10 * yylval.Number + c - '0';
832             }
833             yyInput--;
834             if (sign < 0) {
835                 yylval.Number = -yylval.Number;
836             }
837             return sign ? tSNUMBER : tUNUMBER;
838         }
839         if (isalpha(UCHAR(c))) {
840             for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) {
841                 if (p < &buff[sizeof buff - 1]) {
842                     *p++ = c;
843                 }
844             }
845             *p = '\0';
846             yyInput--;
847             return LookupWord(buff);
848         }
849         if (c != '(') {
850             return *yyInput++;
851         }
852         Count = 0;
853         do {
854             c = *yyInput++;
855             if (c == '\0') {
856                 return c;
857             } else if (c == '(') {
858                 Count++;
859             } else if (c == ')') {
860                 Count--;
861             }
862         } while (Count > 0);
863     }
864 }
865
866 /*
867  * Specify zone is of -50000 to force GMT.  (This allows BST to work).
868  */
869
870 int
871 TclGetDate(p, now, zone, timePtr)
872     char *p;
873     unsigned long now;
874     long zone;
875     unsigned long *timePtr;
876 {
877     struct tm *tm;
878     time_t Start;
879     time_t Time;
880     time_t tod;
881     int thisyear;
882
883     yyInput = p;
884     tm = TclpGetDate((time_t *) &now, 0);
885     thisyear = tm->tm_year + TM_YEAR_BASE;
886     yyYear = thisyear;
887     yyMonth = tm->tm_mon + 1;
888     yyDay = tm->tm_mday;
889     yyTimezone = zone;
890     if (zone == -50000) {
891         yyDSTmode = DSToff;  /* assume GMT */
892         yyTimezone = 0;
893     } else {
894         yyDSTmode = DSTmaybe;
895     }
896     yyHour = 0;
897     yyMinutes = 0;
898     yySeconds = 0;
899     yyMeridian = MER24;
900     yyRelSeconds = 0;
901     yyRelMonth = 0;
902     yyHaveDate = 0;
903     yyHaveDay = 0;
904     yyHaveRel = 0;
905     yyHaveTime = 0;
906     yyHaveZone = 0;
907
908     if (yyparse() || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 ||
909             yyHaveDay > 1) {
910         return -1;
911     }
912     
913     if (yyHaveDate || yyHaveTime || yyHaveDay) {
914         if (TclDateYear < 0) {
915             TclDateYear = -TclDateYear;
916         }
917         /*
918          * The following line handles years that are specified using
919          * only two digits.  The line of code below implements a policy
920          * defined by the X/Open workgroup on the millinium rollover.
921          * Note: some of those dates may not actually be valid on some
922          * platforms.  The POSIX standard startes that the dates 70-99
923          * shall refer to 1970-1999 and 00-38 shall refer to 2000-2038.
924          * This later definition should work on all platforms.
925          */
926
927         if (TclDateYear < 100) {
928             if (TclDateYear >= 69) {
929                 TclDateYear += 1900;
930             } else {
931                 TclDateYear += 2000;
932             }
933         }
934         if (Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
935                 yyMeridian, yyDSTmode, &Start) < 0) {
936             return -1;
937         }
938     } else {
939         Start = now;
940         if (!yyHaveRel) {
941             Start -= ((tm->tm_hour * 60L) + tm->tm_min * 60L) + tm->tm_sec;
942         }
943     }
944
945     Start += yyRelSeconds;
946     if (RelativeMonth(Start, yyRelMonth, &Time) < 0) {
947         return -1;
948     }
949     Start += Time;
950
951     if (yyHaveDay && !yyHaveDate) {
952         tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
953         Start += tod;
954     }
955
956     *timePtr = Start;
957     return 0;
958 }