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)
111 ** An entry in the lexical lookup table.
113 typedef struct _TABLE {
121 ** Daylight-savings mode: on, off, or not yet known.
123 typedef enum _DSTMODE {
124 DSTon, DSToff, DSTmaybe
128 ** Meridian: am, pm, or 24-hour style.
130 typedef enum _MERIDIAN {
136 ** Global variables. We could get rid of most of these by using a good
137 ** union as the yacc stack. (This routine was originally written before
138 ** yacc had the %union construct.) Maybe someday; right now we only use
139 ** the %union very rarely.
141 static char *yyInput;
142 static DSTMODE yyDSTmode;
143 static time_t yyDayOrdinal;
144 static time_t yyDayNumber;
145 static int yyHaveDate;
146 static int yyHaveDay;
147 static int yyHaveRel;
148 static int yyHaveTime;
149 static int yyHaveZone;
150 static time_t yyTimezone;
152 static time_t yyHour;
153 static time_t yyMinutes;
154 static time_t yyMonth;
155 static time_t yySeconds;
156 static time_t yyYear;
157 static MERIDIAN yyMeridian;
158 static time_t yyRelMonth;
159 static time_t yyRelSeconds;
165 enum _MERIDIAN Meridian;
168 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
169 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
171 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
172 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
173 %type <Meridian> tMERIDIAN o_merid
199 time : tUNUMBER tMERIDIAN {
205 | tUNUMBER ':' tUNUMBER o_merid {
211 | tUNUMBER ':' tUNUMBER tSNUMBER {
216 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
218 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
224 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
230 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
263 date : tUNUMBER '/' tUNUMBER {
267 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
272 | tUNUMBER tSNUMBER tSNUMBER {
273 /* ISO 8601 format. yyyy-mm-dd. */
278 | tUNUMBER tMONTH tSNUMBER {
279 /* e.g. 17-JUN-1992. */
288 | tMONTH tUNUMBER ',' tUNUMBER {
297 | tUNUMBER tMONTH tUNUMBER {
305 yyRelSeconds = -yyRelSeconds;
306 yyRelMonth = -yyRelMonth;
311 relunit : tUNUMBER tMINUTE_UNIT {
312 yyRelSeconds += $1 * $2 * 60L;
314 | tSNUMBER tMINUTE_UNIT {
315 yyRelSeconds += $1 * $2 * 60L;
318 yyRelSeconds += $1 * 60L;
320 | tSNUMBER tSEC_UNIT {
323 | tUNUMBER tSEC_UNIT {
329 | tSNUMBER tMONTH_UNIT {
330 yyRelMonth += $1 * $2;
332 | tUNUMBER tMONTH_UNIT {
333 yyRelMonth += $1 * $2;
341 if (yyHaveTime && yyHaveDate && !yyHaveRel)
347 yyMonth= ($1/100)%100;
358 yyMinutes = $1 % 100;
367 o_merid : /* NULL */ {
377 /* Month and day table. */
378 static TABLE const MonthDayTable[] = {
379 { "january", tMONTH, 1 },
380 { "february", tMONTH, 2 },
381 { "march", tMONTH, 3 },
382 { "april", tMONTH, 4 },
383 { "may", tMONTH, 5 },
384 { "june", tMONTH, 6 },
385 { "july", tMONTH, 7 },
386 { "august", tMONTH, 8 },
387 { "september", tMONTH, 9 },
388 { "sept", tMONTH, 9 },
389 { "october", tMONTH, 10 },
390 { "november", tMONTH, 11 },
391 { "december", tMONTH, 12 },
392 { "sunday", tDAY, 0 },
393 { "monday", tDAY, 1 },
394 { "tuesday", tDAY, 2 },
396 { "wednesday", tDAY, 3 },
397 { "wednes", tDAY, 3 },
398 { "thursday", tDAY, 4 },
400 { "thurs", tDAY, 4 },
401 { "friday", tDAY, 5 },
402 { "saturday", tDAY, 6 },
406 /* Time units table. */
407 static TABLE const UnitsTable[] = {
408 { "year", tMONTH_UNIT, 12 },
409 { "month", tMONTH_UNIT, 1 },
410 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
411 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
412 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
413 { "hour", tMINUTE_UNIT, 60 },
414 { "minute", tMINUTE_UNIT, 1 },
415 { "min", tMINUTE_UNIT, 1 },
416 { "second", tSEC_UNIT, 1 },
417 { "sec", tSEC_UNIT, 1 },
421 /* Assorted relative-time words. */
422 static TABLE const OtherTable[] = {
423 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
424 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
425 { "today", tMINUTE_UNIT, 0 },
426 { "now", tMINUTE_UNIT, 0 },
427 { "last", tUNUMBER, -1 },
428 { "this", tMINUTE_UNIT, 0 },
429 { "next", tUNUMBER, 2 },
430 { "first", tUNUMBER, 1 },
431 /* { "second", tUNUMBER, 2 }, */
432 { "third", tUNUMBER, 3 },
433 { "fourth", tUNUMBER, 4 },
434 { "fifth", tUNUMBER, 5 },
435 { "sixth", tUNUMBER, 6 },
436 { "seventh", tUNUMBER, 7 },
437 { "eighth", tUNUMBER, 8 },
438 { "ninth", tUNUMBER, 9 },
439 { "tenth", tUNUMBER, 10 },
440 { "eleventh", tUNUMBER, 11 },
441 { "twelfth", tUNUMBER, 12 },
446 /* The timezone table. */
447 /* Some of these are commented out because a time_t can't store a float. */
448 static TABLE const TimezoneTable[] = {
449 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
450 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
451 { "utc", tZONE, HOUR( 0) },
452 { "wet", tZONE, HOUR( 0) }, /* Western European */
453 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
454 { "wat", tZONE, HOUR( 1) }, /* West Africa */
455 { "at", tZONE, HOUR( 2) }, /* Azores */
457 /* For completeness. BST is also British Summer, and GST is
458 * also Guam Standard. */
459 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
460 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
463 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
464 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
465 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
467 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
468 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
469 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
470 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
471 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
472 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
473 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
474 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
475 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
476 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
477 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
478 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
479 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
480 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
481 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
482 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
483 { "nt", tZONE, HOUR(11) }, /* Nome */
484 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
485 { "cet", tZONE, -HOUR(1) }, /* Central European */
486 { "met", tZONE, -HOUR(1) }, /* Middle European */
487 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
488 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
489 { "mesz", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
490 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
491 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
492 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
493 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
494 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
495 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
497 { "it", tZONE, -HOUR(3.5) },/* Iran */
499 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
500 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
502 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
504 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
506 /* For completeness. NST is also Newfoundland Standard, and SST is
507 * also Swedish Summer. */
508 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
509 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
511 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
512 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
514 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
516 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
517 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
519 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
520 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
522 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
523 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
524 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
525 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
526 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
527 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
528 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
532 /* Military timezone table. */
533 static TABLE const MilitaryTable[] = {
534 { "a", tZONE, HOUR( 1) },
535 { "b", tZONE, HOUR( 2) },
536 { "c", tZONE, HOUR( 3) },
537 { "d", tZONE, HOUR( 4) },
538 { "e", tZONE, HOUR( 5) },
539 { "f", tZONE, HOUR( 6) },
540 { "g", tZONE, HOUR( 7) },
541 { "h", tZONE, HOUR( 8) },
542 { "i", tZONE, HOUR( 9) },
543 { "k", tZONE, HOUR( 10) },
544 { "l", tZONE, HOUR( 11) },
545 { "m", tZONE, HOUR( 12) },
546 { "n", tZONE, HOUR(- 1) },
547 { "o", tZONE, HOUR(- 2) },
548 { "p", tZONE, HOUR(- 3) },
549 { "q", tZONE, HOUR(- 4) },
550 { "r", tZONE, HOUR(- 5) },
551 { "s", tZONE, HOUR(- 6) },
552 { "t", tZONE, HOUR(- 7) },
553 { "u", tZONE, HOUR(- 8) },
554 { "v", tZONE, HOUR(- 9) },
555 { "w", tZONE, HOUR(-10) },
556 { "x", tZONE, HOUR(-11) },
557 { "y", tZONE, HOUR(-12) },
558 { "z", tZONE, HOUR( 0) },
575 ToSeconds(Hours, Minutes, Seconds, Meridian)
581 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
585 if (Hours < 0 || Hours > 23)
587 return (Hours * 60L + Minutes) * 60L + Seconds;
589 if (Hours < 1 || Hours > 12)
591 return (Hours * 60L + Minutes) * 60L + Seconds;
593 if (Hours < 1 || Hours > 12)
595 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
604 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
614 static int DaysInMonth[12] = {
615 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
625 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
627 if (Year < EPOCH || Year > 1999
628 || Month < 1 || Month > 12
629 /* Lint fluff: "conversion from long may lose accuracy" */
630 || Day < 1 || Day > DaysInMonth[(int)--Month])
633 for (Julian = Day - 1, i = 0; i < Month; i++)
634 Julian += DaysInMonth[i];
635 for (i = EPOCH; i < Year; i++)
636 Julian += 365 + (i % 4 == 0);
637 Julian *= SECSPERDAY;
638 Julian += yyTimezone * 60L;
639 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
643 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
650 DSTcorrect(Start, Future)
657 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
658 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
659 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
664 RelativeDate(Start, DayOrdinal, DayNumber)
673 tm = localtime(&now);
674 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
675 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
676 return DSTcorrect(Start, now);
681 RelativeMonth(Start, RelMonth)
691 tm = localtime(&Start);
692 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
694 Month = Month % 12 + 1;
695 return DSTcorrect(Start,
696 Convert(Month, (time_t)tm->tm_mday, Year,
697 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
708 register const TABLE *tp;
712 /* Make it lowercase. */
713 for (p = buff; *p; p++)
717 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
718 yylval.Meridian = MERam;
721 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
722 yylval.Meridian = MERpm;
726 /* See if we have an abbreviation for a month. */
727 if (strlen(buff) == 3)
729 else if (strlen(buff) == 4 && buff[3] == '.') {
736 for (tp = MonthDayTable; tp->name; tp++) {
738 if (strncmp(buff, tp->name, 3) == 0) {
739 yylval.Number = tp->value;
743 else if (strcmp(buff, tp->name) == 0) {
744 yylval.Number = tp->value;
749 for (tp = TimezoneTable; tp->name; tp++)
750 if (strcmp(buff, tp->name) == 0) {
751 yylval.Number = tp->value;
755 if (strcmp(buff, "dst") == 0)
758 for (tp = UnitsTable; tp->name; tp++)
759 if (strcmp(buff, tp->name) == 0) {
760 yylval.Number = tp->value;
764 /* Strip off any plural and try the units table again. */
765 i = strlen(buff) - 1;
766 if (buff[i] == 's') {
768 for (tp = UnitsTable; tp->name; tp++)
769 if (strcmp(buff, tp->name) == 0) {
770 yylval.Number = tp->value;
773 buff[i] = 's'; /* Put back for "this" in OtherTable. */
776 for (tp = OtherTable; tp->name; tp++)
777 if (strcmp(buff, tp->name) == 0) {
778 yylval.Number = tp->value;
782 /* Military timezones. */
783 if (buff[1] == '\0' && isalpha(*buff)) {
784 for (tp = MilitaryTable; tp->name; tp++)
785 if (strcmp(buff, tp->name) == 0) {
786 yylval.Number = tp->value;
791 /* Drop out any periods and try the timezone table again. */
792 for (i = 0, p = q = buff; *q; q++)
799 for (tp = TimezoneTable; tp->name; tp++)
800 if (strcmp(buff, tp->name) == 0) {
801 yylval.Number = tp->value;
819 while (isspace(*yyInput))
822 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
823 if (c == '-' || c == '+') {
824 sign = c == '-' ? -1 : 1;
825 if (!isdigit(*++yyInput))
826 /* skip the '-' sign */
831 for (yylval.Number = 0; isdigit(c = *yyInput++); )
832 yylval.Number = 10 * yylval.Number + c - '0';
835 yylval.Number = -yylval.Number;
836 return sign ? tSNUMBER : tUNUMBER;
839 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
840 if (p < &buff[sizeof buff - 1])
844 return LookupWord(buff);
861 #define TM_YEAR_ORIGIN 1900
863 /* Yield A - B, measured in seconds. */
868 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
869 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
871 /* difference in day of year */
872 a->tm_yday - b->tm_yday
873 /* + intervening leap days */
874 + ((ay >> 2) - (by >> 2))
876 + ((ay/100 >> 2) - (by/100 >> 2))
877 /* + difference in years * 365 */
878 + (long)(ay-by) * 365
880 return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
881 + (a->tm_min - b->tm_min))
882 + (a->tm_sec - b->tm_sec));
898 (void)time(&ftz.time);
900 if (! (tm = gmtime (&ftz.time)))
902 gmt = *tm; /* Make a copy, in case localtime modifies *tm. */
904 if (! (tm = localtime (&ftz.time)))
907 ftz.timezone = difftm (&gmt, tm) / 60;
912 tm = localtime(&now->time);
913 yyYear = tm->tm_year;
914 yyMonth = tm->tm_mon + 1;
916 yyTimezone = now->timezone;
917 yyDSTmode = DSTmaybe;
931 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
934 if (yyHaveDate || yyHaveTime || yyHaveDay) {
935 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
936 yyMeridian, yyDSTmode);
943 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
946 Start += yyRelSeconds;
947 Start += RelativeMonth(Start, yyRelMonth);
949 if (yyHaveDay && !yyHaveDate) {
950 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
954 /* Have to do *something* with a legitimate -1 so it's distinguishable
955 * from the error return value. (Alternately could set errno on error.) */
956 return Start == -1 ? 0 : Start;
971 (void)printf("Enter date, or blank line to exit.\n\t> ");
972 (void)fflush(stdout);
973 while (gets(buff) && buff[0]) {
974 d = get_date(buff, (struct timeb *)NULL);
976 (void)printf("Bad format - couldn't convert.\n");
978 (void)printf("%s", ctime(&d));
979 (void)printf("\t> ");
980 (void)fflush(stdout);
985 #endif /* defined(TEST) */