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 code is in the public domain and has no copyright.
18 /* Since the code of getdate.y is not included in the Emacs executable
19 itself, there is no need to #define static in this file. Even if
20 the code were included in the Emacs executable, it probably
21 wouldn't do any harm to #undef it here; this will only cause
22 problems if we try to write to a static variable, which I don't
23 think this code needs to do. */
32 # include <stdlib.h> /* for `free'; used by Bison 1.27 */
35 #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
36 # define IN_CTYPE_DOMAIN(c) 1
38 # define IN_CTYPE_DOMAIN(c) isascii(c)
41 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
42 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
43 #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
44 #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
46 /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
47 - Its arg may be any int or unsigned int; it need not be an unsigned char.
48 - It's guaranteed to evaluate its argument exactly once.
49 - It's typically faster.
50 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
51 only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless
52 it's important to use the locale's definition of `digit' even when the
53 host does not conform to Posix. */
54 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
56 #if defined (STDC_HEADERS) || defined (USG)
60 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
61 # define __attribute__(x)
64 #ifndef ATTRIBUTE_UNUSED
65 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
68 /* Some old versions of bison generate parsers that use bcopy.
69 That loses on systems that don't provide the function, so we have
70 to redefine it here. */
71 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
72 # define bcopy(from, to, len) memcpy ((to), (from), (len))
75 /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
76 as well as gratuitiously global symbol names, so we can have multiple
77 yacc generated parsers in the same program. Note that these are only
78 the variables produced by yacc. If other parser generators (bison,
79 byacc, etc) produce additional global names that conflict at link time,
80 then those parser generators need to be fixed instead of adding those
81 names to this list. */
83 #define yymaxdepth gd_maxdepth
84 #define yyparse gd_parse
86 #define yyerror gd_error
87 #define yylval gd_lval
88 #define yychar gd_char
89 #define yydebug gd_debug
90 #define yypact gd_pact
97 #define yyexca gd_exca
98 #define yyerrflag gd_errflag
99 #define yynerrs gd_nerrs
103 #define yy_yys gd_yys
104 #define yystate gd_state
107 #define yy_yyv gd_yyv
109 #define yylloc gd_lloc
110 #define yyreds gd_reds /* With YYDEBUG defined */
111 #define yytoks gd_toks /* With YYDEBUG defined */
112 #define yylhs gd_yylhs
113 #define yylen gd_yylen
114 #define yydefred gd_yydefred
115 #define yydgoto gd_yydgoto
116 #define yysindex gd_yysindex
117 #define yyrindex gd_yyrindex
118 #define yygindex gd_yygindex
119 #define yytable gd_yytable
120 #define yycheck gd_yycheck
123 static int yyerror ();
126 #define HOUR(x) ((x) * 60)
128 #define MAX_BUFF_LEN 128 /* size of buffer to read the date into */
131 ** An entry in the lexical lookup table.
133 typedef struct _TABLE {
141 ** Meridian: am, pm, or 24-hour style.
143 typedef enum _MERIDIAN {
149 ** Global variables. We could get rid of most of these by using a good
150 ** union as the yacc stack. (This routine was originally written before
151 ** yacc had the %union construct.) Maybe someday; right now we only use
152 ** the %union very rarely.
154 static const char *yyInput;
155 static int yyDayOrdinal;
156 static int yyDayNumber;
157 static int yyHaveDate;
158 static int yyHaveDay;
159 static int yyHaveRel;
160 static int yyHaveTime;
161 static int yyHaveZone;
162 static int yyTimezone;
165 static int yyMinutes;
167 static int yySeconds;
169 static MERIDIAN yyMeridian;
171 static int yyRelHour;
172 static int yyRelMinutes;
173 static int yyRelMonth;
174 static int yyRelSeconds;
175 static int yyRelYear;
179 /* This grammar has 13 shift/reduce conflicts. */
184 enum _MERIDIAN Meridian;
187 %token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID
188 %token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
189 %token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
191 %type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
192 %type <Number> tMONTH tMONTH_UNIT
193 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
194 %type <Meridian> tMERIDIAN o_merid
220 time : tUNUMBER tMERIDIAN {
226 | tUNUMBER ':' tUNUMBER o_merid {
232 | tUNUMBER ':' tUNUMBER tSNUMBER {
238 ? -$4 % 100 + (-$4 / 100) * 60
239 : - ($4 % 100 + ($4 / 100) * 60));
241 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
247 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
254 ? -$6 % 100 + (-$6 / 100) * 60
255 : - ($6 % 100 + ($6 / 100) * 60));
263 yyTimezone = $1 - 60;
267 yyTimezone = $1 - 60;
285 date : tUNUMBER '/' tUNUMBER {
289 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
290 /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
291 The goal in recognizing YYYY/MM/DD is solely to support legacy
292 machine-generated dates like those in an RCS log listing. If
293 you want portability, use the ISO 8601 format. */
307 | tUNUMBER tSNUMBER tSNUMBER {
308 /* ISO 8601 format. yyyy-mm-dd. */
313 | tUNUMBER tMONTH tSNUMBER {
314 /* e.g. 17-JUN-1992. */
323 | tMONTH tUNUMBER ',' tUNUMBER {
332 | tUNUMBER tMONTH tUNUMBER {
340 yyRelSeconds = -yyRelSeconds;
341 yyRelMinutes = -yyRelMinutes;
342 yyRelHour = -yyRelHour;
343 yyRelDay = -yyRelDay;
344 yyRelMonth = -yyRelMonth;
345 yyRelYear = -yyRelYear;
350 relunit : tUNUMBER tYEAR_UNIT {
351 yyRelYear += $1 * $2;
353 | tSNUMBER tYEAR_UNIT {
354 yyRelYear += $1 * $2;
359 | tUNUMBER tMONTH_UNIT {
360 yyRelMonth += $1 * $2;
362 | tSNUMBER tMONTH_UNIT {
363 yyRelMonth += $1 * $2;
368 | tUNUMBER tDAY_UNIT {
371 | tSNUMBER tDAY_UNIT {
377 | tUNUMBER tHOUR_UNIT {
378 yyRelHour += $1 * $2;
380 | tSNUMBER tHOUR_UNIT {
381 yyRelHour += $1 * $2;
386 | tUNUMBER tMINUTE_UNIT {
387 yyRelMinutes += $1 * $2;
389 | tSNUMBER tMINUTE_UNIT {
390 yyRelMinutes += $1 * $2;
395 | tUNUMBER tSEC_UNIT {
396 yyRelSeconds += $1 * $2;
398 | tSNUMBER tSEC_UNIT {
399 yyRelSeconds += $1 * $2;
408 if (yyHaveTime && yyHaveDate && !yyHaveRel)
416 yyMonth= ($1/100)%100;
430 yyMinutes = $1 % 100;
451 /* Include this file down here because bison inserts code above which
452 may define-away `const'. We want the prototype for get_date to have
453 the same signature as the function definition does. */
456 extern struct tm *gmtime ();
457 extern struct tm *localtime ();
458 extern time_t mktime ();
460 /* Month and day table. */
461 static TABLE const MonthDayTable[] = {
462 { "january", tMONTH, 1 },
463 { "february", tMONTH, 2 },
464 { "march", tMONTH, 3 },
465 { "april", tMONTH, 4 },
466 { "may", tMONTH, 5 },
467 { "june", tMONTH, 6 },
468 { "july", tMONTH, 7 },
469 { "august", tMONTH, 8 },
470 { "september", tMONTH, 9 },
471 { "sept", tMONTH, 9 },
472 { "october", tMONTH, 10 },
473 { "november", tMONTH, 11 },
474 { "december", tMONTH, 12 },
475 { "sunday", tDAY, 0 },
476 { "monday", tDAY, 1 },
477 { "tuesday", tDAY, 2 },
479 { "wednesday", tDAY, 3 },
480 { "wednes", tDAY, 3 },
481 { "thursday", tDAY, 4 },
483 { "thurs", tDAY, 4 },
484 { "friday", tDAY, 5 },
485 { "saturday", tDAY, 6 },
489 /* Time units table. */
490 static TABLE const UnitsTable[] = {
491 { "year", tYEAR_UNIT, 1 },
492 { "month", tMONTH_UNIT, 1 },
493 { "fortnight", tDAY_UNIT, 14 },
494 { "week", tDAY_UNIT, 7 },
495 { "day", tDAY_UNIT, 1 },
496 { "hour", tHOUR_UNIT, 1 },
497 { "minute", tMINUTE_UNIT, 1 },
498 { "min", tMINUTE_UNIT, 1 },
499 { "second", tSEC_UNIT, 1 },
500 { "sec", tSEC_UNIT, 1 },
504 /* Assorted relative-time words. */
505 static TABLE const OtherTable[] = {
506 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
507 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
508 { "today", tMINUTE_UNIT, 0 },
509 { "now", tMINUTE_UNIT, 0 },
510 { "last", tUNUMBER, -1 },
511 { "this", tMINUTE_UNIT, 0 },
512 { "next", tUNUMBER, 1 },
513 { "first", tUNUMBER, 1 },
514 /* { "second", tUNUMBER, 2 }, */
515 { "third", tUNUMBER, 3 },
516 { "fourth", tUNUMBER, 4 },
517 { "fifth", tUNUMBER, 5 },
518 { "sixth", tUNUMBER, 6 },
519 { "seventh", tUNUMBER, 7 },
520 { "eighth", tUNUMBER, 8 },
521 { "ninth", tUNUMBER, 9 },
522 { "tenth", tUNUMBER, 10 },
523 { "eleventh", tUNUMBER, 11 },
524 { "twelfth", tUNUMBER, 12 },
529 /* The timezone table. This table is necessarily incomplete, as time
530 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
531 as Eastern time in Australia, not as US Eastern Standard Time.
532 You cannot rely on getdate to handle arbitrary time zone
533 abbreviations; use numeric abbreviations like `-0500' instead. */
534 static TABLE const TimezoneTable[] = {
535 { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
536 { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
537 { "utc", tZONE, HOUR ( 0) },
538 { "wet", tZONE, HOUR ( 0) }, /* Western European */
539 { "west", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
540 { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
541 { "art", tZONE, HOUR ( 3) }, /* Argentina */
542 { "brt", tZONE, HOUR ( 3) }, /* Brazil */
543 { "brst", tDAYZONE, HOUR ( 3) }, /* Brazil Summer */
544 { "nst", tZONE, HOUR ( 3) + 30 }, /* Newfoundland Standard */
545 { "ndt", tDAYZONE, HOUR ( 3) + 30 }, /* Newfoundland Daylight */
546 { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
547 { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
548 { "clt", tZONE, HOUR ( 4) }, /* Chile */
549 { "clst", tDAYZONE, HOUR ( 4) }, /* Chile Summer */
550 { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
551 { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
552 { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
553 { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
554 { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
555 { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
556 { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
557 { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
558 { "akst", tZONE, HOUR ( 9) }, /* Alaska Standard */
559 { "akdt", tDAYZONE, HOUR ( 9) }, /* Alaska Daylight */
560 { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
561 { "hast", tZONE, HOUR (10) }, /* Hawaii-Aleutian Standard */
562 { "hadt", tDAYZONE, HOUR (10) }, /* Hawaii-Aleutian Daylight */
563 { "sst", tZONE, HOUR (12) }, /* Samoa Standard */
564 { "wat", tZONE, -HOUR (1) }, /* West Africa */
565 { "cet", tZONE, -HOUR (1) }, /* Central European */
566 { "cest", tDAYZONE, -HOUR (1) }, /* Central European Summer */
567 { "met", tZONE, -HOUR (1) }, /* Middle European */
568 { "mez", tZONE, -HOUR (1) }, /* Middle European */
569 { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
570 { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
571 { "eet", tZONE, -HOUR (2) }, /* Eastern European */
572 { "eest", tDAYZONE, -HOUR (2) }, /* Eastern European Summer */
573 { "cat", tZONE, -HOUR (2) }, /* Central Africa */
574 { "sast", tZONE, -HOUR (2) }, /* South Africa Standard */
575 { "eat", tZONE, -HOUR (3) }, /* East Africa */
576 { "msk", tZONE, -HOUR (3) }, /* Moscow */
577 { "msd", tDAYZONE, -HOUR (3) }, /* Moscow Daylight */
578 { "ist", tZONE, - (HOUR (5) + 30) }, /* India Standard */
579 { "sgt", tZONE, -HOUR (8) }, /* Singapore */
580 { "kst", tZONE, -HOUR (9) }, /* Korea Standard */
581 { "jst", tZONE, -HOUR (9) }, /* Japan Standard */
582 { "gst", tZONE, -HOUR (10) }, /* Guam Standard */
583 { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
584 { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
588 /* Military timezone table. */
589 static TABLE const MilitaryTable[] = {
590 { "a", tZONE, HOUR ( 1) },
591 { "b", tZONE, HOUR ( 2) },
592 { "c", tZONE, HOUR ( 3) },
593 { "d", tZONE, HOUR ( 4) },
594 { "e", tZONE, HOUR ( 5) },
595 { "f", tZONE, HOUR ( 6) },
596 { "g", tZONE, HOUR ( 7) },
597 { "h", tZONE, HOUR ( 8) },
598 { "i", tZONE, HOUR ( 9) },
599 { "k", tZONE, HOUR ( 10) },
600 { "l", tZONE, HOUR ( 11) },
601 { "m", tZONE, HOUR ( 12) },
602 { "n", tZONE, HOUR (- 1) },
603 { "o", tZONE, HOUR (- 2) },
604 { "p", tZONE, HOUR (- 3) },
605 { "q", tZONE, HOUR (- 4) },
606 { "r", tZONE, HOUR (- 5) },
607 { "s", tZONE, HOUR (- 6) },
608 { "t", tZONE, HOUR (- 7) },
609 { "u", tZONE, HOUR (- 8) },
610 { "v", tZONE, HOUR (- 9) },
611 { "w", tZONE, HOUR (-10) },
612 { "x", tZONE, HOUR (-11) },
613 { "y", tZONE, HOUR (-12) },
614 { "z", tZONE, HOUR ( 0) },
624 char *s ATTRIBUTE_UNUSED;
630 ToHour (Hours, Meridian)
637 if (Hours < 0 || Hours > 23)
641 if (Hours < 1 || Hours > 12)
647 if (Hours < 1 || Hours > 12)
665 /* XPG4 suggests that years 00-68 map to 2000-2068, and
666 years 69-99 map to 1969-1999. */
681 register const TABLE *tp;
685 /* Make it lowercase. */
686 for (p = buff; *p; p++)
687 if (ISUPPER ((unsigned char) *p))
690 if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
692 yylval.Meridian = MERam;
695 if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
697 yylval.Meridian = MERpm;
701 /* See if we have an abbreviation for a month. */
702 if (strlen (buff) == 3)
704 else if (strlen (buff) == 4 && buff[3] == '.')
712 for (tp = MonthDayTable; tp->name; tp++)
716 if (strncmp (buff, tp->name, 3) == 0)
718 yylval.Number = tp->value;
722 else if (strcmp (buff, tp->name) == 0)
724 yylval.Number = tp->value;
729 for (tp = TimezoneTable; tp->name; tp++)
730 if (strcmp (buff, tp->name) == 0)
732 yylval.Number = tp->value;
736 if (strcmp (buff, "dst") == 0)
739 for (tp = UnitsTable; tp->name; tp++)
740 if (strcmp (buff, tp->name) == 0)
742 yylval.Number = tp->value;
746 /* Strip off any plural and try the units table again. */
747 i = strlen (buff) - 1;
751 for (tp = UnitsTable; tp->name; tp++)
752 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)
763 yylval.Number = tp->value;
767 /* Military timezones. */
768 if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff))
770 for (tp = MilitaryTable; tp->name; tp++)
771 if (strcmp (buff, tp->name) == 0)
773 yylval.Number = tp->value;
778 /* Drop out any periods and try the timezone table again. */
779 for (i = 0, p = q = buff; *q; q++)
786 for (tp = TimezoneTable; tp->name; tp++)
787 if (strcmp (buff, tp->name) == 0)
789 yylval.Number = tp->value;
799 register unsigned char c;
807 while (ISSPACE ((unsigned char) *yyInput))
810 if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
812 if (c == '-' || c == '+')
814 sign = c == '-' ? -1 : 1;
815 if (!ISDIGIT (*++yyInput))
816 /* skip the '-' sign */
821 for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
822 yylval.Number = 10 * yylval.Number + c - '0';
825 yylval.Number = -yylval.Number;
826 return sign ? tSNUMBER : tUNUMBER;
830 for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
831 if (p < &buff[sizeof buff - 1])
835 return LookupWord (buff);
854 #define TM_YEAR_ORIGIN 1900
856 /* Yield A - B, measured in seconds. */
858 difftm (struct tm *a, struct tm *b)
860 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
861 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
863 /* difference in day of year */
864 a->tm_yday - b->tm_yday
865 /* + intervening leap days */
866 + ((ay >> 2) - (by >> 2))
867 - (ay / 100 - by / 100)
868 + ((ay / 100 >> 2) - (by / 100 >> 2))
869 /* + difference in years * 365 */
870 + (long) (ay - by) * 365
872 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
873 + (a->tm_min - b->tm_min))
874 + (a->tm_sec - b->tm_sec));
878 get_date (const char *p, const time_t *now)
880 struct tm tm, tm0, *tmp;
884 Start = now ? *now : time ((time_t *) NULL);
885 tmp = localtime (&Start);
888 yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
889 yyMonth = tmp->tm_mon + 1;
890 yyDay = tmp->tm_mday;
891 yyHour = tmp->tm_hour;
892 yyMinutes = tmp->tm_min;
893 yySeconds = tmp->tm_sec;
894 tm.tm_isdst = tmp->tm_isdst;
909 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
912 tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
913 tm.tm_mon = yyMonth - 1 + yyRelMonth;
914 tm.tm_mday = yyDay + yyRelDay;
915 if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
917 tm.tm_hour = ToHour (yyHour, yyMeridian);
920 tm.tm_min = yyMinutes;
921 tm.tm_sec = yySeconds;
925 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
927 tm.tm_hour += yyRelHour;
928 tm.tm_min += yyRelMinutes;
929 tm.tm_sec += yyRelSeconds;
931 /* Let mktime deduce tm_isdst if we have an absolute timestamp,
932 or if the relative timestamp mentions days, months, or years. */
933 if (yyHaveDate | yyHaveDay | yyHaveTime | yyRelDay | yyRelMonth | yyRelYear)
938 Start = mktime (&tm);
940 if (Start == (time_t) -1)
943 /* Guard against falsely reporting errors near the time_t boundaries
944 when parsing times in other time zones. For example, if the min
945 time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
946 of UTC, then the min localtime value is 1970-01-01 08:00:00; if
947 we apply mktime to 1970-01-01 00:00:00 we will get an error, so
948 we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
949 zone by 24 hours to compensate. This algorithm assumes that
950 there is no DST transition within a day of the time_t boundaries. */
954 if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
957 yyTimezone -= 24 * 60;
962 yyTimezone += 24 * 60;
964 Start = mktime (&tm);
967 if (Start == (time_t) -1)
971 if (yyHaveDay && !yyHaveDate)
973 tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
974 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
975 Start = mktime (&tm);
976 if (Start == (time_t) -1)
983 struct tm *gmt = gmtime (&Start);
986 delta = yyTimezone * 60L + difftm (&tm, gmt);
987 if ((Start + delta < Start) != (delta < 0))
988 return -1; /* time_t overflow */
1003 char buff[MAX_BUFF_LEN + 1];
1006 (void) printf ("Enter date, or blank line to exit.\n\t> ");
1007 (void) fflush (stdout);
1009 buff[MAX_BUFF_LEN] = 0;
1010 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
1012 d = get_date (buff, (time_t *) NULL);
1014 (void) printf ("Bad format - couldn't convert.\n");
1016 (void) printf ("%s", ctime (&d));
1017 (void) printf ("\t> ");
1018 (void) fflush (stdout);
1023 #endif /* defined (TEST) */