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 ();
105 #if !defined(lint) && !defined(SABER)
107 "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
108 #endif /* !defined(lint) && !defined(SABER) */
112 #define HOUR(x) ((time_t)(x) * 60)
113 #define SECSPERDAY (24L * 60L * 60L)
117 ** An entry in the lexical lookup table.
119 typedef struct _TABLE {
127 ** Daylight-savings mode: on, off, or not yet known.
129 typedef enum _DSTMODE {
130 DSTon, DSToff, DSTmaybe
134 ** Meridian: am, pm, or 24-hour style.
136 typedef enum _MERIDIAN {
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.
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;
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;
171 enum _MERIDIAN Meridian;
174 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
175 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
177 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
178 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
179 %type <Meridian> tMERIDIAN o_merid
205 time : tUNUMBER tMERIDIAN {
211 | tUNUMBER ':' tUNUMBER o_merid {
217 | tUNUMBER ':' tUNUMBER tSNUMBER {
222 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
224 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
230 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
236 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
269 date : tUNUMBER '/' tUNUMBER {
273 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
278 | tUNUMBER tSNUMBER tSNUMBER {
279 /* ISO 8601 format. yyyy-mm-dd. */
284 | tUNUMBER tMONTH tSNUMBER {
285 /* e.g. 17-JUN-1992. */
294 | tMONTH tUNUMBER ',' tUNUMBER {
303 | tUNUMBER tMONTH tUNUMBER {
311 yyRelSeconds = -yyRelSeconds;
312 yyRelMonth = -yyRelMonth;
317 relunit : tUNUMBER tMINUTE_UNIT {
318 yyRelSeconds += $1 * $2 * 60L;
320 | tSNUMBER tMINUTE_UNIT {
321 yyRelSeconds += $1 * $2 * 60L;
324 yyRelSeconds += $1 * 60L;
326 | tSNUMBER tSEC_UNIT {
329 | tUNUMBER tSEC_UNIT {
335 | tSNUMBER tMONTH_UNIT {
336 yyRelMonth += $1 * $2;
338 | tUNUMBER tMONTH_UNIT {
339 yyRelMonth += $1 * $2;
347 if (yyHaveTime && yyHaveDate && !yyHaveRel)
353 yyMonth= ($1/100)%100;
364 yyMinutes = $1 % 100;
373 o_merid : /* NULL */ {
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 },
402 { "wednesday", tDAY, 3 },
403 { "wednes", tDAY, 3 },
404 { "thursday", tDAY, 4 },
406 { "thurs", tDAY, 4 },
407 { "friday", tDAY, 5 },
408 { "saturday", tDAY, 6 },
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 },
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 },
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 */
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 */
469 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
470 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
471 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
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 */
502 { "it", tZONE, -HOUR(3.5) },/* Iran */
504 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
505 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
507 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
509 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
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 */
516 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
517 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
519 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
521 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
522 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
524 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
525 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
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 */
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) },
580 ToSeconds(Hours, Minutes, Seconds, Meridian)
586 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
590 if (Hours < 0 || Hours > 23)
592 return (Hours * 60L + Minutes) * 60L + Seconds;
594 if (Hours < 1 || Hours > 12)
596 return (Hours * 60L + Minutes) * 60L + Seconds;
598 if (Hours < 1 || Hours > 12)
600 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
609 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
619 static int DaysInMonth[12] = {
620 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
630 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
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])
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)
648 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
655 DSTcorrect(Start, Future)
662 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
663 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
664 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
669 RelativeDate(Start, DayOrdinal, DayNumber)
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);
686 RelativeMonth(Start, RelMonth)
696 tm = localtime(&Start);
697 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
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,
713 register const TABLE *tp;
717 /* Make it lowercase. */
718 for (p = buff; *p; p++)
722 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
723 yylval.Meridian = MERam;
726 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
727 yylval.Meridian = MERpm;
731 /* See if we have an abbreviation for a month. */
732 if (strlen(buff) == 3)
734 else if (strlen(buff) == 4 && buff[3] == '.') {
741 for (tp = MonthDayTable; tp->name; tp++) {
743 if (strncmp(buff, tp->name, 3) == 0) {
744 yylval.Number = tp->value;
748 else if (strcmp(buff, tp->name) == 0) {
749 yylval.Number = tp->value;
754 for (tp = TimezoneTable; tp->name; tp++)
755 if (strcmp(buff, tp->name) == 0) {
756 yylval.Number = tp->value;
760 if (strcmp(buff, "dst") == 0)
763 for (tp = UnitsTable; tp->name; tp++)
764 if (strcmp(buff, tp->name) == 0) {
765 yylval.Number = tp->value;
769 /* Strip off any plural and try the units table again. */
770 i = strlen(buff) - 1;
771 if (buff[i] == 's') {
773 for (tp = UnitsTable; tp->name; tp++)
774 if (strcmp(buff, tp->name) == 0) {
775 yylval.Number = tp->value;
778 buff[i] = 's'; /* Put back for "this" in OtherTable. */
781 for (tp = OtherTable; tp->name; tp++)
782 if (strcmp(buff, tp->name) == 0) {
783 yylval.Number = tp->value;
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;
796 /* Drop out any periods and try the timezone table again. */
797 for (i = 0, p = q = buff; *q; q++)
804 for (tp = TimezoneTable; tp->name; tp++)
805 if (strcmp(buff, tp->name) == 0) {
806 yylval.Number = tp->value;
824 while (isspace(*yyInput))
827 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
828 if (c == '-' || c == '+') {
829 sign = c == '-' ? -1 : 1;
830 if (!isdigit(*++yyInput))
831 /* skip the '-' sign */
836 for (yylval.Number = 0; isdigit(c = *yyInput++); )
837 yylval.Number = 10 * yylval.Number + c - '0';
840 yylval.Number = -yylval.Number;
841 return sign ? tSNUMBER : tUNUMBER;
844 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
845 if (p < &buff[sizeof buff - 1])
849 return LookupWord(buff);
866 #define TM_YEAR_ORIGIN 1900
868 /* Yield A - B, measured in seconds. */
873 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
874 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
876 /* difference in day of year */
877 a->tm_yday - b->tm_yday
878 /* + intervening leap days */
879 + ((ay >> 2) - (by >> 2))
881 + ((ay/100 >> 2) - (by/100 >> 2))
882 /* + difference in years * 365 */
883 + (long)(ay-by) * 365
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));
903 (void)time(&ftz.time);
905 if (! (tm = gmtime (&ftz.time)))
909 if (! (tm = localtime (&ftz.time)))
912 ftz.timezone = difftm (&gmt, tm) / 60;
917 tm = localtime(&now->time);
918 yyYear = tm->tm_year;
919 yyMonth = tm->tm_mon + 1;
921 yyTimezone = now->timezone;
922 yyDSTmode = DSTmaybe;
936 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
939 if (yyHaveDate || yyHaveTime || yyHaveDay) {
940 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
941 yyMeridian, yyDSTmode);
948 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
951 Start += yyRelSeconds;
952 Start += RelativeMonth(Start, yyRelMonth);
954 if (yyHaveDay && !yyHaveDate) {
955 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
959 /* Have to do *something* with a legitimate -1 so it's distinguishable
960 * from the error return value. (Alternately could set errno on error.) */
961 return Start == -1 ? 0 : Start;
976 (void)printf("Enter date, or blank line to exit.\n\t> ");
977 (void)fflush(stdout);
978 while (gets(buff) && buff[0]) {
979 d = get_date(buff, (struct timeb *)NULL);
981 (void)printf("Bad format - couldn't convert.\n");
983 (void)printf("%s", ctime(&d));
984 (void)printf("\t> ");
985 (void)fflush(stdout);
990 #endif /* defined(TEST) */