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 */
20 /* Since the code of getdate.y is not included in the Emacs executable
21 itself, there is no need to #define static in this file. Even if
22 the code were included in the Emacs executable, it probably
23 wouldn't do any harm to #undef it here; this will only cause
24 problems if we try to write to a static variable, which I don't
25 think this code needs to do. */
32 #define alloca __builtin_alloca
37 #ifdef _AIX /* for Bison */
48 /* The code at the top of get_date which figures out the offset of the
49 current time zone checks various CPP symbols to see if special
50 tricks are need, but defaults to using the gettimeofday system call.
51 Include <sys/time.h> if that will be used. */
60 #include <sys/types.h>
62 #ifdef TIME_WITH_SYS_TIME
66 #ifdef HAVE_SYS_TIME_H
74 #undef timezone /* needed for sgi */
77 #if defined(HAVE_SYS_TIMEB_H) || (!defined(USG) && defined(HAVE_FTIME))
78 #include <sys/timeb.h>
81 ** We use the obsolete `struct timeb' as part of our interface!
82 ** Since the system doesn't have it, we define it here;
83 ** our callers must do likewise.
86 time_t time; /* Seconds since the epoch */
87 unsigned short millitm; /* Field not used */
88 short timezone; /* Minutes west of GMT */
89 short dstflag; /* Field not used */
91 #endif /* defined(HAVE_SYS_TIMEB_H) */
93 #endif /* defined(vms) */
95 #if defined (STDC_HEADERS) || defined (USG)
99 /* Some old versions of bison generate parsers that use bcopy.
100 That loses on systems that don't provide the function, so we have
101 to redefine it here. */
102 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
103 #define bcopy(from, to, len) memcpy ((to), (from), (len))
106 extern struct tm *gmtime();
107 extern struct tm *localtime();
109 #define yyparse getdate_yyparse
110 #define yylex getdate_yylex
111 #define yyerror getdate_yyerror
114 static int yyerror ();
116 #if !defined(lint) && !defined(SABER)
118 "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
119 #endif /* !defined(lint) && !defined(SABER) */
123 #define HOUR(x) ((time_t)(x) * 60)
124 #define SECSPERDAY (24L * 60L * 60L)
128 ** An entry in the lexical lookup table.
130 typedef struct _TABLE {
138 ** Daylight-savings mode: on, off, or not yet known.
140 typedef enum _DSTMODE {
141 DSTon, DSToff, DSTmaybe
145 ** Meridian: am, pm, or 24-hour style.
147 typedef enum _MERIDIAN {
153 ** Global variables. We could get rid of most of these by using a good
154 ** union as the yacc stack. (This routine was originally written before
155 ** yacc had the %union construct.) Maybe someday; right now we only use
156 ** the %union very rarely.
158 static char *yyInput;
159 static DSTMODE yyDSTmode;
160 static time_t yyDayOrdinal;
161 static time_t yyDayNumber;
162 static int yyHaveDate;
163 static int yyHaveDay;
164 static int yyHaveRel;
165 static int yyHaveTime;
166 static int yyHaveZone;
167 static time_t yyTimezone;
169 static time_t yyHour;
170 static time_t yyMinutes;
171 static time_t yyMonth;
172 static time_t yySeconds;
173 static time_t yyYear;
174 static MERIDIAN yyMeridian;
175 static time_t yyRelMonth;
176 static time_t yyRelSeconds;
182 enum _MERIDIAN Meridian;
185 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
186 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
188 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
189 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
190 %type <Meridian> tMERIDIAN o_merid
216 time : tUNUMBER tMERIDIAN {
222 | tUNUMBER ':' tUNUMBER o_merid {
228 | tUNUMBER ':' tUNUMBER tSNUMBER {
233 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
235 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
241 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
247 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
280 date : tUNUMBER '/' tUNUMBER {
284 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
289 | tUNUMBER tSNUMBER tSNUMBER {
290 /* ISO 8601 format. yyyy-mm-dd. */
299 | tMONTH tUNUMBER ',' tUNUMBER {
308 | tUNUMBER tMONTH tUNUMBER {
316 yyRelSeconds = -yyRelSeconds;
317 yyRelMonth = -yyRelMonth;
322 relunit : tUNUMBER tMINUTE_UNIT {
323 yyRelSeconds += $1 * $2 * 60L;
325 | tSNUMBER tMINUTE_UNIT {
326 yyRelSeconds += $1 * $2 * 60L;
329 yyRelSeconds += $1 * 60L;
331 | tSNUMBER tSEC_UNIT {
334 | tUNUMBER tSEC_UNIT {
340 | tSNUMBER tMONTH_UNIT {
341 yyRelMonth += $1 * $2;
343 | tUNUMBER tMONTH_UNIT {
344 yyRelMonth += $1 * $2;
352 if (yyHaveTime && yyHaveDate && !yyHaveRel)
360 yyDay= (date_part)%100;
361 yyMonth= (date_part/100)%100;
362 yyYear = date_part/10000;
371 yyMinutes = $1 % 100;
379 o_merid : /* NULL */ {
389 /* Month and day table. */
390 static TABLE const MonthDayTable[] = {
391 { "january", tMONTH, 1 },
392 { "february", tMONTH, 2 },
393 { "march", tMONTH, 3 },
394 { "april", tMONTH, 4 },
395 { "may", tMONTH, 5 },
396 { "june", tMONTH, 6 },
397 { "july", tMONTH, 7 },
398 { "august", tMONTH, 8 },
399 { "september", tMONTH, 9 },
400 { "sept", tMONTH, 9 },
401 { "october", tMONTH, 10 },
402 { "november", tMONTH, 11 },
403 { "december", tMONTH, 12 },
404 { "sunday", tDAY, 0 },
405 { "monday", tDAY, 1 },
406 { "tuesday", tDAY, 2 },
408 { "wednesday", tDAY, 3 },
409 { "wednes", tDAY, 3 },
410 { "thursday", tDAY, 4 },
412 { "thurs", tDAY, 4 },
413 { "friday", tDAY, 5 },
414 { "saturday", tDAY, 6 },
418 /* Time units table. */
419 static TABLE const UnitsTable[] = {
420 { "year", tMONTH_UNIT, 12 },
421 { "month", tMONTH_UNIT, 1 },
422 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
423 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
424 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
425 { "hour", tMINUTE_UNIT, 60 },
426 { "minute", tMINUTE_UNIT, 1 },
427 { "min", tMINUTE_UNIT, 1 },
428 { "second", tSEC_UNIT, 1 },
429 { "sec", tSEC_UNIT, 1 },
433 /* Assorted relative-time words. */
434 static TABLE const OtherTable[] = {
435 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
436 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
437 { "today", tMINUTE_UNIT, 0 },
438 { "now", tMINUTE_UNIT, 0 },
439 { "last", tUNUMBER, -1 },
440 { "this", tMINUTE_UNIT, 0 },
441 { "next", tUNUMBER, 2 },
442 { "first", tUNUMBER, 1 },
443 /* { "second", tUNUMBER, 2 }, */
444 { "third", tUNUMBER, 3 },
445 { "fourth", tUNUMBER, 4 },
446 { "fifth", tUNUMBER, 5 },
447 { "sixth", tUNUMBER, 6 },
448 { "seventh", tUNUMBER, 7 },
449 { "eighth", tUNUMBER, 8 },
450 { "ninth", tUNUMBER, 9 },
451 { "tenth", tUNUMBER, 10 },
452 { "eleventh", tUNUMBER, 11 },
453 { "twelfth", tUNUMBER, 12 },
458 /* The timezone table. */
459 /* Some of these are commented out because a time_t can't store a float. */
460 static TABLE const TimezoneTable[] = {
461 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
462 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
463 { "utc", tZONE, HOUR( 0) },
464 { "wet", tZONE, HOUR( 0) }, /* Western European */
465 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
466 { "wat", tZONE, HOUR( 1) }, /* West Africa */
467 { "at", tZONE, HOUR( 2) }, /* Azores */
469 /* For completeness. BST is also British Summer, and GST is
470 * also Guam Standard. */
471 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
472 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
475 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
476 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
477 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
479 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
480 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
481 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
482 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
483 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
484 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
485 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
486 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
487 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
488 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
489 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
490 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
491 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
492 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
493 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
494 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
495 { "nt", tZONE, HOUR(11) }, /* Nome */
496 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
497 { "cet", tZONE, -HOUR(1) }, /* Central European */
498 { "met", tZONE, -HOUR(1) }, /* Middle European */
499 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
500 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
501 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
502 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
503 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
504 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
505 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
506 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
508 { "it", tZONE, -HOUR(3.5) },/* Iran */
510 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
511 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
513 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
515 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
517 /* For completeness. NST is also Newfoundland Stanard, and SST is
518 * also Swedish Summer. */
519 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
520 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
522 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
523 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
525 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
527 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
528 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
530 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
531 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
533 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
534 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
535 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
536 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
537 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
538 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
539 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
543 /* Military timezone table. */
544 static TABLE const MilitaryTable[] = {
545 { "a", tZONE, HOUR( 1) },
546 { "b", tZONE, HOUR( 2) },
547 { "c", tZONE, HOUR( 3) },
548 { "d", tZONE, HOUR( 4) },
549 { "e", tZONE, HOUR( 5) },
550 { "f", tZONE, HOUR( 6) },
551 { "g", tZONE, HOUR( 7) },
552 { "h", tZONE, HOUR( 8) },
553 { "i", tZONE, HOUR( 9) },
554 { "k", tZONE, HOUR( 10) },
555 { "l", tZONE, HOUR( 11) },
556 { "m", tZONE, HOUR( 12) },
557 { "n", tZONE, HOUR(- 1) },
558 { "o", tZONE, HOUR(- 2) },
559 { "p", tZONE, HOUR(- 3) },
560 { "q", tZONE, HOUR(- 4) },
561 { "r", tZONE, HOUR(- 5) },
562 { "s", tZONE, HOUR(- 6) },
563 { "t", tZONE, HOUR(- 7) },
564 { "u", tZONE, HOUR(- 8) },
565 { "v", tZONE, HOUR(- 9) },
566 { "w", tZONE, HOUR(-10) },
567 { "x", tZONE, HOUR(-11) },
568 { "y", tZONE, HOUR(-12) },
569 { "z", tZONE, HOUR( 0) },
586 ToSeconds(Hours, Minutes, Seconds, Meridian)
592 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
596 if (Hours < 0 || Hours > 23)
598 return (Hours * 60L + Minutes) * 60L + Seconds;
600 if (Hours < 1 || Hours > 12)
602 return (Hours * 60L + Minutes) * 60L + Seconds;
604 if (Hours < 1 || Hours > 12)
606 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
613 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
623 static int DaysInMonth[12] = {
624 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
634 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
636 if (Year < EPOCH || Year > 1999
637 || Month < 1 || Month > 12
638 /* Lint fluff: "conversion from long may lose accuracy" */
639 || Day < 1 || Day > DaysInMonth[(int)--Month])
642 for (Julian = Day - 1, i = 0; i < Month; i++)
643 Julian += DaysInMonth[i];
644 for (i = EPOCH; i < Year; i++)
645 Julian += 365 + (i % 4 == 0);
646 Julian *= SECSPERDAY;
647 Julian += yyTimezone * 60L;
648 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
652 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
659 DSTcorrect(Start, Future)
666 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
667 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
668 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
673 RelativeDate(Start, DayOrdinal, DayNumber)
682 tm = localtime(&now);
683 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
684 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
685 return DSTcorrect(Start, now);
690 RelativeMonth(Start, RelMonth)
700 tm = localtime(&Start);
701 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
703 Month = Month % 12 + 1;
704 return DSTcorrect(Start,
705 Convert(Month, (time_t)tm->tm_mday, Year,
706 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
717 register const TABLE *tp;
721 /* Make it lowercase. */
722 for (p = buff; *p; p++)
726 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
727 yylval.Meridian = MERam;
730 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
731 yylval.Meridian = MERpm;
735 /* See if we have an abbreviation for a month. */
736 if (strlen(buff) == 3)
738 else if (strlen(buff) == 4 && buff[3] == '.') {
745 for (tp = MonthDayTable; tp->name; tp++) {
747 if (strncmp(buff, tp->name, 3) == 0) {
748 yylval.Number = tp->value;
752 else if (strcmp(buff, tp->name) == 0) {
753 yylval.Number = tp->value;
758 for (tp = TimezoneTable; tp->name; tp++)
759 if (strcmp(buff, tp->name) == 0) {
760 yylval.Number = tp->value;
764 if (strcmp(buff, "dst") == 0)
767 for (tp = UnitsTable; tp->name; tp++)
768 if (strcmp(buff, tp->name) == 0) {
769 yylval.Number = tp->value;
773 /* Strip off any plural and try the units table again. */
774 i = strlen(buff) - 1;
775 if (buff[i] == 's') {
777 for (tp = UnitsTable; tp->name; tp++)
778 if (strcmp(buff, tp->name) == 0) {
779 yylval.Number = tp->value;
782 buff[i] = 's'; /* Put back for "this" in OtherTable. */
785 for (tp = OtherTable; tp->name; tp++)
786 if (strcmp(buff, tp->name) == 0) {
787 yylval.Number = tp->value;
791 /* Military timezones. */
792 if (buff[1] == '\0' && isalpha(*buff)) {
793 for (tp = MilitaryTable; tp->name; tp++)
794 if (strcmp(buff, tp->name) == 0) {
795 yylval.Number = tp->value;
800 /* Drop out any periods and try the timezone table again. */
801 for (i = 0, p = q = buff; *q; q++)
808 for (tp = TimezoneTable; tp->name; tp++)
809 if (strcmp(buff, tp->name) == 0) {
810 yylval.Number = tp->value;
828 while (isspace(*yyInput))
831 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
832 if (c == '-' || c == '+') {
833 sign = c == '-' ? -1 : 1;
834 if (!isdigit(*++yyInput))
835 /* skip the '-' sign */
840 for (yylval.Number = 0; isdigit(c = *yyInput++); )
841 yylval.Number = 10 * yylval.Number + c - '0';
844 yylval.Number = -yylval.Number;
845 return sign ? tSNUMBER : tUNUMBER;
848 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
849 if (p < &buff[sizeof buff - 1])
853 return LookupWord(buff);
871 #define TM_YEAR_ORIGIN 1900
873 /* Yield A - B, measured in seconds. */
878 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
879 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
884 /* difference in day of year */
885 a->tm_yday - b->tm_yday
886 /* + intervening leap days */
887 + ((ay >> 2) - (by >> 2))
889 + ((ay/100 >> 2) - (by/100 >> 2))
890 /* + difference in years * 365 */
891 + (time_t)(ay-by) * 365
892 )*24 + (a->tm_hour - b->tm_hour)
893 )*60 + (a->tm_min - b->tm_min)
894 )*60 + (a->tm_sec - b->tm_sec);
910 (void)time(&ftz.time);
912 if (! (tm = gmtime (&ftz.time)))
914 gmt = *tm; /* Make a copy, in case localtime modifies *tm. */
915 ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60;
918 tm = localtime(&now->time);
919 yyYear = tm->tm_year;
920 yyMonth = tm->tm_mon + 1;
922 yyTimezone = now->timezone;
923 yyDSTmode = DSTmaybe;
937 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
940 if (yyHaveDate || yyHaveTime || yyHaveDay) {
941 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
942 yyMeridian, yyDSTmode);
949 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
952 Start += yyRelSeconds;
953 Start += RelativeMonth(Start, yyRelMonth);
955 if (yyHaveDay && !yyHaveDate) {
956 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
960 /* Have to do *something* with a legitimate -1 so it's distinguishable
961 * from the error return value. (Alternately could set errno on error.) */
962 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) */