4 ** Originally written by Steven M. Bellovin <smb@research.att.com> while
5 ** at the University of North Carolina at Chapel Hill. Later tweaked by
6 ** a couple of people on Usenet. Completely overhauled by Rich $alz
7 ** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
8 ** send any email to Rich.
10 ** This grammar has eight shift/reduce conflicts.
12 ** This code is in the public domain and has no copyright.
14 /* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
15 /* SUPPRESS 288 on yyerrlab *//* Label unused */
18 #define alloca __builtin_alloca
23 #ifdef _AIX /* for Bison */
34 /* The code at the top of get_date which figures out the offset of the
35 current time zone checks various CPP symbols to see if special
36 tricks are need, but defaults to using the gettimeofday system call.
37 Include <sys/time.h> if that will be used. */
39 #if !defined (USG) && !defined (sgi) && !defined (__386BSD__)
50 #include <sys/types.h>
52 #if defined(USG) || !defined(HAVE_FTIME)
54 ** If you need to do a tzset() call to set the
55 ** timezone, and don't have ftime().
58 time_t time; /* Seconds since the epoch */
59 unsigned short millitm; /* Field not used */
61 short dstflag; /* Field not used */
66 #include <sys/timeb.h>
68 #endif /* defined(USG) && !defined(HAVE_FTIME) */
70 #if defined(BSD4_2) || defined(BSD4_1C) || (defined (hp9000) && !defined (hpux))
77 #endif /* defined(BSD4_2) */
79 #endif /* defined(vms) */
81 #if defined (STDC_HEADERS) || defined (USG)
89 extern struct tm *localtime();
91 #define yyparse getdate_yyparse
92 #define yylex getdate_yylex
93 #define yyerror getdate_yyerror
95 #if !defined(lint) && !defined(SABER)
97 "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
98 #endif /* !defined(lint) && !defined(SABER) */
102 #define HOUR(x) ((time_t)(x) * 60)
103 #define SECSPERDAY (24L * 60L * 60L)
107 ** An entry in the lexical lookup table.
109 typedef struct _TABLE {
117 ** Daylight-savings mode: on, off, or not yet known.
119 typedef enum _DSTMODE {
120 DSTon, DSToff, DSTmaybe
124 ** Meridian: am, pm, or 24-hour style.
126 typedef enum _MERIDIAN {
132 ** Global variables. We could get rid of most of these by using a good
133 ** union as the yacc stack. (This routine was originally written before
134 ** yacc had the %union construct.) Maybe someday; right now we only use
135 ** the %union very rarely.
137 static char *yyInput;
138 static DSTMODE yyDSTmode;
139 static time_t yyDayOrdinal;
140 static time_t yyDayNumber;
141 static int yyHaveDate;
142 static int yyHaveDay;
143 static int yyHaveRel;
144 static int yyHaveTime;
145 static int yyHaveZone;
146 static time_t yyTimezone;
148 static time_t yyHour;
149 static time_t yyMinutes;
150 static time_t yyMonth;
151 static time_t yySeconds;
152 static time_t yyYear;
153 static MERIDIAN yyMeridian;
154 static time_t yyRelMonth;
155 static time_t yyRelSeconds;
161 enum _MERIDIAN Meridian;
164 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
165 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
167 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
168 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
169 %type <Meridian> tMERIDIAN o_merid
195 time : tUNUMBER tMERIDIAN {
201 | tUNUMBER ':' tUNUMBER o_merid {
207 | tUNUMBER ':' tUNUMBER tSNUMBER {
212 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
214 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
220 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
226 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
259 date : tUNUMBER '/' tUNUMBER {
263 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
268 | tUNUMBER tSNUMBER tSNUMBER {
269 /* ISO 8601 format. yyyy-mm-dd. */
278 | tMONTH tUNUMBER ',' tUNUMBER {
287 | tUNUMBER tMONTH tUNUMBER {
295 yyRelSeconds = -yyRelSeconds;
296 yyRelMonth = -yyRelMonth;
301 relunit : tUNUMBER tMINUTE_UNIT {
302 yyRelSeconds += $1 * $2 * 60L;
304 | tSNUMBER tMINUTE_UNIT {
305 yyRelSeconds += $1 * $2 * 60L;
308 yyRelSeconds += $1 * 60L;
310 | tSNUMBER tSEC_UNIT {
313 | tUNUMBER tSEC_UNIT {
319 | tSNUMBER tMONTH_UNIT {
320 yyRelMonth += $1 * $2;
322 | tUNUMBER tMONTH_UNIT {
323 yyRelMonth += $1 * $2;
331 if (yyHaveTime && yyHaveDate && !yyHaveRel)
339 yyDay= (date_part)%100;
340 yyMonth= (date_part/100)%100;
341 yyYear = date_part/10000;
350 yyMinutes = $1 % 100;
358 o_merid : /* NULL */ {
368 /* Month and day table. */
369 static TABLE MonthDayTable[] = {
370 { "january", tMONTH, 1 },
371 { "february", tMONTH, 2 },
372 { "march", tMONTH, 3 },
373 { "april", tMONTH, 4 },
374 { "may", tMONTH, 5 },
375 { "june", tMONTH, 6 },
376 { "july", tMONTH, 7 },
377 { "august", tMONTH, 8 },
378 { "september", tMONTH, 9 },
379 { "sept", tMONTH, 9 },
380 { "october", tMONTH, 10 },
381 { "november", tMONTH, 11 },
382 { "december", tMONTH, 12 },
383 { "sunday", tDAY, 0 },
384 { "monday", tDAY, 1 },
385 { "tuesday", tDAY, 2 },
387 { "wednesday", tDAY, 3 },
388 { "wednes", tDAY, 3 },
389 { "thursday", tDAY, 4 },
391 { "thurs", tDAY, 4 },
392 { "friday", tDAY, 5 },
393 { "saturday", tDAY, 6 },
397 /* Time units table. */
398 static TABLE UnitsTable[] = {
399 { "year", tMONTH_UNIT, 12 },
400 { "month", tMONTH_UNIT, 1 },
401 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
402 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
403 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
404 { "hour", tMINUTE_UNIT, 60 },
405 { "minute", tMINUTE_UNIT, 1 },
406 { "min", tMINUTE_UNIT, 1 },
407 { "second", tSEC_UNIT, 1 },
408 { "sec", tSEC_UNIT, 1 },
412 /* Assorted relative-time words. */
413 static TABLE OtherTable[] = {
414 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
415 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
416 { "today", tMINUTE_UNIT, 0 },
417 { "now", tMINUTE_UNIT, 0 },
418 { "last", tUNUMBER, -1 },
419 { "this", tMINUTE_UNIT, 0 },
420 { "next", tUNUMBER, 2 },
421 { "first", tUNUMBER, 1 },
422 /* { "second", tUNUMBER, 2 }, */
423 { "third", tUNUMBER, 3 },
424 { "fourth", tUNUMBER, 4 },
425 { "fifth", tUNUMBER, 5 },
426 { "sixth", tUNUMBER, 6 },
427 { "seventh", tUNUMBER, 7 },
428 { "eighth", tUNUMBER, 8 },
429 { "ninth", tUNUMBER, 9 },
430 { "tenth", tUNUMBER, 10 },
431 { "eleventh", tUNUMBER, 11 },
432 { "twelfth", tUNUMBER, 12 },
437 /* The timezone table. */
438 /* Some of these are commented out because a time_t can't store a float. */
439 static TABLE TimezoneTable[] = {
440 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
441 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
442 { "utc", tZONE, HOUR( 0) },
443 { "wet", tZONE, HOUR( 0) }, /* Western European */
444 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
445 { "wat", tZONE, HOUR( 1) }, /* West Africa */
446 { "at", tZONE, HOUR( 2) }, /* Azores */
448 /* For completeness. BST is also British Summer, and GST is
449 * also Guam Standard. */
450 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
451 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
454 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
455 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
456 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
458 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
459 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
460 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
461 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
462 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
463 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
464 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
465 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
466 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
467 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
468 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
469 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
470 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
471 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
472 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
473 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
474 { "nt", tZONE, HOUR(11) }, /* Nome */
475 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
476 { "cet", tZONE, -HOUR(1) }, /* Central European */
477 { "met", tZONE, -HOUR(1) }, /* Middle European */
478 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
479 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
480 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
481 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
482 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
483 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
484 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
485 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
487 { "it", tZONE, -HOUR(3.5) },/* Iran */
489 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
490 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
492 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
494 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
496 /* For completeness. NST is also Newfoundland Stanard, and SST is
497 * also Swedish Summer. */
498 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
499 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
501 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
502 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
504 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
506 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
507 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
509 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
510 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
512 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
513 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
514 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
515 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
516 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
517 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
518 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
522 /* Military timezone table. */
523 static TABLE MilitaryTable[] = {
524 { "a", tZONE, HOUR( 1) },
525 { "b", tZONE, HOUR( 2) },
526 { "c", tZONE, HOUR( 3) },
527 { "d", tZONE, HOUR( 4) },
528 { "e", tZONE, HOUR( 5) },
529 { "f", tZONE, HOUR( 6) },
530 { "g", tZONE, HOUR( 7) },
531 { "h", tZONE, HOUR( 8) },
532 { "i", tZONE, HOUR( 9) },
533 { "k", tZONE, HOUR( 10) },
534 { "l", tZONE, HOUR( 11) },
535 { "m", tZONE, HOUR( 12) },
536 { "n", tZONE, HOUR(- 1) },
537 { "o", tZONE, HOUR(- 2) },
538 { "p", tZONE, HOUR(- 3) },
539 { "q", tZONE, HOUR(- 4) },
540 { "r", tZONE, HOUR(- 5) },
541 { "s", tZONE, HOUR(- 6) },
542 { "t", tZONE, HOUR(- 7) },
543 { "u", tZONE, HOUR(- 8) },
544 { "v", tZONE, HOUR(- 9) },
545 { "w", tZONE, HOUR(-10) },
546 { "x", tZONE, HOUR(-11) },
547 { "y", tZONE, HOUR(-12) },
548 { "z", tZONE, HOUR( 0) },
565 ToSeconds(Hours, Minutes, Seconds, Meridian)
571 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
575 if (Hours < 0 || Hours > 23)
577 return (Hours * 60L + Minutes) * 60L + Seconds;
579 if (Hours < 1 || Hours > 12)
581 return (Hours * 60L + Minutes) * 60L + Seconds;
583 if (Hours < 1 || Hours > 12)
585 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
592 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
602 static int DaysInMonth[12] = {
603 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
613 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
615 if (Year < EPOCH || Year > 1999
616 || Month < 1 || Month > 12
617 /* Lint fluff: "conversion from long may lose accuracy" */
618 || Day < 1 || Day > DaysInMonth[(int)--Month])
621 for (Julian = Day - 1, i = 0; i < Month; i++)
622 Julian += DaysInMonth[i];
623 for (i = EPOCH; i < Year; i++)
624 Julian += 365 + (i % 4 == 0);
625 Julian *= SECSPERDAY;
626 Julian += yyTimezone * 60L;
627 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
631 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
638 DSTcorrect(Start, Future)
645 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
646 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
647 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
652 RelativeDate(Start, DayOrdinal, DayNumber)
661 tm = localtime(&now);
662 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
663 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
664 return DSTcorrect(Start, now);
669 RelativeMonth(Start, RelMonth)
679 tm = localtime(&Start);
680 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
682 Month = Month % 12 + 1;
683 return DSTcorrect(Start,
684 Convert(Month, (time_t)tm->tm_mday, Year,
685 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
700 /* Make it lowercase. */
701 for (p = buff; *p; p++)
705 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
706 yylval.Meridian = MERam;
709 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
710 yylval.Meridian = MERpm;
714 /* See if we have an abbreviation for a month. */
715 if (strlen(buff) == 3)
717 else if (strlen(buff) == 4 && buff[3] == '.') {
724 for (tp = MonthDayTable; tp->name; tp++) {
726 if (strncmp(buff, tp->name, 3) == 0) {
727 yylval.Number = tp->value;
731 else if (strcmp(buff, tp->name) == 0) {
732 yylval.Number = tp->value;
737 for (tp = TimezoneTable; tp->name; tp++)
738 if (strcmp(buff, tp->name) == 0) {
739 yylval.Number = tp->value;
743 if (strcmp(buff, "dst") == 0)
746 for (tp = UnitsTable; tp->name; tp++)
747 if (strcmp(buff, tp->name) == 0) {
748 yylval.Number = tp->value;
752 /* Strip off any plural and try the units table again. */
753 i = strlen(buff) - 1;
754 if (buff[i] == 's') {
756 for (tp = UnitsTable; tp->name; tp++)
757 if (strcmp(buff, tp->name) == 0) {
758 yylval.Number = tp->value;
761 buff[i] = 's'; /* Put back for "this" in OtherTable. */
764 for (tp = OtherTable; tp->name; tp++)
765 if (strcmp(buff, tp->name) == 0) {
766 yylval.Number = tp->value;
770 /* Military timezones. */
771 if (buff[1] == '\0' && isalpha(*buff)) {
772 for (tp = MilitaryTable; tp->name; tp++)
773 if (strcmp(buff, tp->name) == 0) {
774 yylval.Number = tp->value;
779 /* Drop out any periods and try the timezone table again. */
780 for (i = 0, p = q = buff; *q; q++)
787 for (tp = TimezoneTable; tp->name; tp++)
788 if (strcmp(buff, tp->name) == 0) {
789 yylval.Number = tp->value;
807 while (isspace(*yyInput))
810 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
811 if (c == '-' || c == '+') {
812 sign = c == '-' ? -1 : 1;
813 if (!isdigit(*++yyInput))
814 /* skip the '-' sign */
819 for (yylval.Number = 0; isdigit(c = *yyInput++); )
820 yylval.Number = 10 * yylval.Number + c - '0';
823 yylval.Number = -yylval.Number;
824 return sign ? tSNUMBER : tUNUMBER;
827 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
828 if (p < &buff[sizeof buff - 1])
832 return LookupWord(buff);
863 #if !defined(HAVE_FTIME)
864 (void)time(&ftz.time);
865 /* Set the timezone global. */
869 ftz.timezone = (int) _timezone / 60;
873 #else /* neither sgi nor 386BSD */
875 extern time_t timezone;
877 ftz.timezone = (int) timezone / 60;
878 #else /* neither sgi nor 386BSD nor USG */
882 gettimeofday (&tv, &tz);
883 ftz.timezone = (int) tz.tz_minuteswest;
884 #endif /* neither sgi nor 386BSD nor USG */
885 #endif /* neither sgi nor 386BSD */
888 #else /* HAVE_FTIME */
890 #endif /* HAVE_FTIME */
893 tm = localtime(&now->time);
894 yyYear = tm->tm_year;
895 yyMonth = tm->tm_mon + 1;
897 yyTimezone = now->timezone;
898 yyDSTmode = DSTmaybe;
912 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
915 if (yyHaveDate || yyHaveTime || yyHaveDay) {
916 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
917 yyMeridian, yyDSTmode);
924 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
927 Start += yyRelSeconds;
928 Start += RelativeMonth(Start, yyRelMonth);
930 if (yyHaveDay && !yyHaveDate) {
931 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
935 /* Have to do *something* with a legitimate -1 so it's distinguishable
936 * from the error return value. (Alternately could set errno on error.) */
937 return Start == -1 ? 0 : Start;
951 (void)printf("Enter date, or blank line to exit.\n\t> ");
952 (void)fflush(stdout);
953 while (gets(buff) && buff[0]) {
954 d = get_date(buff, (struct timeb *)NULL);
956 (void)printf("Bad format - couldn't convert.\n");
958 (void)printf("%s", ctime(&d));
959 (void)printf("\t> ");
960 (void)fflush(stdout);
965 #endif /* defined(TEST) */