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;
8 ** This grammar has 10 shift/reduce conflicts.
10 ** This code is in the public domain and has no copyright.
12 /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13 /* SUPPRESS 288 on yyerrlab *//* Label unused */
20 /* The code at the top of get_date which figures out the offset of the
21 current time zone checks various CPP symbols to see if special
22 tricks are need, but defaults to using the gettimeofday system call.
23 Include <sys/time.h> if that will be used. */
27 #else /* defined(vms) */
28 # include <sys/types.h>
29 # include <sys/time.h>
30 # include <sys/timeb.h>
31 #endif /* !defined(vms) */
33 #if defined (__STDC__) || defined (USG)
37 /* Some old versions of bison generate parsers that use bcopy.
38 That loses on systems that don't provide the function, so we have
39 to redefine it here. */
40 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
41 #define bcopy(from, to, len) memcpy ((to), (from), (len))
44 #if defined (__STDC__)
48 /* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
51 We don't want to mess with all the portability hassles of alloca.
52 In particular, most (all?) versions of bison will use alloca in
53 their parser. If bison works on your system (e.g. it should work
54 with gcc), then go ahead and use it, but the more general solution
55 is to use byacc instead of bison, which should generate a portable
56 parser. I played with adding "#define alloca dont_use_alloca", to
57 give an error if the parser generator uses alloca (and thus detect
58 unportable getdate.c's), but that seems to cause as many problems
61 extern struct tm *gmtime();
62 extern struct tm *localtime();
64 #define yyparse getdate_yyparse
65 #define yylex getdate_yylex
66 #define yyerror getdate_yyerror
68 static int yyparse ();
70 static int yyerror ();
73 #define HOUR(x) ((time_t)(x) * 60)
74 #define SECSPERDAY (24L * 60L * 60L)
78 ** An entry in the lexical lookup table.
80 typedef struct _TABLE {
88 ** Daylight-savings mode: on, off, or not yet known.
90 typedef enum _DSTMODE {
91 DSTon, DSToff, DSTmaybe
95 ** Meridian: am, pm, or 24-hour style.
97 typedef enum _MERIDIAN {
103 ** Global variables. We could get rid of most of these by using a good
104 ** union as the yacc stack. (This routine was originally written before
105 ** yacc had the %union construct.) Maybe someday; right now we only use
106 ** the %union very rarely.
108 static char *yyInput;
109 static DSTMODE yyDSTmode;
110 static time_t yyDayOrdinal;
111 static time_t yyDayNumber;
112 static int yyHaveDate;
113 static int yyHaveDay;
114 static int yyHaveRel;
115 static int yyHaveTime;
116 static int yyHaveZone;
117 static time_t yyTimezone;
119 static time_t yyHour;
120 static time_t yyMinutes;
121 static time_t yyMonth;
122 static time_t yySeconds;
123 static time_t yyYear;
124 static MERIDIAN yyMeridian;
125 static time_t yyRelMonth;
126 static time_t yyRelSeconds;
132 enum _MERIDIAN Meridian;
135 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
136 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
138 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
139 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
140 %type <Meridian> tMERIDIAN o_merid
166 time : tUNUMBER tMERIDIAN {
172 | tUNUMBER ':' tUNUMBER o_merid {
178 | tUNUMBER ':' tUNUMBER tSNUMBER {
183 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
185 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
191 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
197 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
230 date : tUNUMBER '/' tUNUMBER {
234 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
245 | tUNUMBER tSNUMBER tSNUMBER {
246 /* ISO 8601 format. yyyy-mm-dd. */
251 | tUNUMBER tMONTH tSNUMBER {
252 /* e.g. 17-JUN-1992. */
261 | tMONTH tUNUMBER ',' tUNUMBER {
270 | tUNUMBER tMONTH tUNUMBER {
278 yyRelSeconds = -yyRelSeconds;
279 yyRelMonth = -yyRelMonth;
284 relunit : tUNUMBER tMINUTE_UNIT {
285 yyRelSeconds += $1 * $2 * 60L;
287 | tSNUMBER tMINUTE_UNIT {
288 yyRelSeconds += $1 * $2 * 60L;
291 yyRelSeconds += $1 * 60L;
293 | tSNUMBER tSEC_UNIT {
296 | tUNUMBER tSEC_UNIT {
302 | tSNUMBER tMONTH_UNIT {
303 yyRelMonth += $1 * $2;
305 | tUNUMBER tMONTH_UNIT {
306 yyRelMonth += $1 * $2;
314 if (yyHaveTime && yyHaveDate && !yyHaveRel)
320 yyMonth= ($1/100)%100;
331 yyMinutes = $1 % 100;
340 o_merid : /* NULL */ {
350 /* Month and day table. */
351 static TABLE const MonthDayTable[] = {
352 { "january", tMONTH, 1 },
353 { "february", tMONTH, 2 },
354 { "march", tMONTH, 3 },
355 { "april", tMONTH, 4 },
356 { "may", tMONTH, 5 },
357 { "june", tMONTH, 6 },
358 { "july", tMONTH, 7 },
359 { "august", tMONTH, 8 },
360 { "september", tMONTH, 9 },
361 { "sept", tMONTH, 9 },
362 { "october", tMONTH, 10 },
363 { "november", tMONTH, 11 },
364 { "december", tMONTH, 12 },
365 { "sunday", tDAY, 0 },
366 { "monday", tDAY, 1 },
367 { "tuesday", tDAY, 2 },
369 { "wednesday", tDAY, 3 },
370 { "wednes", tDAY, 3 },
371 { "thursday", tDAY, 4 },
373 { "thurs", tDAY, 4 },
374 { "friday", tDAY, 5 },
375 { "saturday", tDAY, 6 },
379 /* Time units table. */
380 static TABLE const UnitsTable[] = {
381 { "year", tMONTH_UNIT, 12 },
382 { "month", tMONTH_UNIT, 1 },
383 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
384 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
385 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
386 { "hour", tMINUTE_UNIT, 60 },
387 { "minute", tMINUTE_UNIT, 1 },
388 { "min", tMINUTE_UNIT, 1 },
389 { "second", tSEC_UNIT, 1 },
390 { "sec", tSEC_UNIT, 1 },
394 /* Assorted relative-time words. */
395 static TABLE const OtherTable[] = {
396 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
397 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
398 { "today", tMINUTE_UNIT, 0 },
399 { "now", tMINUTE_UNIT, 0 },
400 { "last", tUNUMBER, -1 },
401 { "this", tMINUTE_UNIT, 0 },
402 { "next", tUNUMBER, 2 },
403 { "first", tUNUMBER, 1 },
404 /* { "second", tUNUMBER, 2 }, */
405 { "third", tUNUMBER, 3 },
406 { "fourth", tUNUMBER, 4 },
407 { "fifth", tUNUMBER, 5 },
408 { "sixth", tUNUMBER, 6 },
409 { "seventh", tUNUMBER, 7 },
410 { "eighth", tUNUMBER, 8 },
411 { "ninth", tUNUMBER, 9 },
412 { "tenth", tUNUMBER, 10 },
413 { "eleventh", tUNUMBER, 11 },
414 { "twelfth", tUNUMBER, 12 },
419 /* The timezone table. */
420 /* Some of these are commented out because a time_t can't store a float. */
421 static TABLE const TimezoneTable[] = {
422 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
423 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
424 { "utc", tZONE, HOUR( 0) },
425 { "wet", tZONE, HOUR( 0) }, /* Western European */
426 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
427 { "wat", tZONE, HOUR( 1) }, /* West Africa */
428 { "at", tZONE, HOUR( 2) }, /* Azores */
430 /* For completeness. BST is also British Summer, and GST is
431 * also Guam Standard. */
432 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
433 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
436 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
437 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
438 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
440 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
441 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
442 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
443 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
444 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
445 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
446 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
447 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
448 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
449 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
450 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
451 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
452 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
453 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
454 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
455 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
456 { "nt", tZONE, HOUR(11) }, /* Nome */
457 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
458 { "cet", tZONE, -HOUR(1) }, /* Central European */
459 { "met", tZONE, -HOUR(1) }, /* Middle European */
460 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
461 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
462 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
463 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
464 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
465 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
466 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
467 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
469 { "it", tZONE, -HOUR(3.5) },/* Iran */
471 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
472 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
474 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
476 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
478 /* For completeness. NST is also Newfoundland Stanard, and SST is
479 * also Swedish Summer. */
480 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
481 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
483 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
484 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
486 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
488 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
489 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
491 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
492 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
494 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
495 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
496 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
497 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
498 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
499 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
500 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
504 /* Military timezone table. */
505 static TABLE const MilitaryTable[] = {
506 { "a", tZONE, HOUR( 1) },
507 { "b", tZONE, HOUR( 2) },
508 { "c", tZONE, HOUR( 3) },
509 { "d", tZONE, HOUR( 4) },
510 { "e", tZONE, HOUR( 5) },
511 { "f", tZONE, HOUR( 6) },
512 { "g", tZONE, HOUR( 7) },
513 { "h", tZONE, HOUR( 8) },
514 { "i", tZONE, HOUR( 9) },
515 { "k", tZONE, HOUR( 10) },
516 { "l", tZONE, HOUR( 11) },
517 { "m", tZONE, HOUR( 12) },
518 { "n", tZONE, HOUR(- 1) },
519 { "o", tZONE, HOUR(- 2) },
520 { "p", tZONE, HOUR(- 3) },
521 { "q", tZONE, HOUR(- 4) },
522 { "r", tZONE, HOUR(- 5) },
523 { "s", tZONE, HOUR(- 6) },
524 { "t", tZONE, HOUR(- 7) },
525 { "u", tZONE, HOUR(- 8) },
526 { "v", tZONE, HOUR(- 9) },
527 { "w", tZONE, HOUR(-10) },
528 { "x", tZONE, HOUR(-11) },
529 { "y", tZONE, HOUR(-12) },
530 { "z", tZONE, HOUR( 0) },
547 ToSeconds(Hours, Minutes, Seconds, Meridian)
553 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
557 if (Hours < 0 || Hours > 23)
559 return (Hours * 60L + Minutes) * 60L + Seconds;
561 if (Hours < 1 || Hours > 12)
565 return (Hours * 60L + Minutes) * 60L + Seconds;
567 if (Hours < 1 || Hours > 12)
571 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
580 * A negative number, which means to use its absolute value (why?)
581 * A number from 0 to 99, which means a year from 1900 to 1999, or
582 * The actual year (>=100). */
584 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
594 static int DaysInMonth[12] = {
595 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
607 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
609 /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
610 I'm too lazy to try to check for time_t overflow in another way. */
611 if (Year < EPOCH || Year > 2038
612 || Month < 1 || Month > 12
613 /* Lint fluff: "conversion from long may lose accuracy" */
614 || Day < 1 || Day > DaysInMonth[(int)--Month])
617 for (Julian = Day - 1, i = 0; i < Month; i++)
618 Julian += DaysInMonth[i];
619 for (i = EPOCH; i < Year; i++)
620 Julian += 365 + (i % 4 == 0);
621 Julian *= SECSPERDAY;
622 Julian += yyTimezone * 60L;
623 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
627 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
634 DSTcorrect(Start, Future)
641 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
642 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
643 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
648 RelativeDate(Start, DayOrdinal, DayNumber)
657 tm = localtime(&now);
658 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
659 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
660 return DSTcorrect(Start, now);
665 RelativeMonth(Start, RelMonth)
675 tm = localtime(&Start);
676 Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
678 Month = Month % 12 + 1;
679 return DSTcorrect(Start,
680 Convert(Month, (time_t)tm->tm_mday, Year,
681 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
692 register const TABLE *tp;
696 /* Make it lowercase. */
697 for (p = buff; *p; p++)
701 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
702 yylval.Meridian = MERam;
705 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
706 yylval.Meridian = MERpm;
710 /* See if we have an abbreviation for a month. */
711 if (strlen(buff) == 3)
713 else if (strlen(buff) == 4 && buff[3] == '.') {
720 for (tp = MonthDayTable; tp->name; tp++) {
722 if (strncmp(buff, tp->name, 3) == 0) {
723 yylval.Number = tp->value;
727 else if (strcmp(buff, tp->name) == 0) {
728 yylval.Number = tp->value;
733 for (tp = TimezoneTable; tp->name; tp++)
734 if (strcmp(buff, tp->name) == 0) {
735 yylval.Number = tp->value;
739 if (strcmp(buff, "dst") == 0)
742 for (tp = UnitsTable; tp->name; tp++)
743 if (strcmp(buff, tp->name) == 0) {
744 yylval.Number = tp->value;
748 /* Strip off any plural and try the units table again. */
749 i = strlen(buff) - 1;
750 if (buff[i] == 's') {
752 for (tp = UnitsTable; tp->name; tp++)
753 if (strcmp(buff, tp->name) == 0) {
754 yylval.Number = tp->value;
757 buff[i] = 's'; /* Put back for "this" in OtherTable. */
760 for (tp = OtherTable; tp->name; tp++)
761 if (strcmp(buff, tp->name) == 0) {
762 yylval.Number = tp->value;
766 /* Military timezones. */
767 if (buff[1] == '\0' && isalpha(*buff)) {
768 for (tp = MilitaryTable; tp->name; tp++)
769 if (strcmp(buff, tp->name) == 0) {
770 yylval.Number = tp->value;
775 /* Drop out any periods and try the timezone table again. */
776 for (i = 0, p = q = buff; *q; q++)
783 for (tp = TimezoneTable; tp->name; tp++)
784 if (strcmp(buff, tp->name) == 0) {
785 yylval.Number = tp->value;
803 while (isspace(*yyInput))
806 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
807 if (c == '-' || c == '+') {
808 sign = c == '-' ? -1 : 1;
809 if (!isdigit(*++yyInput))
810 /* skip the '-' sign */
815 for (yylval.Number = 0; isdigit(c = *yyInput++); )
816 yylval.Number = 10 * yylval.Number + c - '0';
819 yylval.Number = -yylval.Number;
820 return sign ? tSNUMBER : tUNUMBER;
823 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
824 if (p < &buff[sizeof buff - 1])
828 return LookupWord(buff);
845 #define TM_YEAR_ORIGIN 1900
847 /* Yield A - B, measured in seconds. */
852 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
853 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
855 /* difference in day of year */
856 a->tm_yday - b->tm_yday
857 /* + intervening leap days */
858 + ((ay >> 2) - (by >> 2))
860 + ((ay/100 >> 2) - (by/100 >> 2))
861 /* + difference in years * 365 */
862 + (long)(ay-by) * 365
864 return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
865 + (a->tm_min - b->tm_min))
866 + (a->tm_sec - b->tm_sec));
885 (void)time (&nowtime);
887 gmt_ptr = gmtime (&nowtime);
890 /* Make a copy, in case localtime modifies *tm (I think
891 that comment now applies to *gmt_ptr, but I am too
892 lazy to dig into how gmtime and locatime allocate the
893 structures they return pointers to). */
897 if (! (tm = localtime (&nowtime)))
901 ftz.timezone = difftm (&gmt, tm) / 60;
903 /* We are on a system like VMS, where the system clock is
904 in local time and the system has no concept of timezones.
905 Hopefully we can fake this out (for the case in which the
906 user specifies no timezone) by just saying the timezone
918 tm = localtime(&nowtime);
919 yyYear = tm->tm_year + 1900;
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;
977 (void)printf("Enter date, or blank line to exit.\n\t> ");
978 (void)fflush(stdout);
979 while (gets(buff) && buff[0]) {
980 d = get_date(buff, (struct timeb *)NULL);
982 (void)printf("Bad format - couldn't convert.\n");
984 (void)printf("%s", ctime(&d));
985 (void)printf("\t> ");
986 (void)fflush(stdout);
991 #endif /* defined(TEST) */