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 nine shift/reduce conflicts.
11 ** This code is in the public domain and has no copyright.
13 /* SUPPRESS 287 on yaccpar_sccsid *//* Unusd 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. */
36 #define alloca __builtin_alloca
41 #ifdef _AIX /* for Bison */
52 /* The code at the top of get_date which figures out the offset of the
53 current time zone checks various CPP symbols to see if special
54 tricks are need, but defaults to using the gettimeofday system call.
55 Include <sys/time.h> if that will be used. */
64 #include <sys/types.h>
66 #ifdef TIME_WITH_SYS_TIME
70 #ifdef HAVE_SYS_TIME_H
78 #undef timezone /* needed for sgi */
81 #if defined(HAVE_SYS_TIMEB_H) || (!defined(USG) && defined(HAVE_FTIME))
82 #include <sys/timeb.h>
85 ** We use the obsolete `struct timeb' as part of our interface!
86 ** Since the system doesn't have it, we define it here;
87 ** our callers must do likewise.
90 time_t time; /* Seconds since the epoch */
91 unsigned short millitm; /* Field not used */
92 short timezone; /* Minutes west of GMT */
93 short dstflag; /* Field not used */
95 #endif /* defined(HAVE_SYS_TIMEB_H) */
97 #endif /* defined(vms) */
99 #if defined (STDC_HEADERS) || defined (USG)
103 /* Some old versions of bison generate parsers that use bcopy.
104 That loses on systems that don't provide the function, so we have
105 to redefine it here. */
106 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
107 #define bcopy(from, to, len) memcpy ((to), (from), (len))
110 extern struct tm *gmtime();
111 extern struct tm *localtime();
113 #define yyparse getdate_yyparse
114 #define yylex getdate_yylex
115 #define yyerror getdate_yyerror
118 static int yyerror ();
120 #if !defined(lint) && !defined(SABER)
122 "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
123 #endif /* !defined(lint) && !defined(SABER) */
127 #define HOUR(x) ((time_t)(x) * 60)
128 #define SECSPERDAY (24L * 60L * 60L)
132 ** An entry in the lexical lookup table.
134 typedef struct _TABLE {
142 ** Daylight-savings mode: on, off, or not yet known.
144 typedef enum _DSTMODE {
145 DSTon, DSToff, DSTmaybe
149 ** Meridian: am, pm, or 24-hour style.
151 typedef enum _MERIDIAN {
157 ** Global variables. We could get rid of most of these by using a good
158 ** union as the yacc stack. (This routine was originally written before
159 ** yacc had the %union construct.) Maybe someday; right now we only use
160 ** the %union very rarely.
162 static char *yyInput;
163 static DSTMODE yyDSTmode;
164 static time_t yyDayOrdinal;
165 static time_t yyDayNumber;
166 static int yyHaveDate;
167 static int yyHaveDay;
168 static int yyHaveRel;
169 static int yyHaveTime;
170 static int yyHaveZone;
171 static time_t yyTimezone;
173 static time_t yyHour;
174 static time_t yyMinutes;
175 static time_t yyMonth;
176 static time_t yySeconds;
177 static time_t yyYear;
178 static MERIDIAN yyMeridian;
179 static time_t yyRelMonth;
180 static time_t yyRelSeconds;
186 enum _MERIDIAN Meridian;
189 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
190 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
192 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
193 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
194 %type <Meridian> tMERIDIAN o_merid
220 time : tUNUMBER tMERIDIAN {
226 | tUNUMBER ':' tUNUMBER o_merid {
232 | tUNUMBER ':' tUNUMBER tSNUMBER {
237 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
239 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
245 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
251 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
284 date : tUNUMBER '/' tUNUMBER {
288 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
293 | tUNUMBER tSNUMBER tSNUMBER {
294 /* ISO 8601 format. yyyy-mm-dd. */
303 | tMONTH tUNUMBER ',' tUNUMBER {
312 | tUNUMBER tMONTH tUNUMBER {
320 yyRelSeconds = -yyRelSeconds;
321 yyRelMonth = -yyRelMonth;
326 relunit : tUNUMBER tMINUTE_UNIT {
327 yyRelSeconds += $1 * $2 * 60L;
329 | tSNUMBER tMINUTE_UNIT {
330 yyRelSeconds += $1 * $2 * 60L;
333 yyRelSeconds += $1 * 60L;
335 | tSNUMBER tSEC_UNIT {
338 | tUNUMBER tSEC_UNIT {
344 | tSNUMBER tMONTH_UNIT {
345 yyRelMonth += $1 * $2;
347 | tUNUMBER tMONTH_UNIT {
348 yyRelMonth += $1 * $2;
356 if (yyHaveTime && yyHaveDate && !yyHaveRel)
364 yyDay= (date_part)%100;
365 yyMonth= (date_part/100)%100;
366 yyYear = date_part/10000;
375 yyMinutes = $1 % 100;
383 o_merid : /* NULL */ {
393 /* Month and day table. */
394 static TABLE const MonthDayTable[] = {
395 { "january", tMONTH, 1 },
396 { "february", tMONTH, 2 },
397 { "march", tMONTH, 3 },
398 { "april", tMONTH, 4 },
399 { "may", tMONTH, 5 },
400 { "june", tMONTH, 6 },
401 { "july", tMONTH, 7 },
402 { "august", tMONTH, 8 },
403 { "september", tMONTH, 9 },
404 { "sept", tMONTH, 9 },
405 { "october", tMONTH, 10 },
406 { "november", tMONTH, 11 },
407 { "december", tMONTH, 12 },
408 { "sunday", tDAY, 0 },
409 { "monday", tDAY, 1 },
410 { "tuesday", tDAY, 2 },
412 { "wednesday", tDAY, 3 },
413 { "wednes", tDAY, 3 },
414 { "thursday", tDAY, 4 },
416 { "thurs", tDAY, 4 },
417 { "friday", tDAY, 5 },
418 { "saturday", tDAY, 6 },
422 /* Time units table. */
423 static TABLE const UnitsTable[] = {
424 { "year", tMONTH_UNIT, 12 },
425 { "month", tMONTH_UNIT, 1 },
426 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
427 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
428 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
429 { "hour", tMINUTE_UNIT, 60 },
430 { "minute", tMINUTE_UNIT, 1 },
431 { "min", tMINUTE_UNIT, 1 },
432 { "second", tSEC_UNIT, 1 },
433 { "sec", tSEC_UNIT, 1 },
437 /* Assorted relative-time words. */
438 static TABLE const OtherTable[] = {
439 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
440 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
441 { "today", tMINUTE_UNIT, 0 },
442 { "now", tMINUTE_UNIT, 0 },
443 { "last", tUNUMBER, -1 },
444 { "this", tMINUTE_UNIT, 0 },
445 { "next", tUNUMBER, 2 },
446 { "first", tUNUMBER, 1 },
447 /* { "second", tUNUMBER, 2 }, */
448 { "third", tUNUMBER, 3 },
449 { "fourth", tUNUMBER, 4 },
450 { "fifth", tUNUMBER, 5 },
451 { "sixth", tUNUMBER, 6 },
452 { "seventh", tUNUMBER, 7 },
453 { "eighth", tUNUMBER, 8 },
454 { "ninth", tUNUMBER, 9 },
455 { "tenth", tUNUMBER, 10 },
456 { "eleventh", tUNUMBER, 11 },
457 { "twelfth", tUNUMBER, 12 },
462 /* The timezone table. */
463 /* Some of these are commented out because a time_t can't store a float. */
464 static TABLE const TimezoneTable[] = {
465 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
466 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
467 { "utc", tZONE, HOUR( 0) },
468 { "wet", tZONE, HOUR( 0) }, /* Western European */
469 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
470 { "wat", tZONE, HOUR( 1) }, /* West Africa */
471 { "at", tZONE, HOUR( 2) }, /* Azores */
473 /* For completeness. BST is also British Summer, and GST is
474 * also Guam Standard. */
475 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
476 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
479 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
480 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
481 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
483 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
484 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
485 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
486 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
487 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
488 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
489 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
490 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
491 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
492 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
493 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
494 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
495 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
496 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
497 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
498 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
499 { "nt", tZONE, HOUR(11) }, /* Nome */
500 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
501 { "cet", tZONE, -HOUR(1) }, /* Central European */
502 { "met", tZONE, -HOUR(1) }, /* Middle European */
503 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
504 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
505 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
506 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
507 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
508 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
509 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
510 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
512 { "it", tZONE, -HOUR(3.5) },/* Iran */
514 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
515 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
517 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
519 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
521 /* For completeness. NST is also Newfoundland Stanard, and SST is
522 * also Swedish Summer. */
523 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
524 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
526 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
527 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
529 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
531 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
532 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
534 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
535 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
537 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
538 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
539 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
540 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
541 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
542 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
543 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
547 /* Military timezone table. */
548 static TABLE const MilitaryTable[] = {
549 { "a", tZONE, HOUR( 1) },
550 { "b", tZONE, HOUR( 2) },
551 { "c", tZONE, HOUR( 3) },
552 { "d", tZONE, HOUR( 4) },
553 { "e", tZONE, HOUR( 5) },
554 { "f", tZONE, HOUR( 6) },
555 { "g", tZONE, HOUR( 7) },
556 { "h", tZONE, HOUR( 8) },
557 { "i", tZONE, HOUR( 9) },
558 { "k", tZONE, HOUR( 10) },
559 { "l", tZONE, HOUR( 11) },
560 { "m", tZONE, HOUR( 12) },
561 { "n", tZONE, HOUR(- 1) },
562 { "o", tZONE, HOUR(- 2) },
563 { "p", tZONE, HOUR(- 3) },
564 { "q", tZONE, HOUR(- 4) },
565 { "r", tZONE, HOUR(- 5) },
566 { "s", tZONE, HOUR(- 6) },
567 { "t", tZONE, HOUR(- 7) },
568 { "u", tZONE, HOUR(- 8) },
569 { "v", tZONE, HOUR(- 9) },
570 { "w", tZONE, HOUR(-10) },
571 { "x", tZONE, HOUR(-11) },
572 { "y", tZONE, HOUR(-12) },
573 { "z", tZONE, HOUR( 0) },
590 ToSeconds(Hours, Minutes, Seconds, Meridian)
596 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
600 if (Hours < 0 || Hours > 23)
602 return (Hours * 60L + Minutes) * 60L + Seconds;
604 if (Hours < 1 || Hours > 12)
606 return (Hours * 60L + Minutes) * 60L + Seconds;
608 if (Hours < 1 || Hours > 12)
610 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
617 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
627 static int DaysInMonth[12] = {
628 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
638 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
640 if (Year < EPOCH || Year > 1999
641 || Month < 1 || Month > 12
642 /* Lint fluff: "conversion from long may lose accuracy" */
643 || Day < 1 || Day > DaysInMonth[(int)--Month])
646 for (Julian = Day - 1, i = 0; i < Month; i++)
647 Julian += DaysInMonth[i];
648 for (i = EPOCH; i < Year; i++)
649 Julian += 365 + (i % 4 == 0);
650 Julian *= SECSPERDAY;
651 Julian += yyTimezone * 60L;
652 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
656 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
663 DSTcorrect(Start, Future)
670 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
671 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
672 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
677 RelativeDate(Start, DayOrdinal, DayNumber)
686 tm = localtime(&now);
687 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
688 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
689 return DSTcorrect(Start, now);
694 RelativeMonth(Start, RelMonth)
704 tm = localtime(&Start);
705 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
707 Month = Month % 12 + 1;
708 return DSTcorrect(Start,
709 Convert(Month, (time_t)tm->tm_mday, Year,
710 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
721 register const TABLE *tp;
725 /* Make it lowercase. */
726 for (p = buff; *p; p++)
730 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
731 yylval.Meridian = MERam;
734 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
735 yylval.Meridian = MERpm;
739 /* See if we have an abbreviation for a month. */
740 if (strlen(buff) == 3)
742 else if (strlen(buff) == 4 && buff[3] == '.') {
749 for (tp = MonthDayTable; tp->name; tp++) {
751 if (strncmp(buff, tp->name, 3) == 0) {
752 yylval.Number = tp->value;
756 else if (strcmp(buff, tp->name) == 0) {
757 yylval.Number = tp->value;
762 for (tp = TimezoneTable; tp->name; tp++)
763 if (strcmp(buff, tp->name) == 0) {
764 yylval.Number = tp->value;
768 if (strcmp(buff, "dst") == 0)
771 for (tp = UnitsTable; tp->name; tp++)
772 if (strcmp(buff, tp->name) == 0) {
773 yylval.Number = tp->value;
777 /* Strip off any plural and try the units table again. */
778 i = strlen(buff) - 1;
779 if (buff[i] == 's') {
781 for (tp = UnitsTable; tp->name; tp++)
782 if (strcmp(buff, tp->name) == 0) {
783 yylval.Number = tp->value;
786 buff[i] = 's'; /* Put back for "this" in OtherTable. */
789 for (tp = OtherTable; tp->name; tp++)
790 if (strcmp(buff, tp->name) == 0) {
791 yylval.Number = tp->value;
795 /* Military timezones. */
796 if (buff[1] == '\0' && isalpha(*buff)) {
797 for (tp = MilitaryTable; tp->name; tp++)
798 if (strcmp(buff, tp->name) == 0) {
799 yylval.Number = tp->value;
804 /* Drop out any periods and try the timezone table again. */
805 for (i = 0, p = q = buff; *q; q++)
812 for (tp = TimezoneTable; tp->name; tp++)
813 if (strcmp(buff, tp->name) == 0) {
814 yylval.Number = tp->value;
832 while (isspace(*yyInput))
835 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
836 if (c == '-' || c == '+') {
837 sign = c == '-' ? -1 : 1;
838 if (!isdigit(*++yyInput))
839 /* skip the '-' sign */
844 for (yylval.Number = 0; isdigit(c = *yyInput++); )
845 yylval.Number = 10 * yylval.Number + c - '0';
848 yylval.Number = -yylval.Number;
849 return sign ? tSNUMBER : tUNUMBER;
852 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
853 if (p < &buff[sizeof buff - 1])
857 return LookupWord(buff);
875 #define TM_YEAR_ORIGIN 1900
877 /* Yield A - B, measured in seconds. */
882 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
883 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
888 /* difference in day of year */
889 a->tm_yday - b->tm_yday
890 /* + intervening leap days */
891 + ((ay >> 2) - (by >> 2))
893 + ((ay/100 >> 2) - (by/100 >> 2))
894 /* + difference in years * 365 */
895 + (time_t)(ay-by) * 365
896 )*24 + (a->tm_hour - b->tm_hour)
897 )*60 + (a->tm_min - b->tm_min)
898 )*60 + (a->tm_sec - b->tm_sec);
914 (void)time(&ftz.time);
916 if (! (tm = gmtime (&ftz.time)))
918 gmt = *tm; /* Make a copy, in case localtime modifies *tm. */
919 ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60;
922 tm = localtime(&now->time);
923 yyYear = tm->tm_year;
924 yyMonth = tm->tm_mon + 1;
926 yyTimezone = now->timezone;
927 yyDSTmode = DSTmaybe;
941 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
944 if (yyHaveDate || yyHaveTime || yyHaveDay) {
945 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
946 yyMeridian, yyDSTmode);
953 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
956 Start += yyRelSeconds;
957 Start += RelativeMonth(Start, yyRelMonth);
959 if (yyHaveDay && !yyHaveDate) {
960 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
964 /* Have to do *something* with a legitimate -1 so it's distinguishable
965 * from the error return value. (Alternately could set errno on error.) */
966 return Start == -1 ? 0 : Start;
980 (void)printf("Enter date, or blank line to exit.\n\t> ");
981 (void)fflush(stdout);
982 while (gets(buff) && buff[0]) {
983 d = get_date(buff, (struct timeb *)NULL);
985 (void)printf("Bad format - couldn't convert.\n");
987 (void)printf("%s", ctime(&d));
988 (void)printf("\t> ");
989 (void)fflush(stdout);
994 #endif /* defined(TEST) */