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. */
530 static TABLE const TimezoneTable[] = {
531 { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
532 { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
533 { "utc", tZONE, HOUR ( 0) },
534 { "wet", tZONE, HOUR ( 0) }, /* Western European */
535 { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
536 { "wat", tZONE, HOUR ( 1) }, /* West Africa */
537 { "at", tZONE, HOUR ( 2) }, /* Azores */
539 /* For completeness. BST is also British Summer, and GST is
540 * also Guam Standard. */
541 { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */
542 { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */
545 { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */
546 { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */
547 { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */
549 { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
550 { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
551 { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
552 { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
553 { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
554 { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
555 { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
556 { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
557 { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
558 { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
559 { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */
560 { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */
561 { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
562 { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */
563 { "cat", tZONE, HOUR (10) }, /* Central Alaska */
564 { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */
565 { "nt", tZONE, HOUR (11) }, /* Nome */
566 { "idlw", tZONE, HOUR (12) }, /* International Date Line West */
567 { "cet", tZONE, -HOUR (1) }, /* Central European */
568 { "met", tZONE, -HOUR (1) }, /* Middle European */
569 { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */
570 { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
571 { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
572 { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */
573 { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */
574 { "fwt", tZONE, -HOUR (1) }, /* French Winter */
575 { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */
576 { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
577 { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */
579 { "it", tZONE, -HOUR (3.5) },/* Iran */
581 { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */
582 { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */
584 { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */
586 { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */
588 /* For completeness. NST is also Newfoundland Standard, and SST is
589 * also Swedish Summer. */
590 { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */
591 { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */
593 { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */
594 { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */
596 { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */
598 { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */
599 { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
601 { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */
602 { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */
604 { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */
605 { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */
606 { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */
607 { "nzt", tZONE, -HOUR (12) }, /* New Zealand */
608 { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
609 { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
610 { "idle", tZONE, -HOUR (12) }, /* International Date Line East */
614 /* Military timezone table. */
615 static TABLE const MilitaryTable[] = {
616 { "a", tZONE, HOUR ( 1) },
617 { "b", tZONE, HOUR ( 2) },
618 { "c", tZONE, HOUR ( 3) },
619 { "d", tZONE, HOUR ( 4) },
620 { "e", tZONE, HOUR ( 5) },
621 { "f", tZONE, HOUR ( 6) },
622 { "g", tZONE, HOUR ( 7) },
623 { "h", tZONE, HOUR ( 8) },
624 { "i", tZONE, HOUR ( 9) },
625 { "k", tZONE, HOUR ( 10) },
626 { "l", tZONE, HOUR ( 11) },
627 { "m", tZONE, HOUR ( 12) },
628 { "n", tZONE, HOUR (- 1) },
629 { "o", tZONE, HOUR (- 2) },
630 { "p", tZONE, HOUR (- 3) },
631 { "q", tZONE, HOUR (- 4) },
632 { "r", tZONE, HOUR (- 5) },
633 { "s", tZONE, HOUR (- 6) },
634 { "t", tZONE, HOUR (- 7) },
635 { "u", tZONE, HOUR (- 8) },
636 { "v", tZONE, HOUR (- 9) },
637 { "w", tZONE, HOUR (-10) },
638 { "x", tZONE, HOUR (-11) },
639 { "y", tZONE, HOUR (-12) },
640 { "z", tZONE, HOUR ( 0) },
650 char *s ATTRIBUTE_UNUSED;
656 ToHour (Hours, Meridian)
663 if (Hours < 0 || Hours > 23)
667 if (Hours < 1 || Hours > 12)
673 if (Hours < 1 || Hours > 12)
691 /* XPG4 suggests that years 00-68 map to 2000-2068, and
692 years 69-99 map to 1969-1999. */
707 register const TABLE *tp;
711 /* Make it lowercase. */
712 for (p = buff; *p; p++)
713 if (ISUPPER ((unsigned char) *p))
716 if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
718 yylval.Meridian = MERam;
721 if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
723 yylval.Meridian = MERpm;
727 /* See if we have an abbreviation for a month. */
728 if (strlen (buff) == 3)
730 else if (strlen (buff) == 4 && buff[3] == '.')
738 for (tp = MonthDayTable; tp->name; tp++)
742 if (strncmp (buff, tp->name, 3) == 0)
744 yylval.Number = tp->value;
748 else if (strcmp (buff, tp->name) == 0)
750 yylval.Number = tp->value;
755 for (tp = TimezoneTable; tp->name; tp++)
756 if (strcmp (buff, tp->name) == 0)
758 yylval.Number = tp->value;
762 if (strcmp (buff, "dst") == 0)
765 for (tp = UnitsTable; tp->name; tp++)
766 if (strcmp (buff, tp->name) == 0)
768 yylval.Number = tp->value;
772 /* Strip off any plural and try the units table again. */
773 i = strlen (buff) - 1;
777 for (tp = UnitsTable; tp->name; tp++)
778 if (strcmp (buff, tp->name) == 0)
780 yylval.Number = tp->value;
783 buff[i] = 's'; /* Put back for "this" in OtherTable. */
786 for (tp = OtherTable; tp->name; tp++)
787 if (strcmp (buff, tp->name) == 0)
789 yylval.Number = tp->value;
793 /* Military timezones. */
794 if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff))
796 for (tp = MilitaryTable; tp->name; tp++)
797 if (strcmp (buff, tp->name) == 0)
799 yylval.Number = tp->value;
804 /* Drop out any periods and try the timezone table again. */
805 for (i = 0, p = q = buff; *q; q++)
812 for (tp = TimezoneTable; tp->name; tp++)
813 if (strcmp (buff, tp->name) == 0)
815 yylval.Number = tp->value;
825 register unsigned char c;
833 while (ISSPACE ((unsigned char) *yyInput))
836 if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
838 if (c == '-' || c == '+')
840 sign = c == '-' ? -1 : 1;
841 if (!ISDIGIT (*++yyInput))
842 /* skip the '-' sign */
847 for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
848 yylval.Number = 10 * yylval.Number + c - '0';
851 yylval.Number = -yylval.Number;
852 return sign ? tSNUMBER : tUNUMBER;
856 for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
857 if (p < &buff[sizeof buff - 1])
861 return LookupWord (buff);
880 #define TM_YEAR_ORIGIN 1900
882 /* Yield A - B, measured in seconds. */
884 difftm (struct tm *a, struct tm *b)
886 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
887 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
889 /* difference in day of year */
890 a->tm_yday - b->tm_yday
891 /* + intervening leap days */
892 + ((ay >> 2) - (by >> 2))
893 - (ay / 100 - by / 100)
894 + ((ay / 100 >> 2) - (by / 100 >> 2))
895 /* + difference in years * 365 */
896 + (long) (ay - by) * 365
898 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
899 + (a->tm_min - b->tm_min))
900 + (a->tm_sec - b->tm_sec));
904 get_date (const char *p, const time_t *now)
906 struct tm tm, tm0, *tmp;
910 Start = now ? *now : time ((time_t *) NULL);
911 tmp = localtime (&Start);
914 yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
915 yyMonth = tmp->tm_mon + 1;
916 yyDay = tmp->tm_mday;
917 yyHour = tmp->tm_hour;
918 yyMinutes = tmp->tm_min;
919 yySeconds = tmp->tm_sec;
920 tm.tm_isdst = tmp->tm_isdst;
935 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
938 tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
939 tm.tm_mon = yyMonth - 1 + yyRelMonth;
940 tm.tm_mday = yyDay + yyRelDay;
941 if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
943 tm.tm_hour = ToHour (yyHour, yyMeridian);
946 tm.tm_min = yyMinutes;
947 tm.tm_sec = yySeconds;
951 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
953 tm.tm_hour += yyRelHour;
954 tm.tm_min += yyRelMinutes;
955 tm.tm_sec += yyRelSeconds;
957 /* Let mktime deduce tm_isdst if we have an absolute timestamp,
958 or if the relative timestamp mentions days, months, or years. */
959 if (yyHaveDate | yyHaveDay | yyHaveTime | yyRelDay | yyRelMonth | yyRelYear)
964 Start = mktime (&tm);
966 if (Start == (time_t) -1)
969 /* Guard against falsely reporting errors near the time_t boundaries
970 when parsing times in other time zones. For example, if the min
971 time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
972 of UTC, then the min localtime value is 1970-01-01 08:00:00; if
973 we apply mktime to 1970-01-01 00:00:00 we will get an error, so
974 we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
975 zone by 24 hours to compensate. This algorithm assumes that
976 there is no DST transition within a day of the time_t boundaries. */
980 if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
983 yyTimezone -= 24 * 60;
988 yyTimezone += 24 * 60;
990 Start = mktime (&tm);
993 if (Start == (time_t) -1)
997 if (yyHaveDay && !yyHaveDate)
999 tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
1000 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
1001 Start = mktime (&tm);
1002 if (Start == (time_t) -1)
1009 struct tm *gmt = gmtime (&Start);
1012 delta = yyTimezone * 60L + difftm (&tm, gmt);
1013 if ((Start + delta < Start) != (delta < 0))
1014 return -1; /* time_t overflow */
1029 char buff[MAX_BUFF_LEN + 1];
1032 (void) printf ("Enter date, or blank line to exit.\n\t> ");
1033 (void) fflush (stdout);
1035 buff[MAX_BUFF_LEN] = 0;
1036 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
1038 d = get_date (buff, (time_t *) NULL);
1040 (void) printf ("Bad format - couldn't convert.\n");
1042 (void) printf ("%s", ctime (&d));
1043 (void) printf ("\t> ");
1044 (void) fflush (stdout);
1049 #endif /* defined (TEST) */