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.6 2009/12/13 03:14:06 delphij 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 # include <sys/timeb.h>
32 #endif /* !defined(vms) */
34 #if defined (__STDC__) || defined (USG)
38 /* Some old versions of bison generate parsers that use bcopy.
39 That loses on systems that don't provide the function, so we have
40 to redefine it here. */
41 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
42 #define bcopy(from, to, len) memcpy ((to), (from), (len))
45 #if defined (__STDC__)
49 /* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
52 We don't want to mess with all the portability hassles of alloca.
53 In particular, most (all?) versions of bison will use alloca in
54 their parser. If bison works on your system (e.g. it should work
55 with gcc), then go ahead and use it, but the more general solution
56 is to use byacc instead of bison, which should generate a portable
57 parser. I played with adding "#define alloca dont_use_alloca", to
58 give an error if the parser generator uses alloca (and thus detect
59 unportable getdate.c's), but that seems to cause as many problems
64 #define yyparse getdate_yyparse
65 #define yylex getdate_yylex
66 #define yyerror getdate_yyerror
68 static int yyparse(void);
69 static int yylex(void);
70 static int yyerror(const char *);
72 time_t get_date(char *, struct timeb *);
75 #define HOUR(x) ((time_t)(x) * 60)
76 #define SECSPERDAY (24L * 60L * 60L)
80 ** An entry in the lexical lookup table.
82 typedef struct _TABLE {
90 ** Daylight-savings mode: on, off, or not yet known.
92 typedef enum _DSTMODE {
93 DSTon, DSToff, DSTmaybe
97 ** Meridian: am, pm, or 24-hour style.
99 typedef enum _MERIDIAN {
105 ** Global variables. We could get rid of most of these by using a good
106 ** union as the yacc stack. (This routine was originally written before
107 ** yacc had the %union construct.) Maybe someday; right now we only use
108 ** the %union very rarely.
110 static char *yyInput;
111 static DSTMODE yyDSTmode;
112 static time_t yyDayOrdinal;
113 static time_t yyDayNumber;
114 static int yyHaveDate;
115 static int yyHaveDay;
116 static int yyHaveRel;
117 static int yyHaveTime;
118 static int yyHaveZone;
119 static time_t yyTimezone;
121 static time_t yyHour;
122 static time_t yyMinutes;
123 static time_t yyMonth;
124 static time_t yySeconds;
125 static time_t yyYear;
126 static MERIDIAN yyMeridian;
127 static time_t yyRelMonth;
128 static time_t yyRelSeconds;
134 enum _MERIDIAN Meridian;
137 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
138 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
140 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
141 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
142 %type <Meridian> tMERIDIAN o_merid
168 time : tUNUMBER tMERIDIAN {
174 | tUNUMBER ':' tUNUMBER o_merid {
180 | tUNUMBER ':' tUNUMBER tSNUMBER {
185 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
187 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
193 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
199 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
232 date : tUNUMBER '/' tUNUMBER {
236 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
247 | tUNUMBER tSNUMBER tSNUMBER {
248 /* ISO 8601 format. yyyy-mm-dd. */
253 | tUNUMBER tMONTH tSNUMBER {
254 /* e.g. 17-JUN-1992. */
263 | tMONTH tUNUMBER ',' tUNUMBER {
272 | tUNUMBER tMONTH tUNUMBER {
280 yyRelSeconds = -yyRelSeconds;
281 yyRelMonth = -yyRelMonth;
286 relunit : tUNUMBER tMINUTE_UNIT {
287 yyRelSeconds += $1 * $2 * 60L;
289 | tSNUMBER tMINUTE_UNIT {
290 yyRelSeconds += $1 * $2 * 60L;
293 yyRelSeconds += $1 * 60L;
295 | tSNUMBER tSEC_UNIT {
298 | tUNUMBER tSEC_UNIT {
304 | tSNUMBER tMONTH_UNIT {
305 yyRelMonth += $1 * $2;
307 | tUNUMBER tMONTH_UNIT {
308 yyRelMonth += $1 * $2;
316 if (yyHaveTime && yyHaveDate && !yyHaveRel)
322 yyMonth= ($1/100)%100;
333 yyMinutes = $1 % 100;
342 o_merid : /* NULL */ {
352 /* Month and day table. */
353 static TABLE const MonthDayTable[] = {
354 { "january", tMONTH, 1 },
355 { "february", tMONTH, 2 },
356 { "march", tMONTH, 3 },
357 { "april", tMONTH, 4 },
358 { "may", tMONTH, 5 },
359 { "june", tMONTH, 6 },
360 { "july", tMONTH, 7 },
361 { "august", tMONTH, 8 },
362 { "september", tMONTH, 9 },
363 { "sept", tMONTH, 9 },
364 { "october", tMONTH, 10 },
365 { "november", tMONTH, 11 },
366 { "december", tMONTH, 12 },
367 { "sunday", tDAY, 0 },
368 { "monday", tDAY, 1 },
369 { "tuesday", tDAY, 2 },
371 { "wednesday", tDAY, 3 },
372 { "wednes", tDAY, 3 },
373 { "thursday", tDAY, 4 },
375 { "thurs", tDAY, 4 },
376 { "friday", tDAY, 5 },
377 { "saturday", tDAY, 6 },
381 /* Time units table. */
382 static TABLE const UnitsTable[] = {
383 { "year", tMONTH_UNIT, 12 },
384 { "month", tMONTH_UNIT, 1 },
385 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
386 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
387 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
388 { "hour", tMINUTE_UNIT, 60 },
389 { "minute", tMINUTE_UNIT, 1 },
390 { "min", tMINUTE_UNIT, 1 },
391 { "second", tSEC_UNIT, 1 },
392 { "sec", tSEC_UNIT, 1 },
396 /* Assorted relative-time words. */
397 static TABLE const OtherTable[] = {
398 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
399 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
400 { "today", tMINUTE_UNIT, 0 },
401 { "now", tMINUTE_UNIT, 0 },
402 { "last", tUNUMBER, -1 },
403 { "this", tMINUTE_UNIT, 0 },
404 { "next", tUNUMBER, 2 },
405 { "first", tUNUMBER, 1 },
406 /* { "second", tUNUMBER, 2 }, */
407 { "third", tUNUMBER, 3 },
408 { "fourth", tUNUMBER, 4 },
409 { "fifth", tUNUMBER, 5 },
410 { "sixth", tUNUMBER, 6 },
411 { "seventh", tUNUMBER, 7 },
412 { "eighth", tUNUMBER, 8 },
413 { "ninth", tUNUMBER, 9 },
414 { "tenth", tUNUMBER, 10 },
415 { "eleventh", tUNUMBER, 11 },
416 { "twelfth", tUNUMBER, 12 },
421 /* The timezone table. */
422 /* Some of these are commented out because a time_t can't store a float. */
423 static TABLE const TimezoneTable[] = {
424 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
425 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
426 { "utc", tZONE, HOUR( 0) },
427 { "wet", tZONE, HOUR( 0) }, /* Western European */
428 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
429 { "wat", tZONE, HOUR( 1) }, /* West Africa */
430 { "at", tZONE, HOUR( 2) }, /* Azores */
432 /* For completeness. BST is also British Summer, and GST is
433 * also Guam Standard. */
434 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
435 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
438 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
439 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
440 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
442 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
443 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
444 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
445 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
446 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
447 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
448 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
449 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
450 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
451 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
452 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
453 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
454 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
455 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
456 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
457 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
458 { "nt", tZONE, HOUR(11) }, /* Nome */
459 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
460 { "cet", tZONE, -HOUR(1) }, /* Central European */
461 { "met", tZONE, -HOUR(1) }, /* Middle European */
462 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
463 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
464 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
465 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
466 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
467 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
468 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
469 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
471 { "it", tZONE, -HOUR(3.5) },/* Iran */
473 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
474 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
476 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
478 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
480 /* For completeness. NST is also Newfoundland Stanard, and SST is
481 * also Swedish Summer. */
482 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
483 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
485 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
486 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
488 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
490 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
491 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
493 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
494 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
496 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
497 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
498 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
499 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
500 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
501 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
502 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
506 /* Military timezone table. */
507 static TABLE const MilitaryTable[] = {
508 { "a", tZONE, HOUR( 1) },
509 { "b", tZONE, HOUR( 2) },
510 { "c", tZONE, HOUR( 3) },
511 { "d", tZONE, HOUR( 4) },
512 { "e", tZONE, HOUR( 5) },
513 { "f", tZONE, HOUR( 6) },
514 { "g", tZONE, HOUR( 7) },
515 { "h", tZONE, HOUR( 8) },
516 { "i", tZONE, HOUR( 9) },
517 { "k", tZONE, HOUR( 10) },
518 { "l", tZONE, HOUR( 11) },
519 { "m", tZONE, HOUR( 12) },
520 { "n", tZONE, HOUR(- 1) },
521 { "o", tZONE, HOUR(- 2) },
522 { "p", tZONE, HOUR(- 3) },
523 { "q", tZONE, HOUR(- 4) },
524 { "r", tZONE, HOUR(- 5) },
525 { "s", tZONE, HOUR(- 6) },
526 { "t", tZONE, HOUR(- 7) },
527 { "u", tZONE, HOUR(- 8) },
528 { "v", tZONE, HOUR(- 9) },
529 { "w", tZONE, HOUR(-10) },
530 { "x", tZONE, HOUR(-11) },
531 { "y", tZONE, HOUR(-12) },
532 { "z", tZONE, HOUR( 0) },
541 yyerror(const char *s __unused)
548 ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
550 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
554 if (Hours < 0 || Hours > 23)
556 return (Hours * 60L + Minutes) * 60L + Seconds;
558 if (Hours < 1 || Hours > 12)
562 return (Hours * 60L + Minutes) * 60L + Seconds;
564 if (Hours < 1 || Hours > 12)
568 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
577 * A negative number, which means to use its absolute value (why?)
578 * A number from 0 to 99, which means a year from 1900 to 1999, or
579 * The actual year (>=100). */
581 Convert(time_t Month, time_t Day, time_t Year,
582 time_t Hours, time_t Minutes, time_t Seconds,
583 MERIDIAN Meridian, DSTMODE DSTmode)
585 static int DaysInMonth[12] = {
586 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
598 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
600 /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
601 I'm too lazy to try to check for time_t overflow in another way. */
602 if (Year < EPOCH || Year > 2038
603 || Month < 1 || Month > 12
604 /* Lint fluff: "conversion from long may lose accuracy" */
605 || Day < 1 || Day > DaysInMonth[(int)--Month])
608 for (Julian = Day - 1, i = 0; i < Month; i++)
609 Julian += DaysInMonth[i];
610 for (i = EPOCH; i < Year; i++)
611 Julian += 365 + (i % 4 == 0);
612 Julian *= SECSPERDAY;
613 Julian += yyTimezone * 60L;
614 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
618 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
625 DSTcorrect(time_t Start, time_t Future)
630 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
631 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
632 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
637 RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
643 tm = localtime(&now);
644 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
645 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
646 return DSTcorrect(Start, now);
651 RelativeMonth(time_t Start, time_t RelMonth)
659 tm = localtime(&Start);
660 Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
662 Month = Month % 12 + 1;
663 return DSTcorrect(Start,
664 Convert(Month, (time_t)tm->tm_mday, Year,
665 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
671 LookupWord(char *buff)
679 /* Make it lowercase. */
680 for (p = buff; *p; p++)
684 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
685 yylval.Meridian = MERam;
688 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
689 yylval.Meridian = MERpm;
693 /* See if we have an abbreviation for a month. */
694 if (strlen(buff) == 3)
696 else if (strlen(buff) == 4 && buff[3] == '.') {
703 for (tp = MonthDayTable; tp->name; tp++) {
705 if (strncmp(buff, tp->name, 3) == 0) {
706 yylval.Number = tp->value;
710 else if (strcmp(buff, tp->name) == 0) {
711 yylval.Number = tp->value;
716 for (tp = TimezoneTable; tp->name; tp++)
717 if (strcmp(buff, tp->name) == 0) {
718 yylval.Number = tp->value;
722 if (strcmp(buff, "dst") == 0)
725 for (tp = UnitsTable; tp->name; tp++)
726 if (strcmp(buff, tp->name) == 0) {
727 yylval.Number = tp->value;
731 /* Strip off any plural and try the units table again. */
732 i = strlen(buff) - 1;
733 if (buff[i] == 's') {
735 for (tp = UnitsTable; tp->name; tp++)
736 if (strcmp(buff, tp->name) == 0) {
737 yylval.Number = tp->value;
740 buff[i] = 's'; /* Put back for "this" in OtherTable. */
743 for (tp = OtherTable; tp->name; tp++)
744 if (strcmp(buff, tp->name) == 0) {
745 yylval.Number = tp->value;
749 /* Military timezones. */
750 if (buff[1] == '\0' && isalpha(*buff)) {
751 for (tp = MilitaryTable; tp->name; tp++)
752 if (strcmp(buff, tp->name) == 0) {
753 yylval.Number = tp->value;
758 /* Drop out any periods and try the timezone table again. */
759 for (i = 0, p = q = buff; *q; q++)
766 for (tp = TimezoneTable; tp->name; tp++)
767 if (strcmp(buff, tp->name) == 0) {
768 yylval.Number = tp->value;
786 while (isspace(*yyInput))
789 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
790 if (c == '-' || c == '+') {
791 sign = c == '-' ? -1 : 1;
792 if (!isdigit(*++yyInput))
793 /* skip the '-' sign */
798 for (yylval.Number = 0; isdigit(c = *yyInput++); )
799 yylval.Number = 10 * yylval.Number + c - '0';
802 yylval.Number = -yylval.Number;
803 return sign ? tSNUMBER : tUNUMBER;
806 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
807 if (p < &buff[sizeof buff - 1])
811 return LookupWord(buff);
828 #define TM_YEAR_ORIGIN 1900
830 /* Yield A - B, measured in seconds. */
832 difftm (struct tm *a, struct tm *b)
834 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
835 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
837 /* difference in day of year */
838 a->tm_yday - b->tm_yday
839 /* + intervening leap days */
840 + ((ay >> 2) - (by >> 2))
842 + ((ay/100 >> 2) - (by/100 >> 2))
843 /* + difference in years * 365 */
844 + (long)(ay-by) * 365
846 return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
847 + (a->tm_min - b->tm_min))
848 + (a->tm_sec - b->tm_sec));
852 get_date(char *p, struct timeb *now)
860 bzero (&gmt, sizeof(struct tm));
866 (void)time (&nowtime);
868 gmt_ptr = gmtime (&nowtime);
871 /* Make a copy, in case localtime modifies *tm (I think
872 that comment now applies to *gmt_ptr, but I am too
873 lazy to dig into how gmtime and locatime allocate the
874 structures they return pointers to). */
878 if (! (tm = localtime (&nowtime)))
882 ftz.timezone = difftm (&gmt, tm) / 60;
884 /* We are on a system like VMS, where the system clock is
885 in local time and the system has no concept of timezones.
886 Hopefully we can fake this out (for the case in which the
887 user specifies no timezone) by just saying the timezone
899 tm = localtime(&nowtime);
900 yyYear = tm->tm_year + 1900;
901 yyMonth = tm->tm_mon + 1;
903 yyTimezone = now->timezone;
904 yyDSTmode = DSTmaybe;
918 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
921 if (yyHaveDate || yyHaveTime || yyHaveDay) {
922 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
923 yyMeridian, yyDSTmode);
930 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
933 Start += yyRelSeconds;
934 Start += RelativeMonth(Start, yyRelMonth);
936 if (yyHaveDay && !yyHaveDate) {
937 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
941 /* Have to do *something* with a legitimate -1 so it's distinguishable
942 * from the error return value. (Alternately could set errno on error.) */
943 return Start == -1 ? 0 : Start;
951 main(int ac, char *av[])
956 (void)printf("Enter date, or blank line to exit.\n\t> ");
957 (void)fflush(stdout);
958 while (gets(buff) && buff[0]) {
959 d = get_date(buff, (struct timeb *)NULL);
961 (void)printf("Bad format - couldn't convert.\n");
963 (void)printf("%s", ctime(&d));
964 (void)printf("\t> ");
965 (void)fflush(stdout);
970 #endif /* defined(TEST) */