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 */
15 #include <sys/cdefs.h>
16 __FBSDID("$FreeBSD: src/usr.bin/find/getdate.y,v 1.7 2010/02/09 21:24:41 ed Exp $");
21 /* The code at the top of get_date which figures out the offset of the
22 current time zone checks various CPP symbols to see if special
23 tricks are need, but defaults to using the gettimeofday system call.
24 Include <sys/time.h> if that will be used. */
28 #else /* defined(vms) */
29 # include <sys/types.h>
30 # include <sys/time.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
63 #define yyparse getdate_yyparse
64 #define yylex getdate_yylex
65 #define yyerror getdate_yyerror
67 static int yyparse(void);
68 static int yylex(void);
69 static int yyerror(const char *);
71 time_t get_date(char *);
74 #define HOUR(x) ((time_t)(x) * 60)
75 #define SECSPERDAY (24L * 60L * 60L)
79 ** An entry in the lexical lookup table.
81 typedef struct _TABLE {
89 ** Daylight-savings mode: on, off, or not yet known.
91 typedef enum _DSTMODE {
92 DSTon, DSToff, DSTmaybe
96 ** Meridian: am, pm, or 24-hour style.
98 typedef enum _MERIDIAN {
104 ** Global variables. We could get rid of most of these by using a good
105 ** union as the yacc stack. (This routine was originally written before
106 ** yacc had the %union construct.) Maybe someday; right now we only use
107 ** the %union very rarely.
109 static char *yyInput;
110 static DSTMODE yyDSTmode;
111 static time_t yyDayOrdinal;
112 static time_t yyDayNumber;
113 static int yyHaveDate;
114 static int yyHaveDay;
115 static int yyHaveRel;
116 static int yyHaveTime;
117 static int yyHaveZone;
118 static time_t yyTimezone;
120 static time_t yyHour;
121 static time_t yyMinutes;
122 static time_t yyMonth;
123 static time_t yySeconds;
124 static time_t yyYear;
125 static MERIDIAN yyMeridian;
126 static time_t yyRelMonth;
127 static time_t yyRelSeconds;
133 enum _MERIDIAN Meridian;
136 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
137 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
139 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
140 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
141 %type <Meridian> tMERIDIAN o_merid
167 time : tUNUMBER tMERIDIAN {
173 | tUNUMBER ':' tUNUMBER o_merid {
179 | tUNUMBER ':' tUNUMBER tSNUMBER {
184 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
186 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
192 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
198 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
231 date : tUNUMBER '/' tUNUMBER {
235 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
246 | tUNUMBER tSNUMBER tSNUMBER {
247 /* ISO 8601 format. yyyy-mm-dd. */
252 | tUNUMBER tMONTH tSNUMBER {
253 /* e.g. 17-JUN-1992. */
262 | tMONTH tUNUMBER ',' tUNUMBER {
271 | tUNUMBER tMONTH tUNUMBER {
279 yyRelSeconds = -yyRelSeconds;
280 yyRelMonth = -yyRelMonth;
285 relunit : tUNUMBER tMINUTE_UNIT {
286 yyRelSeconds += $1 * $2 * 60L;
288 | tSNUMBER tMINUTE_UNIT {
289 yyRelSeconds += $1 * $2 * 60L;
292 yyRelSeconds += $1 * 60L;
294 | tSNUMBER tSEC_UNIT {
297 | tUNUMBER tSEC_UNIT {
303 | tSNUMBER tMONTH_UNIT {
304 yyRelMonth += $1 * $2;
306 | tUNUMBER tMONTH_UNIT {
307 yyRelMonth += $1 * $2;
315 if (yyHaveTime && yyHaveDate && !yyHaveRel)
321 yyMonth= ($1/100)%100;
332 yyMinutes = $1 % 100;
341 o_merid : /* NULL */ {
351 /* Month and day table. */
352 static TABLE const MonthDayTable[] = {
353 { "january", tMONTH, 1 },
354 { "february", tMONTH, 2 },
355 { "march", tMONTH, 3 },
356 { "april", tMONTH, 4 },
357 { "may", tMONTH, 5 },
358 { "june", tMONTH, 6 },
359 { "july", tMONTH, 7 },
360 { "august", tMONTH, 8 },
361 { "september", tMONTH, 9 },
362 { "sept", tMONTH, 9 },
363 { "october", tMONTH, 10 },
364 { "november", tMONTH, 11 },
365 { "december", tMONTH, 12 },
366 { "sunday", tDAY, 0 },
367 { "monday", tDAY, 1 },
368 { "tuesday", tDAY, 2 },
370 { "wednesday", tDAY, 3 },
371 { "wednes", tDAY, 3 },
372 { "thursday", tDAY, 4 },
374 { "thurs", tDAY, 4 },
375 { "friday", tDAY, 5 },
376 { "saturday", tDAY, 6 },
380 /* Time units table. */
381 static TABLE const UnitsTable[] = {
382 { "year", tMONTH_UNIT, 12 },
383 { "month", tMONTH_UNIT, 1 },
384 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
385 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
386 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
387 { "hour", tMINUTE_UNIT, 60 },
388 { "minute", tMINUTE_UNIT, 1 },
389 { "min", tMINUTE_UNIT, 1 },
390 { "second", tSEC_UNIT, 1 },
391 { "sec", tSEC_UNIT, 1 },
395 /* Assorted relative-time words. */
396 static TABLE const OtherTable[] = {
397 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
398 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
399 { "today", tMINUTE_UNIT, 0 },
400 { "now", tMINUTE_UNIT, 0 },
401 { "last", tUNUMBER, -1 },
402 { "this", tMINUTE_UNIT, 0 },
403 { "next", tUNUMBER, 2 },
404 { "first", tUNUMBER, 1 },
405 /* { "second", tUNUMBER, 2 }, */
406 { "third", tUNUMBER, 3 },
407 { "fourth", tUNUMBER, 4 },
408 { "fifth", tUNUMBER, 5 },
409 { "sixth", tUNUMBER, 6 },
410 { "seventh", tUNUMBER, 7 },
411 { "eighth", tUNUMBER, 8 },
412 { "ninth", tUNUMBER, 9 },
413 { "tenth", tUNUMBER, 10 },
414 { "eleventh", tUNUMBER, 11 },
415 { "twelfth", tUNUMBER, 12 },
420 /* The timezone table. */
421 /* Some of these are commented out because a time_t can't store a float. */
422 static TABLE const TimezoneTable[] = {
423 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
424 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
425 { "utc", tZONE, HOUR( 0) },
426 { "wet", tZONE, HOUR( 0) }, /* Western European */
427 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
428 { "wat", tZONE, HOUR( 1) }, /* West Africa */
429 { "at", tZONE, HOUR( 2) }, /* Azores */
431 /* For completeness. BST is also British Summer, and GST is
432 * also Guam Standard. */
433 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
434 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
437 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
438 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
439 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
441 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
442 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
443 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
444 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
445 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
446 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
447 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
448 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
449 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
450 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
451 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
452 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
453 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
454 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
455 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
456 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
457 { "nt", tZONE, HOUR(11) }, /* Nome */
458 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
459 { "cet", tZONE, -HOUR(1) }, /* Central European */
460 { "met", tZONE, -HOUR(1) }, /* Middle European */
461 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
462 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
463 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
464 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
465 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
466 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
467 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
468 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
470 { "it", tZONE, -HOUR(3.5) },/* Iran */
472 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
473 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
475 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
477 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
479 /* For completeness. NST is also Newfoundland Stanard, and SST is
480 * also Swedish Summer. */
481 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
482 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
484 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
485 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
487 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
489 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
490 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
492 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
493 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
495 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
496 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
497 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
498 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
499 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
500 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
501 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
505 /* Military timezone table. */
506 static TABLE const MilitaryTable[] = {
507 { "a", tZONE, HOUR( 1) },
508 { "b", tZONE, HOUR( 2) },
509 { "c", tZONE, HOUR( 3) },
510 { "d", tZONE, HOUR( 4) },
511 { "e", tZONE, HOUR( 5) },
512 { "f", tZONE, HOUR( 6) },
513 { "g", tZONE, HOUR( 7) },
514 { "h", tZONE, HOUR( 8) },
515 { "i", tZONE, HOUR( 9) },
516 { "k", tZONE, HOUR( 10) },
517 { "l", tZONE, HOUR( 11) },
518 { "m", tZONE, HOUR( 12) },
519 { "n", tZONE, HOUR(- 1) },
520 { "o", tZONE, HOUR(- 2) },
521 { "p", tZONE, HOUR(- 3) },
522 { "q", tZONE, HOUR(- 4) },
523 { "r", tZONE, HOUR(- 5) },
524 { "s", tZONE, HOUR(- 6) },
525 { "t", tZONE, HOUR(- 7) },
526 { "u", tZONE, HOUR(- 8) },
527 { "v", tZONE, HOUR(- 9) },
528 { "w", tZONE, HOUR(-10) },
529 { "x", tZONE, HOUR(-11) },
530 { "y", tZONE, HOUR(-12) },
531 { "z", tZONE, HOUR( 0) },
540 yyerror(const char *s __unused)
547 ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
549 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
553 if (Hours < 0 || Hours > 23)
555 return (Hours * 60L + Minutes) * 60L + Seconds;
557 if (Hours < 1 || Hours > 12)
561 return (Hours * 60L + Minutes) * 60L + Seconds;
563 if (Hours < 1 || Hours > 12)
567 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
576 * A negative number, which means to use its absolute value (why?)
577 * A number from 0 to 99, which means a year from 1900 to 1999, or
578 * The actual year (>=100). */
580 Convert(time_t Month, time_t Day, time_t Year,
581 time_t Hours, time_t Minutes, time_t Seconds,
582 MERIDIAN Meridian, DSTMODE DSTmode)
584 static int DaysInMonth[12] = {
585 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
597 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
599 /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
600 I'm too lazy to try to check for time_t overflow in another way. */
601 if (Year < EPOCH || Year > 2038
602 || Month < 1 || Month > 12
603 /* Lint fluff: "conversion from long may lose accuracy" */
604 || Day < 1 || Day > DaysInMonth[(int)--Month])
607 for (Julian = Day - 1, i = 0; i < Month; i++)
608 Julian += DaysInMonth[i];
609 for (i = EPOCH; i < Year; i++)
610 Julian += 365 + (i % 4 == 0);
611 Julian *= SECSPERDAY;
612 Julian += yyTimezone * 60L;
613 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
617 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
624 DSTcorrect(time_t Start, time_t Future)
629 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
630 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
631 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
636 RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
642 tm = localtime(&now);
643 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
644 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
645 return DSTcorrect(Start, now);
650 RelativeMonth(time_t Start, time_t RelMonth)
658 tm = localtime(&Start);
659 Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
661 Month = Month % 12 + 1;
662 return DSTcorrect(Start,
663 Convert(Month, (time_t)tm->tm_mday, Year,
664 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
670 LookupWord(char *buff)
678 /* Make it lowercase. */
679 for (p = buff; *p; p++)
683 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
684 yylval.Meridian = MERam;
687 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
688 yylval.Meridian = MERpm;
692 /* See if we have an abbreviation for a month. */
693 if (strlen(buff) == 3)
695 else if (strlen(buff) == 4 && buff[3] == '.') {
702 for (tp = MonthDayTable; tp->name; tp++) {
704 if (strncmp(buff, tp->name, 3) == 0) {
705 yylval.Number = tp->value;
709 else if (strcmp(buff, tp->name) == 0) {
710 yylval.Number = tp->value;
715 for (tp = TimezoneTable; tp->name; tp++)
716 if (strcmp(buff, tp->name) == 0) {
717 yylval.Number = tp->value;
721 if (strcmp(buff, "dst") == 0)
724 for (tp = UnitsTable; tp->name; tp++)
725 if (strcmp(buff, tp->name) == 0) {
726 yylval.Number = tp->value;
730 /* Strip off any plural and try the units table again. */
731 i = strlen(buff) - 1;
732 if (buff[i] == 's') {
734 for (tp = UnitsTable; tp->name; tp++)
735 if (strcmp(buff, tp->name) == 0) {
736 yylval.Number = tp->value;
739 buff[i] = 's'; /* Put back for "this" in OtherTable. */
742 for (tp = OtherTable; tp->name; tp++)
743 if (strcmp(buff, tp->name) == 0) {
744 yylval.Number = tp->value;
748 /* Military timezones. */
749 if (buff[1] == '\0' && isalpha(*buff)) {
750 for (tp = MilitaryTable; tp->name; tp++)
751 if (strcmp(buff, tp->name) == 0) {
752 yylval.Number = tp->value;
757 /* Drop out any periods and try the timezone table again. */
758 for (i = 0, p = q = buff; *q; q++)
765 for (tp = TimezoneTable; tp->name; tp++)
766 if (strcmp(buff, tp->name) == 0) {
767 yylval.Number = tp->value;
785 while (isspace(*yyInput))
788 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
789 if (c == '-' || c == '+') {
790 sign = c == '-' ? -1 : 1;
791 if (!isdigit(*++yyInput))
792 /* skip the '-' sign */
797 for (yylval.Number = 0; isdigit(c = *yyInput++); )
798 yylval.Number = 10 * yylval.Number + c - '0';
801 yylval.Number = -yylval.Number;
802 return sign ? tSNUMBER : tUNUMBER;
805 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
806 if (p < &buff[sizeof buff - 1])
810 return LookupWord(buff);
827 #define TM_YEAR_ORIGIN 1900
829 /* Yield A - B, measured in seconds. */
831 difftm (struct tm *a, struct tm *b)
833 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
834 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
836 /* difference in day of year */
837 a->tm_yday - b->tm_yday
838 /* + intervening leap days */
839 + ((ay >> 2) - (by >> 2))
841 + ((ay/100 >> 2) - (by/100 >> 2))
842 /* + difference in years * 365 */
843 + (long)(ay-by) * 365
845 return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
846 + (a->tm_min - b->tm_min))
847 + (a->tm_sec - b->tm_sec));
853 struct tm *tm, *gmt_ptr, gmt;
859 bzero (&gmt, sizeof(struct tm));
862 (void)time (&nowtime);
864 gmt_ptr = gmtime (&nowtime);
867 /* Make a copy, in case localtime modifies *tm (I think
868 that comment now applies to *gmt_ptr, but I am too
869 lazy to dig into how gmtime and locatime allocate the
870 structures they return pointers to). */
874 if (! (tm = localtime (&nowtime)))
878 tzoff = difftm (&gmt, tm) / 60;
880 /* We are on a system like VMS, where the system clock is
881 in local time and the system has no concept of timezones.
882 Hopefully we can fake this out (for the case in which the
883 user specifies no timezone) by just saying the timezone
890 tm = localtime(&nowtime);
891 yyYear = tm->tm_year + 1900;
892 yyMonth = tm->tm_mon + 1;
895 yyDSTmode = DSTmaybe;
909 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
912 if (yyHaveDate || yyHaveTime || yyHaveDay) {
913 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
914 yyMeridian, yyDSTmode);
921 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
924 Start += yyRelSeconds;
925 Start += RelativeMonth(Start, yyRelMonth);
927 if (yyHaveDay && !yyHaveDate) {
928 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
932 /* Have to do *something* with a legitimate -1 so it's distinguishable
933 * from the error return value. (Alternately could set errno on error.) */
934 return Start == -1 ? 0 : Start;
942 main(int ac, char *av[])
947 (void)printf("Enter date, or blank line to exit.\n\t> ");
948 (void)fflush(stdout);
949 while (gets(buff) && buff[0]) {
952 (void)printf("Bad format - couldn't convert.\n");
954 (void)printf("%s", ctime(&d));
955 (void)printf("\t> ");
956 (void)fflush(stdout);
961 #endif /* defined(TEST) */