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. */
34 /* The following block of alloca-related preprocessor directives is here
35 solely to allow compilation by non GNU-C compilers of the C parser
36 produced from this file by old versions of bison. Newer versions of
37 bison include a block similar to this one in bison.simple. */
41 #define alloca __builtin_alloca
46 #ifdef _AIX /* for Bison */
57 /* The code at the top of get_date which figures out the offset of the
58 current time zone checks various CPP symbols to see if special
59 tricks are need, but defaults to using the gettimeofday system call.
60 Include <sys/time.h> if that will be used. */
69 #include <sys/types.h>
71 #ifdef TIME_WITH_SYS_TIME
75 #ifdef HAVE_SYS_TIME_H
83 #undef timezone /* needed for sgi */
86 #if defined(HAVE_SYS_TIMEB_H)
87 #include <sys/timeb.h>
90 ** We use the obsolete `struct timeb' as part of our interface!
91 ** Since the system doesn't have it, we define it here;
92 ** our callers must do likewise.
95 time_t time; /* Seconds since the epoch */
96 unsigned short millitm; /* Field not used */
97 short timezone; /* Minutes west of GMT */
98 short dstflag; /* Field not used */
100 #endif /* defined(HAVE_SYS_TIMEB_H) */
102 #endif /* defined(vms) */
104 #if defined (STDC_HEADERS) || defined (USG)
108 /* Some old versions of bison generate parsers that use bcopy.
109 That loses on systems that don't provide the function, so we have
110 to redefine it here. */
111 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
112 #define bcopy(from, to, len) memcpy ((to), (from), (len))
115 extern struct tm *gmtime();
116 extern struct tm *localtime();
118 #define yyparse getdate_yyparse
119 #define yylex getdate_yylex
120 #define yyerror getdate_yyerror
123 static int yyerror ();
125 #if !defined(lint) && !defined(SABER)
127 "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
128 #endif /* !defined(lint) && !defined(SABER) */
132 #define HOUR(x) ((time_t)(x) * 60)
133 #define SECSPERDAY (24L * 60L * 60L)
137 ** An entry in the lexical lookup table.
139 typedef struct _TABLE {
147 ** Daylight-savings mode: on, off, or not yet known.
149 typedef enum _DSTMODE {
150 DSTon, DSToff, DSTmaybe
154 ** Meridian: am, pm, or 24-hour style.
156 typedef enum _MERIDIAN {
162 ** Global variables. We could get rid of most of these by using a good
163 ** union as the yacc stack. (This routine was originally written before
164 ** yacc had the %union construct.) Maybe someday; right now we only use
165 ** the %union very rarely.
167 static char *yyInput;
168 static DSTMODE yyDSTmode;
169 static time_t yyDayOrdinal;
170 static time_t yyDayNumber;
171 static int yyHaveDate;
172 static int yyHaveDay;
173 static int yyHaveRel;
174 static int yyHaveTime;
175 static int yyHaveZone;
176 static time_t yyTimezone;
178 static time_t yyHour;
179 static time_t yyMinutes;
180 static time_t yyMonth;
181 static time_t yySeconds;
182 static time_t yyYear;
183 static MERIDIAN yyMeridian;
184 static time_t yyRelMonth;
185 static time_t yyRelSeconds;
191 enum _MERIDIAN Meridian;
194 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
195 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
197 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
198 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
199 %type <Meridian> tMERIDIAN o_merid
225 time : tUNUMBER tMERIDIAN {
231 | tUNUMBER ':' tUNUMBER o_merid {
237 | tUNUMBER ':' tUNUMBER tSNUMBER {
242 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
244 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
250 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
256 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
289 date : tUNUMBER '/' tUNUMBER {
293 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
298 | tUNUMBER tSNUMBER tSNUMBER {
299 /* ISO 8601 format. yyyy-mm-dd. */
304 | tUNUMBER tMONTH tSNUMBER {
305 /* e.g. 17-JUN-1992. */
314 | tMONTH tUNUMBER ',' tUNUMBER {
323 | tUNUMBER tMONTH tUNUMBER {
331 yyRelSeconds = -yyRelSeconds;
332 yyRelMonth = -yyRelMonth;
337 relunit : tUNUMBER tMINUTE_UNIT {
338 yyRelSeconds += $1 * $2 * 60L;
340 | tSNUMBER tMINUTE_UNIT {
341 yyRelSeconds += $1 * $2 * 60L;
344 yyRelSeconds += $1 * 60L;
346 | tSNUMBER tSEC_UNIT {
349 | tUNUMBER tSEC_UNIT {
355 | tSNUMBER tMONTH_UNIT {
356 yyRelMonth += $1 * $2;
358 | tUNUMBER tMONTH_UNIT {
359 yyRelMonth += $1 * $2;
367 if (yyHaveTime && yyHaveDate && !yyHaveRel)
373 yyMonth= ($1/100)%100;
384 yyMinutes = $1 % 100;
393 o_merid : /* NULL */ {
403 /* Month and day table. */
404 static TABLE const MonthDayTable[] = {
405 { "january", tMONTH, 1 },
406 { "february", tMONTH, 2 },
407 { "march", tMONTH, 3 },
408 { "april", tMONTH, 4 },
409 { "may", tMONTH, 5 },
410 { "june", tMONTH, 6 },
411 { "july", tMONTH, 7 },
412 { "august", tMONTH, 8 },
413 { "september", tMONTH, 9 },
414 { "sept", tMONTH, 9 },
415 { "october", tMONTH, 10 },
416 { "november", tMONTH, 11 },
417 { "december", tMONTH, 12 },
418 { "sunday", tDAY, 0 },
419 { "monday", tDAY, 1 },
420 { "tuesday", tDAY, 2 },
422 { "wednesday", tDAY, 3 },
423 { "wednes", tDAY, 3 },
424 { "thursday", tDAY, 4 },
426 { "thurs", tDAY, 4 },
427 { "friday", tDAY, 5 },
428 { "saturday", tDAY, 6 },
432 /* Time units table. */
433 static TABLE const UnitsTable[] = {
434 { "year", tMONTH_UNIT, 12 },
435 { "month", tMONTH_UNIT, 1 },
436 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
437 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
438 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
439 { "hour", tMINUTE_UNIT, 60 },
440 { "minute", tMINUTE_UNIT, 1 },
441 { "min", tMINUTE_UNIT, 1 },
442 { "second", tSEC_UNIT, 1 },
443 { "sec", tSEC_UNIT, 1 },
447 /* Assorted relative-time words. */
448 static TABLE const OtherTable[] = {
449 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
450 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
451 { "today", tMINUTE_UNIT, 0 },
452 { "now", tMINUTE_UNIT, 0 },
453 { "last", tUNUMBER, -1 },
454 { "this", tMINUTE_UNIT, 0 },
455 { "next", tUNUMBER, 2 },
456 { "first", tUNUMBER, 1 },
457 /* { "second", tUNUMBER, 2 }, */
458 { "third", tUNUMBER, 3 },
459 { "fourth", tUNUMBER, 4 },
460 { "fifth", tUNUMBER, 5 },
461 { "sixth", tUNUMBER, 6 },
462 { "seventh", tUNUMBER, 7 },
463 { "eighth", tUNUMBER, 8 },
464 { "ninth", tUNUMBER, 9 },
465 { "tenth", tUNUMBER, 10 },
466 { "eleventh", tUNUMBER, 11 },
467 { "twelfth", tUNUMBER, 12 },
472 /* The timezone table. */
473 /* Some of these are commented out because a time_t can't store a float. */
474 static TABLE const TimezoneTable[] = {
475 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
476 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
477 { "utc", tZONE, HOUR( 0) },
478 { "wet", tZONE, HOUR( 0) }, /* Western European */
479 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
480 { "wat", tZONE, HOUR( 1) }, /* West Africa */
481 { "at", tZONE, HOUR( 2) }, /* Azores */
483 /* For completeness. BST is also British Summer, and GST is
484 * also Guam Standard. */
485 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
486 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
489 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
490 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
491 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
493 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
494 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
495 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
496 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
497 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
498 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
499 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
500 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
501 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
502 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
503 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
504 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
505 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
506 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
507 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
508 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
509 { "nt", tZONE, HOUR(11) }, /* Nome */
510 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
511 { "cet", tZONE, -HOUR(1) }, /* Central European */
512 { "met", tZONE, -HOUR(1) }, /* Middle European */
513 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
514 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
515 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
516 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
517 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
518 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
519 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
520 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
522 { "it", tZONE, -HOUR(3.5) },/* Iran */
524 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
525 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
527 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
529 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
531 /* For completeness. NST is also Newfoundland Stanard, and SST is
532 * also Swedish Summer. */
533 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
534 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
536 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
537 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
539 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
541 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
542 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
544 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
545 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
547 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
548 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
549 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
550 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
551 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
552 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
553 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
557 /* Military timezone table. */
558 static TABLE const MilitaryTable[] = {
559 { "a", tZONE, HOUR( 1) },
560 { "b", tZONE, HOUR( 2) },
561 { "c", tZONE, HOUR( 3) },
562 { "d", tZONE, HOUR( 4) },
563 { "e", tZONE, HOUR( 5) },
564 { "f", tZONE, HOUR( 6) },
565 { "g", tZONE, HOUR( 7) },
566 { "h", tZONE, HOUR( 8) },
567 { "i", tZONE, HOUR( 9) },
568 { "k", tZONE, HOUR( 10) },
569 { "l", tZONE, HOUR( 11) },
570 { "m", tZONE, HOUR( 12) },
571 { "n", tZONE, HOUR(- 1) },
572 { "o", tZONE, HOUR(- 2) },
573 { "p", tZONE, HOUR(- 3) },
574 { "q", tZONE, HOUR(- 4) },
575 { "r", tZONE, HOUR(- 5) },
576 { "s", tZONE, HOUR(- 6) },
577 { "t", tZONE, HOUR(- 7) },
578 { "u", tZONE, HOUR(- 8) },
579 { "v", tZONE, HOUR(- 9) },
580 { "w", tZONE, HOUR(-10) },
581 { "x", tZONE, HOUR(-11) },
582 { "y", tZONE, HOUR(-12) },
583 { "z", tZONE, HOUR( 0) },
600 ToSeconds(Hours, Minutes, Seconds, Meridian)
606 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
610 if (Hours < 0 || Hours > 23)
612 return (Hours * 60L + Minutes) * 60L + Seconds;
614 if (Hours < 1 || Hours > 12)
616 return (Hours * 60L + Minutes) * 60L + Seconds;
618 if (Hours < 1 || Hours > 12)
620 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
629 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
639 static int DaysInMonth[12] = {
640 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
650 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
652 if (Year < EPOCH || Year > 1999
653 || Month < 1 || Month > 12
654 /* Lint fluff: "conversion from long may lose accuracy" */
655 || Day < 1 || Day > DaysInMonth[(int)--Month])
658 for (Julian = Day - 1, i = 0; i < Month; i++)
659 Julian += DaysInMonth[i];
660 for (i = EPOCH; i < Year; i++)
661 Julian += 365 + (i % 4 == 0);
662 Julian *= SECSPERDAY;
663 Julian += yyTimezone * 60L;
664 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
668 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
675 DSTcorrect(Start, Future)
682 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
683 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
684 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
689 RelativeDate(Start, DayOrdinal, DayNumber)
698 tm = localtime(&now);
699 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
700 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
701 return DSTcorrect(Start, now);
706 RelativeMonth(Start, RelMonth)
716 tm = localtime(&Start);
717 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
719 Month = Month % 12 + 1;
720 return DSTcorrect(Start,
721 Convert(Month, (time_t)tm->tm_mday, Year,
722 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
733 register const TABLE *tp;
737 /* Make it lowercase. */
738 for (p = buff; *p; p++)
742 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
743 yylval.Meridian = MERam;
746 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
747 yylval.Meridian = MERpm;
751 /* See if we have an abbreviation for a month. */
752 if (strlen(buff) == 3)
754 else if (strlen(buff) == 4 && buff[3] == '.') {
761 for (tp = MonthDayTable; tp->name; tp++) {
763 if (strncmp(buff, tp->name, 3) == 0) {
764 yylval.Number = tp->value;
768 else if (strcmp(buff, tp->name) == 0) {
769 yylval.Number = tp->value;
774 for (tp = TimezoneTable; tp->name; tp++)
775 if (strcmp(buff, tp->name) == 0) {
776 yylval.Number = tp->value;
780 if (strcmp(buff, "dst") == 0)
783 for (tp = UnitsTable; tp->name; tp++)
784 if (strcmp(buff, tp->name) == 0) {
785 yylval.Number = tp->value;
789 /* Strip off any plural and try the units table again. */
790 i = strlen(buff) - 1;
791 if (buff[i] == 's') {
793 for (tp = UnitsTable; tp->name; tp++)
794 if (strcmp(buff, tp->name) == 0) {
795 yylval.Number = tp->value;
798 buff[i] = 's'; /* Put back for "this" in OtherTable. */
801 for (tp = OtherTable; tp->name; tp++)
802 if (strcmp(buff, tp->name) == 0) {
803 yylval.Number = tp->value;
807 /* Military timezones. */
808 if (buff[1] == '\0' && isalpha(*buff)) {
809 for (tp = MilitaryTable; tp->name; tp++)
810 if (strcmp(buff, tp->name) == 0) {
811 yylval.Number = tp->value;
816 /* Drop out any periods and try the timezone table again. */
817 for (i = 0, p = q = buff; *q; q++)
824 for (tp = TimezoneTable; tp->name; tp++)
825 if (strcmp(buff, tp->name) == 0) {
826 yylval.Number = tp->value;
844 while (isspace(*yyInput))
847 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
848 if (c == '-' || c == '+') {
849 sign = c == '-' ? -1 : 1;
850 if (!isdigit(*++yyInput))
851 /* skip the '-' sign */
856 for (yylval.Number = 0; isdigit(c = *yyInput++); )
857 yylval.Number = 10 * yylval.Number + c - '0';
860 yylval.Number = -yylval.Number;
861 return sign ? tSNUMBER : tUNUMBER;
864 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
865 if (p < &buff[sizeof buff - 1])
869 return LookupWord(buff);
904 (void)time(&ftz.time);
906 /* Compute local timezone. Do *not* take daylight savings
907 into account here. */
908 tmp = localtime (&epoch);
909 tz = tmp->tm_hour * 60 + tmp->tm_min; /* Minutes east of UTC. */
912 tz = 24 * 60 - tz; /* Minutes west of UTC. */
913 if (tmp->tm_year == 70)
914 tz -= 24 * 60; /* Account for date line. */
919 tm = localtime(&now->time);
920 yyYear = tm->tm_year;
921 yyMonth = tm->tm_mon + 1;
923 yyTimezone = now->timezone;
924 yyDSTmode = DSTmaybe;
938 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
941 if (yyHaveDate || yyHaveTime || yyHaveDay) {
942 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
943 yyMeridian, yyDSTmode);
950 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
953 Start += yyRelSeconds;
954 Start += RelativeMonth(Start, yyRelMonth);
956 if (yyHaveDay && !yyHaveDate) {
957 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
961 /* Have to do *something* with a legitimate -1 so it's distinguishable
962 * from the error return value. (Alternately could set errno on error.) */
963 return Start == -1 ? 0 : Start;
977 (void)printf("Enter date, or blank line to exit.\n\t> ");
978 (void)fflush(stdout);
979 while (gets(buff) && buff[0]) {
980 d = get_date(buff, (struct timeb *)NULL);
982 (void)printf("Bad format - couldn't convert.\n");
984 (void)printf("%s", ctime(&d));
985 (void)printf("\t> ");
986 (void)fflush(stdout);
991 #endif /* defined(TEST) */