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 /* $FreeBSD: src/usr.bin/find/getdate.y,v 1.8 2012/05/22 16:33:10 bapt Exp $ */
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 #endif /* !defined(vms) */
32 #if defined (__STDC__) || defined (USG)
36 /* Some old versions of bison generate parsers that use bcopy.
37 That loses on systems that don't provide the function, so we have
38 to redefine it here. */
39 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
40 #define bcopy(from, to, len) memcpy ((to), (from), (len))
43 #if defined (__STDC__)
47 /* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
50 We don't want to mess with all the portability hassles of alloca.
51 In particular, most (all?) versions of bison will use alloca in
52 their parser. If bison works on your system (e.g. it should work
53 with gcc), then go ahead and use it, but the more general solution
54 is to use byacc instead of bison, which should generate a portable
55 parser. I played with adding "#define alloca dont_use_alloca", to
56 give an error if the parser generator uses alloca (and thus detect
57 unportable getdate.c's), but that seems to cause as many problems
62 #define yyparse getdate_yyparse
63 #define yylex getdate_yylex
64 #define yyerror getdate_yyerror
66 static int yyparse(void);
67 static int yylex(void);
68 static int yyerror(const char *);
70 time_t get_date(char *);
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) },
539 yyerror(const char *s __unused)
546 ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
548 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
552 if (Hours < 0 || Hours > 23)
554 return (Hours * 60L + Minutes) * 60L + Seconds;
556 if (Hours < 1 || Hours > 12)
560 return (Hours * 60L + Minutes) * 60L + Seconds;
562 if (Hours < 1 || Hours > 12)
566 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
575 * A negative number, which means to use its absolute value (why?)
576 * A number from 0 to 99, which means a year from 1900 to 1999, or
577 * The actual year (>=100). */
579 Convert(time_t Month, time_t Day, time_t Year,
580 time_t Hours, time_t Minutes, time_t Seconds,
581 MERIDIAN Meridian, DSTMODE DSTmode)
583 static int DaysInMonth[12] = {
584 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
596 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
598 /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
599 I'm too lazy to try to check for time_t overflow in another way. */
600 if (Year < EPOCH || Year > 2038
601 || Month < 1 || Month > 12
602 /* Lint fluff: "conversion from long may lose accuracy" */
603 || Day < 1 || Day > DaysInMonth[(int)--Month])
606 for (Julian = Day - 1, i = 0; i < Month; i++)
607 Julian += DaysInMonth[i];
608 for (i = EPOCH; i < Year; i++)
609 Julian += 365 + (i % 4 == 0);
610 Julian *= SECSPERDAY;
611 Julian += yyTimezone * 60L;
612 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
616 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
623 DSTcorrect(time_t Start, time_t Future)
628 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
629 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
630 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
635 RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
641 tm = localtime(&now);
642 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
643 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
644 return DSTcorrect(Start, now);
649 RelativeMonth(time_t Start, time_t RelMonth)
657 tm = localtime(&Start);
658 Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
660 Month = Month % 12 + 1;
661 return DSTcorrect(Start,
662 Convert(Month, (time_t)tm->tm_mday, Year,
663 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
669 LookupWord(char *buff)
677 /* Make it lowercase. */
678 for (p = buff; *p; p++)
682 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
683 yylval.Meridian = MERam;
686 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
687 yylval.Meridian = MERpm;
691 /* See if we have an abbreviation for a month. */
692 if (strlen(buff) == 3)
694 else if (strlen(buff) == 4 && buff[3] == '.') {
701 for (tp = MonthDayTable; tp->name; tp++) {
703 if (strncmp(buff, tp->name, 3) == 0) {
704 yylval.Number = tp->value;
708 else if (strcmp(buff, tp->name) == 0) {
709 yylval.Number = tp->value;
714 for (tp = TimezoneTable; tp->name; tp++)
715 if (strcmp(buff, tp->name) == 0) {
716 yylval.Number = tp->value;
720 if (strcmp(buff, "dst") == 0)
723 for (tp = UnitsTable; tp->name; tp++)
724 if (strcmp(buff, tp->name) == 0) {
725 yylval.Number = tp->value;
729 /* Strip off any plural and try the units table again. */
730 i = strlen(buff) - 1;
731 if (buff[i] == 's') {
733 for (tp = UnitsTable; tp->name; tp++)
734 if (strcmp(buff, tp->name) == 0) {
735 yylval.Number = tp->value;
738 buff[i] = 's'; /* Put back for "this" in OtherTable. */
741 for (tp = OtherTable; tp->name; tp++)
742 if (strcmp(buff, tp->name) == 0) {
743 yylval.Number = tp->value;
747 /* Military timezones. */
748 if (buff[1] == '\0' && isalpha(*buff)) {
749 for (tp = MilitaryTable; tp->name; tp++)
750 if (strcmp(buff, tp->name) == 0) {
751 yylval.Number = tp->value;
756 /* Drop out any periods and try the timezone table again. */
757 for (i = 0, p = q = buff; *q; q++)
764 for (tp = TimezoneTable; tp->name; tp++)
765 if (strcmp(buff, tp->name) == 0) {
766 yylval.Number = tp->value;
784 while (isspace(*yyInput))
787 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
788 if (c == '-' || c == '+') {
789 sign = c == '-' ? -1 : 1;
790 if (!isdigit(*++yyInput))
791 /* skip the '-' sign */
796 for (yylval.Number = 0; isdigit(c = *yyInput++); )
797 yylval.Number = 10 * yylval.Number + c - '0';
800 yylval.Number = -yylval.Number;
801 return sign ? tSNUMBER : tUNUMBER;
804 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
805 if (p < &buff[sizeof buff - 1])
809 return LookupWord(buff);
826 #define TM_YEAR_ORIGIN 1900
828 /* Yield A - B, measured in seconds. */
830 difftm (struct tm *a, struct tm *b)
832 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
833 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
835 /* difference in day of year */
836 a->tm_yday - b->tm_yday
837 /* + intervening leap days */
838 + ((ay >> 2) - (by >> 2))
840 + ((ay/100 >> 2) - (by/100 >> 2))
841 /* + difference in years * 365 */
842 + (long)(ay-by) * 365
844 return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
845 + (a->tm_min - b->tm_min))
846 + (a->tm_sec - b->tm_sec));
852 struct tm *tm, *gmt_ptr, gmt;
858 bzero (&gmt, sizeof(struct tm));
861 (void)time (&nowtime);
863 gmt_ptr = gmtime (&nowtime);
866 /* Make a copy, in case localtime modifies *tm (I think
867 that comment now applies to *gmt_ptr, but I am too
868 lazy to dig into how gmtime and locatime allocate the
869 structures they return pointers to). */
873 if (! (tm = localtime (&nowtime)))
877 tzoff = difftm (&gmt, tm) / 60;
879 /* We are on a system like VMS, where the system clock is
880 in local time and the system has no concept of timezones.
881 Hopefully we can fake this out (for the case in which the
882 user specifies no timezone) by just saying the timezone
889 tm = localtime(&nowtime);
890 yyYear = tm->tm_year + 1900;
891 yyMonth = tm->tm_mon + 1;
894 yyDSTmode = DSTmaybe;
908 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
911 if (yyHaveDate || yyHaveTime || yyHaveDay) {
912 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
913 yyMeridian, yyDSTmode);
920 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
923 Start += yyRelSeconds;
924 Start += RelativeMonth(Start, yyRelMonth);
926 if (yyHaveDay && !yyHaveDate) {
927 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
931 /* Have to do *something* with a legitimate -1 so it's distinguishable
932 * from the error return value. (Alternately could set errno on error.) */
933 return Start == -1 ? 0 : Start;
941 main(int ac, char *av[])
946 (void)printf("Enter date, or blank line to exit.\n\t> ");
947 (void)fflush(stdout);
948 while (gets(buff) && buff[0]) {
951 (void)printf("Bad format - couldn't convert.\n");
953 (void)printf("%s", ctime(&d));
954 (void)printf("\t> ");
955 (void)fflush(stdout);
960 #endif /* defined(TEST) */