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.
9 ** This grammar has 10 shift/reduce conflicts.
11 ** This code is in the public domain and has no copyright.
13 /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
14 /* SUPPRESS 288 on yyerrlab *//* Label unused */
17 #if defined (emacs) || defined (CONFIG_BROKETS)
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. */
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. */
49 #include <sys/types.h>
51 #ifdef TIME_WITH_SYS_TIME
55 #ifdef HAVE_SYS_TIME_H
63 #undef timezone /* needed for sgi */
66 #if defined (HAVE_SYS_TIMEB_H)
67 #include <sys/timeb.h>
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.
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 */
80 #endif /* defined (HAVE_SYS_TIMEB_H) */
82 #endif /* defined (vms) */
84 #if defined (STDC_HEADERS) || defined (USG)
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))
95 extern struct tm *gmtime ();
96 extern struct tm *localtime ();
98 #define yyparse getdate_yyparse
99 #define yylex getdate_yylex
100 #define yyerror getdate_yyerror
103 static int yyerror ();
106 #define HOUR(x) ((time_t)(x) * 60)
107 #define SECSPERDAY (24L * 60L * 60L)
109 #define MAX_BUFF_LEN 128 /* size of buffer to read the date into */
112 ** An entry in the lexical lookup table.
114 typedef struct _TABLE {
122 ** Daylight-savings mode: on, off, or not yet known.
124 typedef enum _DSTMODE {
125 DSTon, DSToff, DSTmaybe
129 ** Meridian: am, pm, or 24-hour style.
131 typedef enum _MERIDIAN {
137 ** Global variables. We could get rid of most of these by using a good
138 ** union as the yacc stack. (This routine was originally written before
139 ** yacc had the %union construct.) Maybe someday; right now we only use
140 ** the %union very rarely.
142 static char *yyInput;
143 static DSTMODE yyDSTmode;
144 static time_t yyDayOrdinal;
145 static time_t yyDayNumber;
146 static int yyHaveDate;
147 static int yyHaveDay;
148 static int yyHaveRel;
149 static int yyHaveTime;
150 static int yyHaveZone;
151 static time_t yyTimezone;
153 static time_t yyHour;
154 static time_t yyMinutes;
155 static time_t yyMonth;
156 static time_t yySeconds;
157 static time_t yyYear;
158 static MERIDIAN yyMeridian;
159 static time_t yyRelMonth;
160 static time_t yyRelSeconds;
166 enum _MERIDIAN Meridian;
169 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
170 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
172 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
173 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
174 %type <Meridian> tMERIDIAN o_merid
200 time : tUNUMBER tMERIDIAN {
206 | tUNUMBER ':' tUNUMBER o_merid {
212 | tUNUMBER ':' tUNUMBER tSNUMBER {
217 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
219 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
225 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
231 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
264 date : tUNUMBER '/' tUNUMBER {
268 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
273 | tUNUMBER tSNUMBER tSNUMBER {
274 /* ISO 8601 format. yyyy-mm-dd. */
279 | tUNUMBER tMONTH tSNUMBER {
280 /* e.g. 17-JUN-1992. */
289 | tMONTH tUNUMBER ',' tUNUMBER {
298 | tUNUMBER tMONTH tUNUMBER {
306 yyRelSeconds = -yyRelSeconds;
307 yyRelMonth = -yyRelMonth;
312 relunit : tUNUMBER tMINUTE_UNIT {
313 yyRelSeconds += $1 * $2 * 60L;
315 | tSNUMBER tMINUTE_UNIT {
316 yyRelSeconds += $1 * $2 * 60L;
319 yyRelSeconds += $1 * 60L;
321 | tSNUMBER tSEC_UNIT {
324 | tUNUMBER tSEC_UNIT {
330 | tSNUMBER tMONTH_UNIT {
331 yyRelMonth += $1 * $2;
333 | tUNUMBER tMONTH_UNIT {
334 yyRelMonth += $1 * $2;
342 if (yyHaveTime && yyHaveDate && !yyHaveRel)
348 yyMonth= ($1/100)%100;
359 yyMinutes = $1 % 100;
368 o_merid : /* NULL */ {
378 /* Month and day table. */
379 static TABLE const MonthDayTable[] = {
380 { "january", tMONTH, 1 },
381 { "february", tMONTH, 2 },
382 { "march", tMONTH, 3 },
383 { "april", tMONTH, 4 },
384 { "may", tMONTH, 5 },
385 { "june", tMONTH, 6 },
386 { "july", tMONTH, 7 },
387 { "august", tMONTH, 8 },
388 { "september", tMONTH, 9 },
389 { "sept", tMONTH, 9 },
390 { "october", tMONTH, 10 },
391 { "november", tMONTH, 11 },
392 { "december", tMONTH, 12 },
393 { "sunday", tDAY, 0 },
394 { "monday", tDAY, 1 },
395 { "tuesday", tDAY, 2 },
397 { "wednesday", tDAY, 3 },
398 { "wednes", tDAY, 3 },
399 { "thursday", tDAY, 4 },
401 { "thurs", tDAY, 4 },
402 { "friday", tDAY, 5 },
403 { "saturday", tDAY, 6 },
407 /* Time units table. */
408 static TABLE const UnitsTable[] = {
409 { "year", tMONTH_UNIT, 12 },
410 { "month", tMONTH_UNIT, 1 },
411 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
412 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
413 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
414 { "hour", tMINUTE_UNIT, 60 },
415 { "minute", tMINUTE_UNIT, 1 },
416 { "min", tMINUTE_UNIT, 1 },
417 { "second", tSEC_UNIT, 1 },
418 { "sec", tSEC_UNIT, 1 },
422 /* Assorted relative-time words. */
423 static TABLE const OtherTable[] = {
424 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
425 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
426 { "today", tMINUTE_UNIT, 0 },
427 { "now", tMINUTE_UNIT, 0 },
428 { "last", tUNUMBER, -1 },
429 { "this", tMINUTE_UNIT, 0 },
430 { "next", tUNUMBER, 2 },
431 { "first", tUNUMBER, 1 },
432 /* { "second", tUNUMBER, 2 }, */
433 { "third", tUNUMBER, 3 },
434 { "fourth", tUNUMBER, 4 },
435 { "fifth", tUNUMBER, 5 },
436 { "sixth", tUNUMBER, 6 },
437 { "seventh", tUNUMBER, 7 },
438 { "eighth", tUNUMBER, 8 },
439 { "ninth", tUNUMBER, 9 },
440 { "tenth", tUNUMBER, 10 },
441 { "eleventh", tUNUMBER, 11 },
442 { "twelfth", tUNUMBER, 12 },
447 /* The timezone table. */
448 /* Some of these are commented out because a time_t can't store a float. */
449 static TABLE const TimezoneTable[] = {
450 { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
451 { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
452 { "utc", tZONE, HOUR ( 0) },
453 { "wet", tZONE, HOUR ( 0) }, /* Western European */
454 { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
455 { "wat", tZONE, HOUR ( 1) }, /* West Africa */
456 { "at", tZONE, HOUR ( 2) }, /* Azores */
458 /* For completeness. BST is also British Summer, and GST is
459 * also Guam Standard. */
460 { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */
461 { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */
464 { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */
465 { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */
466 { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */
468 { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
469 { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
470 { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
471 { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
472 { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
473 { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
474 { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
475 { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
476 { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
477 { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
478 { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */
479 { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */
480 { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
481 { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */
482 { "cat", tZONE, HOUR (10) }, /* Central Alaska */
483 { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */
484 { "nt", tZONE, HOUR (11) }, /* Nome */
485 { "idlw", tZONE, HOUR (12) }, /* International Date Line West */
486 { "cet", tZONE, -HOUR (1) }, /* Central European */
487 { "met", tZONE, -HOUR (1) }, /* Middle European */
488 { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */
489 { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
490 { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
491 { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */
492 { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */
493 { "fwt", tZONE, -HOUR (1) }, /* French Winter */
494 { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */
495 { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
496 { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */
498 { "it", tZONE, -HOUR (3.5) },/* Iran */
500 { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */
501 { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */
503 { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */
505 { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */
507 /* For completeness. NST is also Newfoundland Standard, and SST is
508 * also Swedish Summer. */
509 { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */
510 { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */
512 { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */
513 { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */
515 { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */
517 { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */
518 { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
520 { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */
521 { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */
523 { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */
524 { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */
525 { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */
526 { "nzt", tZONE, -HOUR (12) }, /* New Zealand */
527 { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
528 { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
529 { "idle", tZONE, -HOUR (12) }, /* International Date Line East */
533 /* Military timezone table. */
534 static TABLE const MilitaryTable[] = {
535 { "a", tZONE, HOUR ( 1) },
536 { "b", tZONE, HOUR ( 2) },
537 { "c", tZONE, HOUR ( 3) },
538 { "d", tZONE, HOUR ( 4) },
539 { "e", tZONE, HOUR ( 5) },
540 { "f", tZONE, HOUR ( 6) },
541 { "g", tZONE, HOUR ( 7) },
542 { "h", tZONE, HOUR ( 8) },
543 { "i", tZONE, HOUR ( 9) },
544 { "k", tZONE, HOUR ( 10) },
545 { "l", tZONE, HOUR ( 11) },
546 { "m", tZONE, HOUR ( 12) },
547 { "n", tZONE, HOUR (- 1) },
548 { "o", tZONE, HOUR (- 2) },
549 { "p", tZONE, HOUR (- 3) },
550 { "q", tZONE, HOUR (- 4) },
551 { "r", tZONE, HOUR (- 5) },
552 { "s", tZONE, HOUR (- 6) },
553 { "t", tZONE, HOUR (- 7) },
554 { "u", tZONE, HOUR (- 8) },
555 { "v", tZONE, HOUR (- 9) },
556 { "w", tZONE, HOUR (-10) },
557 { "x", tZONE, HOUR (-11) },
558 { "y", tZONE, HOUR (-12) },
559 { "z", tZONE, HOUR ( 0) },
576 ToSeconds (Hours, Minutes, Seconds, Meridian)
582 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
586 if (Hours < 0 || Hours > 23)
588 return (Hours * 60L + Minutes) * 60L + Seconds;
590 if (Hours < 1 || Hours > 12)
592 return (Hours * 60L + Minutes) * 60L + Seconds;
594 if (Hours < 1 || Hours > 12)
596 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
605 Convert (Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
615 static int DaysInMonth[12] = {
616 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
626 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
628 if (Year < EPOCH || Year > 1999
629 || Month < 1 || Month > 12
630 /* Lint fluff: "conversion from long may lose accuracy" */
631 || Day < 1 || Day > DaysInMonth[(int)--Month])
634 for (Julian = Day - 1, i = 0; i < Month; i++)
635 Julian += DaysInMonth[i];
636 for (i = EPOCH; i < Year; i++)
637 Julian += 365 + (i % 4 == 0);
638 Julian *= SECSPERDAY;
639 Julian += yyTimezone * 60L;
640 if ((tod = ToSeconds (Hours, Minutes, Seconds, Meridian)) < 0)
644 || (DSTmode == DSTmaybe && localtime (&Julian)->tm_isdst))
651 DSTcorrect (Start, Future)
658 StartDay = (localtime (&Start)->tm_hour + 1) % 24;
659 FutureDay = (localtime (&Future)->tm_hour + 1) % 24;
660 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
665 RelativeDate (Start, DayOrdinal, DayNumber)
674 tm = localtime (&now);
675 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
676 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
677 return DSTcorrect (Start, now);
682 RelativeMonth (Start, RelMonth)
692 tm = localtime (&Start);
693 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
695 Month = Month % 12 + 1;
696 return DSTcorrect (Start,
697 Convert (Month, (time_t)tm->tm_mday, Year,
698 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
709 register const TABLE *tp;
713 /* Make it lowercase. */
714 for (p = buff; *p; p++)
718 if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) {
719 yylval.Meridian = MERam;
722 if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) {
723 yylval.Meridian = MERpm;
727 /* See if we have an abbreviation for a month. */
728 if (strlen (buff) == 3)
730 else if (strlen (buff) == 4 && buff[3] == '.') {
737 for (tp = MonthDayTable; tp->name; tp++) {
739 if (strncmp (buff, tp->name, 3) == 0) {
740 yylval.Number = tp->value;
744 else if (strcmp (buff, tp->name) == 0) {
745 yylval.Number = tp->value;
750 for (tp = TimezoneTable; tp->name; tp++)
751 if (strcmp (buff, tp->name) == 0) {
752 yylval.Number = tp->value;
756 if (strcmp (buff, "dst") == 0)
759 for (tp = UnitsTable; tp->name; tp++)
760 if (strcmp (buff, tp->name) == 0) {
761 yylval.Number = tp->value;
765 /* Strip off any plural and try the units table again. */
766 i = strlen (buff) - 1;
767 if (buff[i] == 's') {
769 for (tp = UnitsTable; tp->name; tp++)
770 if (strcmp (buff, tp->name) == 0) {
771 yylval.Number = tp->value;
774 buff[i] = 's'; /* Put back for "this" in OtherTable. */
777 for (tp = OtherTable; tp->name; tp++)
778 if (strcmp (buff, tp->name) == 0) {
779 yylval.Number = tp->value;
783 /* Military timezones. */
784 if (buff[1] == '\0' && isalpha (*buff)) {
785 for (tp = MilitaryTable; tp->name; tp++)
786 if (strcmp (buff, tp->name) == 0) {
787 yylval.Number = tp->value;
792 /* Drop out any periods and try the timezone table again. */
793 for (i = 0, p = q = buff; *q; q++)
800 for (tp = TimezoneTable; tp->name; tp++)
801 if (strcmp (buff, tp->name) == 0) {
802 yylval.Number = tp->value;
820 while (isspace (*yyInput))
823 if (isdigit (c = *yyInput) || c == '-' || c == '+') {
824 if (c == '-' || c == '+') {
825 sign = c == '-' ? -1 : 1;
826 if (!isdigit (*++yyInput))
827 /* skip the '-' sign */
832 for (yylval.Number = 0; isdigit (c = *yyInput++); )
833 yylval.Number = 10 * yylval.Number + c - '0';
836 yylval.Number = -yylval.Number;
837 return sign ? tSNUMBER : tUNUMBER;
840 for (p = buff; isalpha (c = *yyInput++) || c == '.'; )
841 if (p < &buff[sizeof buff - 1])
845 return LookupWord (buff);
862 #define TM_YEAR_ORIGIN 1900
864 /* Yield A - B, measured in seconds. */
869 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
870 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
872 /* difference in day of year */
873 a->tm_yday - b->tm_yday
874 /* + intervening leap days */
875 + ((ay >> 2) - (by >> 2))
877 + ((ay/100 >> 2) - (by/100 >> 2))
878 /* + difference in years * 365 */
879 + (long)(ay-by) * 365
881 return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
882 + (a->tm_min - b->tm_min))
883 + (a->tm_sec - b->tm_sec));
899 (void)time (&ftz.time);
901 if (! (tm = gmtime (&ftz.time)))
903 gmt = *tm; /* Make a copy, in case localtime modifies *tm. */
905 if (! (tm = localtime (&ftz.time)))
908 ftz.timezone = difftm (&gmt, tm) / 60;
913 tm = localtime (&now->time);
914 yyYear = tm->tm_year;
915 yyMonth = tm->tm_mon + 1;
917 yyTimezone = now->timezone;
918 yyDSTmode = DSTmaybe;
932 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
935 if (yyHaveDate || yyHaveTime || yyHaveDay) {
936 Start = Convert (yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
937 yyMeridian, yyDSTmode);
944 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
947 Start += yyRelSeconds;
948 Start += RelativeMonth (Start, yyRelMonth);
950 if (yyHaveDay && !yyHaveDate) {
951 tod = RelativeDate (Start, yyDayOrdinal, yyDayNumber);
955 /* Have to do *something* with a legitimate -1 so it's distinguishable
956 * from the error return value. (Alternately could set errno on error.) */
957 return Start == -1 ? 0 : Start;
972 (void)printf ("Enter date, or blank line to exit.\n\t> ");
973 (void)fflush (stdout);
974 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) {
975 d = get_date (buff, (struct timeb *)NULL);
977 (void)printf ("Bad format - couldn't convert.\n");
979 (void)printf ("%s", ctime (&d));
980 (void)printf ("\t> ");
981 (void)fflush (stdout);
986 #endif /* defined (TEST) */