2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 /* Originally written by Steven M. Bellovin <smb@research.att.com> while
21 at the University of North Carolina at Chapel Hill. Later tweaked by
22 a couple of people on Usenet. Completely overhauled by Rich $alz
23 <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
25 Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
26 the right thing about local DST. Also modified by Paul Eggert
27 <eggert@cs.ucla.edu> in February 2004 to support
28 nanosecond-resolution time stamps, and in October 2004 to support
29 TZ strings in dates. */
31 /* FIXME: Check for arithmetic overflow in all cases, not just
42 /* There's no need to extend the stack, so there's no need to involve
44 #define YYSTACK_USE_ALLOCA 0
46 /* Tell Bison how much stack space is needed. 20 should be plenty for
47 this grammar, which is not right recursive. Beware setting it too
48 high, since that might cause problems on machines whose
49 implementations have lame stack-overflow checking. */
51 #define YYINITDEPTH YYMAXDEPTH
53 /* Since the code of getdate.y is not included in the Emacs executable
54 itself, there is no need to #define static in this file. Even if
55 the code were included in the Emacs executable, it probably
56 wouldn't do any harm to #undef it here; this will only cause
57 problems if we try to write to a static variable, which I don't
58 think this code needs to do. */
73 /* ISDIGIT differs from isdigit, as follows:
74 - Its arg may be any int or unsigned int; it need not be an unsigned char
76 - It's typically faster.
77 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
78 isdigit unless it's important to use the locale's definition
79 of `digit' even when the host does not conform to POSIX. */
80 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
83 # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
84 # define __attribute__(x)
88 #ifndef ATTRIBUTE_UNUSED
89 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
92 /* Shift A right by B bits portably, by dividing A by 2**B and
93 truncating towards minus infinity. A and B should be free of side
94 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
95 INT_BITS is the number of useful bits in an int. GNU code can
96 assume that INT_BITS is at least 32.
98 ISO C99 says that A >> B is implementation-defined if A < 0. Some
99 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
100 right in the usual way when A < 0, so SHR falls back on division if
101 ordinary A >> B doesn't seem to be the usual signed shift. */
105 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
107 #define EPOCH_YEAR 1970
108 #define TM_YEAR_BASE 1900
110 #define HOUR(x) ((x) * 60)
112 /* Lots of this code assumes time_t and time_t-like values fit into
113 long int. It also assumes that signed integer overflow silently
114 wraps around, but there's no portable way to check for that at
116 verify (TYPE_IS_INTEGER (time_t));
117 verify (LONG_MIN <= TYPE_MINIMUM (time_t) && TYPE_MAXIMUM (time_t) <= LONG_MAX);
119 /* An integer value, and the number of digits in its textual
128 /* An entry in the lexical lookup table. */
136 /* Meridian: am, pm, or 24-hour style. */
137 enum { MERam, MERpm, MER24 };
139 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
141 /* Relative times. */
144 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
154 #if HAVE_COMPOUND_LITERALS
155 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
157 static relative_time const RELATIVE_TIME_0;
160 /* Information passed to and from the parser. */
163 /* The input string remaining to be parsed. */
166 /* N, if this is the Nth Tuesday. */
167 long int day_ordinal;
169 /* Day of week; Sunday is 0. */
172 /* tm_isdst flag for the local zone. */
175 /* Time zone, in minutes east of UTC. */
178 /* Style used for time. */
181 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
187 struct timespec seconds; /* includes nanoseconds */
189 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
192 /* Presence or counts of nonterminals of various flavors parsed so far. */
197 size_t local_zones_seen;
202 /* Table of local time zone abbrevations, terminated by a null entry. */
203 table local_time_zone_table[3];
207 static int yylex (union YYSTYPE *, parser_control *);
208 static int yyerror (parser_control const *, char const *);
209 static long int time_zone_hhmm (textint, long int);
213 /* We want a reentrant parser, even if the TZ manipulation and the calls to
214 localtime and gmtime are not reentrant. */
216 %parse-param { parser_control *pc }
217 %lex-param { parser_control *pc }
219 /* This grammar has 20 shift/reduce conflicts. */
226 struct timespec timespec;
232 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
233 %token <intval> tDAY_UNIT
235 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
236 %token <intval> tMONTH tORDINAL tZONE
238 %token <textintval> tSNUMBER tUNUMBER
239 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
241 %type <intval> o_colon_minutes o_merid
242 %type <timespec> seconds signed_seconds unsigned_seconds
244 %type <rel> relunit relunit_snumber
257 pc->timespec_seen = true;
268 { pc->times_seen++; }
270 { pc->local_zones_seen++; }
272 { pc->zones_seen++; }
274 { pc->dates_seen++; }
278 { pc->rels_seen = true; }
287 pc->seconds.tv_sec = 0;
288 pc->seconds.tv_nsec = 0;
291 | tUNUMBER ':' tUNUMBER o_merid
294 pc->minutes = $3.value;
295 pc->seconds.tv_sec = 0;
296 pc->seconds.tv_nsec = 0;
299 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
302 pc->minutes = $3.value;
303 pc->seconds.tv_sec = 0;
304 pc->seconds.tv_nsec = 0;
305 pc->meridian = MER24;
307 pc->time_zone = time_zone_hhmm ($4, $5);
309 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
312 pc->minutes = $3.value;
316 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
319 pc->minutes = $3.value;
321 pc->meridian = MER24;
323 pc->time_zone = time_zone_hhmm ($6, $7);
330 pc->local_isdst = $1;
331 pc->dsts_seen += (0 < $1);
336 pc->dsts_seen += (0 < $1) + 1;
342 { pc->time_zone = $1; }
343 | tZONE relunit_snumber
344 { pc->time_zone = $1;
346 pc->rel.seconds += $2.seconds;
347 pc->rel.minutes += $2.minutes;
348 pc->rel.hour += $2.hour;
349 pc->rel.day += $2.day;
350 pc->rel.month += $2.month;
351 pc->rel.year += $2.year;
352 pc->rels_seen = true; }
353 | tZONE tSNUMBER o_colon_minutes
354 { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
356 { pc->time_zone = $1 + 60; }
358 { pc->time_zone = $1 + 60; }
374 pc->day_ordinal = $1;
379 pc->day_ordinal = $1.value;
385 tUNUMBER '/' tUNUMBER
387 pc->month = $1.value;
390 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
392 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
393 otherwise as MM/DD/YY.
394 The goal in recognizing YYYY/MM/DD is solely to support legacy
395 machine-generated dates like those in an RCS log listing. If
396 you want portability, use the ISO 8601 format. */
400 pc->month = $3.value;
405 pc->month = $1.value;
410 | tUNUMBER tSNUMBER tSNUMBER
412 /* ISO 8601 format. YYYY-MM-DD. */
414 pc->month = -$2.value;
417 | tUNUMBER tMONTH tSNUMBER
419 /* e.g. 17-JUN-1992. */
422 pc->year.value = -$3.value;
423 pc->year.digits = $3.digits;
425 | tMONTH tSNUMBER tSNUMBER
427 /* e.g. JUN-17-1992. */
430 pc->year.value = -$3.value;
431 pc->year.digits = $3.digits;
438 | tMONTH tUNUMBER ',' tUNUMBER
449 | tUNUMBER tMONTH tUNUMBER
461 pc->rel.seconds -= $1.seconds;
462 pc->rel.minutes -= $1.minutes;
463 pc->rel.hour -= $1.hour;
464 pc->rel.day -= $1.day;
465 pc->rel.month -= $1.month;
466 pc->rel.year -= $1.year;
471 pc->rel.seconds += $1.seconds;
472 pc->rel.minutes += $1.minutes;
473 pc->rel.hour += $1.hour;
474 pc->rel.day += $1.day;
475 pc->rel.month += $1.month;
476 pc->rel.year += $1.year;
482 { $$ = RELATIVE_TIME_0; $$.year = $1; }
483 | tUNUMBER tYEAR_UNIT
484 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
486 { $$ = RELATIVE_TIME_0; $$.year = 1; }
487 | tORDINAL tMONTH_UNIT
488 { $$ = RELATIVE_TIME_0; $$.month = $1; }
489 | tUNUMBER tMONTH_UNIT
490 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
492 { $$ = RELATIVE_TIME_0; $$.month = 1; }
494 { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
496 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
498 { $$ = RELATIVE_TIME_0; $$.day = $1; }
499 | tORDINAL tHOUR_UNIT
500 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
501 | tUNUMBER tHOUR_UNIT
502 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
504 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
505 | tORDINAL tMINUTE_UNIT
506 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
507 | tUNUMBER tMINUTE_UNIT
508 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
510 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
512 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
514 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
515 | tSDECIMAL_NUMBER tSEC_UNIT
516 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
517 | tUDECIMAL_NUMBER tSEC_UNIT
518 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
520 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
526 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
527 | tSNUMBER tMONTH_UNIT
528 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
530 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
531 | tSNUMBER tHOUR_UNIT
532 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
533 | tSNUMBER tMINUTE_UNIT
534 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
536 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
539 seconds: signed_seconds | unsigned_seconds;
544 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
550 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
556 if (pc->dates_seen && ! pc->year.digits
557 && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
564 pc->day = $1.value % 100;
565 pc->month = ($1.value / 100) % 100;
566 pc->year.value = $1.value / 10000;
567 pc->year.digits = $1.digits - 4;
579 pc->hour = $1.value / 100;
580 pc->minutes = $1.value % 100;
582 pc->seconds.tv_sec = 0;
583 pc->seconds.tv_nsec = 0;
584 pc->meridian = MER24;
606 static table const meridian_table[] =
608 { "AM", tMERIDIAN, MERam },
609 { "A.M.", tMERIDIAN, MERam },
610 { "PM", tMERIDIAN, MERpm },
611 { "P.M.", tMERIDIAN, MERpm },
615 static table const dst_table[] =
620 static table const month_and_day_table[] =
622 { "JANUARY", tMONTH, 1 },
623 { "FEBRUARY", tMONTH, 2 },
624 { "MARCH", tMONTH, 3 },
625 { "APRIL", tMONTH, 4 },
626 { "MAY", tMONTH, 5 },
627 { "JUNE", tMONTH, 6 },
628 { "JULY", tMONTH, 7 },
629 { "AUGUST", tMONTH, 8 },
630 { "SEPTEMBER",tMONTH, 9 },
631 { "SEPT", tMONTH, 9 },
632 { "OCTOBER", tMONTH, 10 },
633 { "NOVEMBER", tMONTH, 11 },
634 { "DECEMBER", tMONTH, 12 },
635 { "SUNDAY", tDAY, 0 },
636 { "MONDAY", tDAY, 1 },
637 { "TUESDAY", tDAY, 2 },
639 { "WEDNESDAY",tDAY, 3 },
640 { "WEDNES", tDAY, 3 },
641 { "THURSDAY", tDAY, 4 },
643 { "THURS", tDAY, 4 },
644 { "FRIDAY", tDAY, 5 },
645 { "SATURDAY", tDAY, 6 },
649 static table const time_units_table[] =
651 { "YEAR", tYEAR_UNIT, 1 },
652 { "MONTH", tMONTH_UNIT, 1 },
653 { "FORTNIGHT",tDAY_UNIT, 14 },
654 { "WEEK", tDAY_UNIT, 7 },
655 { "DAY", tDAY_UNIT, 1 },
656 { "HOUR", tHOUR_UNIT, 1 },
657 { "MINUTE", tMINUTE_UNIT, 1 },
658 { "MIN", tMINUTE_UNIT, 1 },
659 { "SECOND", tSEC_UNIT, 1 },
660 { "SEC", tSEC_UNIT, 1 },
664 /* Assorted relative-time words. */
665 static table const relative_time_table[] =
667 { "TOMORROW", tDAY_UNIT, 1 },
668 { "YESTERDAY",tDAY_UNIT, -1 },
669 { "TODAY", tDAY_UNIT, 0 },
670 { "NOW", tDAY_UNIT, 0 },
671 { "LAST", tORDINAL, -1 },
672 { "THIS", tORDINAL, 0 },
673 { "NEXT", tORDINAL, 1 },
674 { "FIRST", tORDINAL, 1 },
675 /*{ "SECOND", tORDINAL, 2 }, */
676 { "THIRD", tORDINAL, 3 },
677 { "FOURTH", tORDINAL, 4 },
678 { "FIFTH", tORDINAL, 5 },
679 { "SIXTH", tORDINAL, 6 },
680 { "SEVENTH", tORDINAL, 7 },
681 { "EIGHTH", tORDINAL, 8 },
682 { "NINTH", tORDINAL, 9 },
683 { "TENTH", tORDINAL, 10 },
684 { "ELEVENTH", tORDINAL, 11 },
685 { "TWELFTH", tORDINAL, 12 },
690 /* The universal time zone table. These labels can be used even for
691 time stamps that would not otherwise be valid, e.g., GMT time
692 stamps in London during summer. */
693 static table const universal_time_zone_table[] =
695 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
696 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
697 { "UTC", tZONE, HOUR ( 0) },
701 /* The time zone table. This table is necessarily incomplete, as time
702 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
703 as Eastern time in Australia, not as US Eastern Standard Time.
704 You cannot rely on getdate to handle arbitrary time zone
705 abbreviations; use numeric abbreviations like `-0500' instead. */
706 static table const time_zone_table[] =
708 { "WET", tZONE, HOUR ( 0) }, /* Western European */
709 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
710 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
711 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
712 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
713 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
714 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
715 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
716 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
717 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
718 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
719 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
720 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
721 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
722 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
723 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
724 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
725 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
726 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
727 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
728 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
729 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
730 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
731 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
732 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
733 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
734 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
735 { "CET", tZONE, HOUR ( 1) }, /* Central European */
736 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
737 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
738 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
739 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
740 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
741 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
742 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
743 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
744 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
745 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
746 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
747 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
748 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
749 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
750 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
751 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
752 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
753 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
754 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
758 /* Military time zone table. */
759 static table const military_table[] =
761 { "A", tZONE, -HOUR ( 1) },
762 { "B", tZONE, -HOUR ( 2) },
763 { "C", tZONE, -HOUR ( 3) },
764 { "D", tZONE, -HOUR ( 4) },
765 { "E", tZONE, -HOUR ( 5) },
766 { "F", tZONE, -HOUR ( 6) },
767 { "G", tZONE, -HOUR ( 7) },
768 { "H", tZONE, -HOUR ( 8) },
769 { "I", tZONE, -HOUR ( 9) },
770 { "K", tZONE, -HOUR (10) },
771 { "L", tZONE, -HOUR (11) },
772 { "M", tZONE, -HOUR (12) },
773 { "N", tZONE, HOUR ( 1) },
774 { "O", tZONE, HOUR ( 2) },
775 { "P", tZONE, HOUR ( 3) },
776 { "Q", tZONE, HOUR ( 4) },
777 { "R", tZONE, HOUR ( 5) },
778 { "S", tZONE, HOUR ( 6) },
779 { "T", tZONE, HOUR ( 7) },
780 { "U", tZONE, HOUR ( 8) },
781 { "V", tZONE, HOUR ( 9) },
782 { "W", tZONE, HOUR (10) },
783 { "X", tZONE, HOUR (11) },
784 { "Y", tZONE, HOUR (12) },
785 { "Z", tZONE, HOUR ( 0) },
791 /* Convert a time zone expressed as HH:MM into an integer count of
792 minutes. If MM is negative, then S is of the form HHMM and needs
793 to be picked apart; otherwise, S is of the form HH. */
796 time_zone_hhmm (textint s, long int mm)
799 return (s.value / 100) * 60 + s.value % 100;
801 return s.value * 60 + (s.negative ? -mm : mm);
805 to_hour (long int hours, int meridian)
809 default: /* Pacify GCC. */
811 return 0 <= hours && hours < 24 ? hours : -1;
813 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
815 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
820 to_year (textint textyear)
822 long int year = textyear.value;
827 /* XPG4 suggests that years 00-68 map to 2000-2068, and
828 years 69-99 map to 1969-1999. */
829 else if (textyear.digits == 2)
830 year += year < 69 ? 2000 : 1900;
836 lookup_zone (parser_control const *pc, char const *name)
840 for (tp = universal_time_zone_table; tp->name; tp++)
841 if (strcmp (name, tp->name) == 0)
844 /* Try local zone abbreviations before those in time_zone_table, as
845 the local ones are more likely to be right. */
846 for (tp = pc->local_time_zone_table; tp->name; tp++)
847 if (strcmp (name, tp->name) == 0)
850 for (tp = time_zone_table; tp->name; tp++)
851 if (strcmp (name, tp->name) == 0)
858 /* Yield the difference between *A and *B,
859 measured in seconds, ignoring leap seconds.
860 The body of this function is taken directly from the GNU C Library;
861 see src/strftime.c. */
863 tm_diff (struct tm const *a, struct tm const *b)
865 /* Compute intervening leap days correctly even if year is negative.
866 Take care to avoid int overflow in leap day calculations. */
867 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
868 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
869 int a100 = a4 / 25 - (a4 % 25 < 0);
870 int b100 = b4 / 25 - (b4 % 25 < 0);
871 int a400 = SHR (a100, 2);
872 int b400 = SHR (b100, 2);
873 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
874 long int ayear = a->tm_year;
875 long int years = ayear - b->tm_year;
876 long int days = (365 * years + intervening_leap_days
877 + (a->tm_yday - b->tm_yday));
878 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
879 + (a->tm_min - b->tm_min))
880 + (a->tm_sec - b->tm_sec));
882 #endif /* ! HAVE_TM_GMTOFF */
885 lookup_word (parser_control const *pc, char *word)
894 /* Make it uppercase. */
895 for (p = word; *p; p++)
897 unsigned char ch = *p;
901 for (tp = meridian_table; tp->name; tp++)
902 if (strcmp (word, tp->name) == 0)
905 /* See if we have an abbreviation for a month. */
906 wordlen = strlen (word);
907 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
909 for (tp = month_and_day_table; tp->name; tp++)
910 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
913 if ((tp = lookup_zone (pc, word)))
916 if (strcmp (word, dst_table[0].name) == 0)
919 for (tp = time_units_table; tp->name; tp++)
920 if (strcmp (word, tp->name) == 0)
923 /* Strip off any plural and try the units table again. */
924 if (word[wordlen - 1] == 'S')
926 word[wordlen - 1] = '\0';
927 for (tp = time_units_table; tp->name; tp++)
928 if (strcmp (word, tp->name) == 0)
930 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
933 for (tp = relative_time_table; tp->name; tp++)
934 if (strcmp (word, tp->name) == 0)
937 /* Military time zones. */
939 for (tp = military_table; tp->name; tp++)
940 if (word[0] == tp->name[0])
943 /* Drop out any periods and try the time zone table again. */
944 for (period_found = false, p = q = word; (*p = *q); q++)
949 if (period_found && (tp = lookup_zone (pc, word)))
956 yylex (YYSTYPE *lvalp, parser_control *pc)
963 while (c = *pc->input, isspace (c))
966 if (ISDIGIT (c) || c == '-' || c == '+')
970 unsigned long int value;
971 if (c == '-' || c == '+')
973 sign = c == '-' ? -1 : 1;
974 while (c = *++pc->input, isspace (c))
977 /* skip the '-' sign */
983 for (value = 0; ; value *= 10)
985 unsigned long int value1 = value + (c - '0');
992 if (ULONG_MAX / 10 < value)
995 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1000 unsigned long int value1;
1002 /* Check for overflow when converting value to time_t. */
1017 if (value != value1)
1020 /* Accumulate fraction, to ns precision. */
1023 for (digits = 2; digits <= LOG10_BILLION; digits++)
1030 /* Skip excess digits, truncating toward -Infinity. */
1032 for (; ISDIGIT (*p); p++)
1038 while (ISDIGIT (*p))
1041 /* Adjust to the timespec convention, which is that
1042 tv_nsec is always a positive offset even if tv_sec is
1052 lvalp->timespec.tv_sec = s;
1053 lvalp->timespec.tv_nsec = ns;
1055 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1059 lvalp->textintval.negative = sign < 0;
1062 lvalp->textintval.value = - value;
1063 if (0 < lvalp->textintval.value)
1068 lvalp->textintval.value = value;
1069 if (lvalp->textintval.value < 0)
1072 lvalp->textintval.digits = p - pc->input;
1074 return sign ? tSNUMBER : tUNUMBER;
1086 if (p < buff + sizeof buff - 1)
1090 while (isalpha (c) || c == '.');
1093 tp = lookup_word (pc, buff);
1096 lvalp->intval = tp->value;
1101 return *pc->input++;
1117 /* Do nothing if the parser reports an error. */
1119 yyerror (parser_control const *pc ATTRIBUTE_UNUSED,
1120 char const *s ATTRIBUTE_UNUSED)
1125 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1126 passing it to mktime, return true if it's OK that mktime returned T.
1127 It's not OK if *TM0 has out-of-range members. */
1130 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1132 if (t == (time_t) -1)
1134 /* Guard against falsely reporting an error when parsing a time
1135 stamp that happens to equal (time_t) -1, on a host that
1136 supports such a time stamp. */
1137 tm1 = localtime (&t);
1142 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1143 | (tm0->tm_min ^ tm1->tm_min)
1144 | (tm0->tm_hour ^ tm1->tm_hour)
1145 | (tm0->tm_mday ^ tm1->tm_mday)
1146 | (tm0->tm_mon ^ tm1->tm_mon)
1147 | (tm0->tm_year ^ tm1->tm_year));
1150 /* A reasonable upper bound for the size of ordinary TZ strings.
1151 Use heap allocation if TZ's length exceeds this. */
1152 enum { TZBUFSIZE = 100 };
1154 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1157 get_tz (char tzbuf[TZBUFSIZE])
1159 char *tz = getenv ("TZ");
1162 size_t tzsize = strlen (tz) + 1;
1163 tz = (tzsize <= TZBUFSIZE
1164 ? memcpy (tzbuf, tz, tzsize)
1165 : xmemdup (tz, tzsize));
1170 /* Parse a date/time string, storing the resulting time value into *RESULT.
1171 The string itself is pointed to by P. Return true if successful.
1172 P can be an incomplete or relative time specification; if so, use
1173 *NOW as the basis for the returned time. */
1175 get_date (struct timespec *result, char const *p, struct timespec const *now)
1179 struct tm const *tmp;
1183 struct timespec gettime_buffer;
1185 bool tz_was_altered = false;
1187 char tz0buf[TZBUFSIZE];
1192 gettime (&gettime_buffer);
1193 now = &gettime_buffer;
1196 Start = now->tv_sec;
1197 Start_ns = now->tv_nsec;
1199 tmp = localtime (&now->tv_sec);
1203 while (c = *p, isspace (c))
1206 if (strncmp (p, "TZ=\"", 4) == 0)
1208 char const *tzbase = p + 4;
1212 for (s = tzbase; *s; s++, tzsize++)
1216 if (! (*s == '\\' || *s == '"'))
1223 char tz1buf[TZBUFSIZE];
1224 bool large_tz = TZBUFSIZE < tzsize;
1226 tz0 = get_tz (tz0buf);
1227 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1228 for (s = tzbase; *s != '"'; s++)
1229 *z++ = *(s += *s == '\\');
1231 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1236 tz_was_altered = true;
1242 pc.year.value = tmp->tm_year;
1243 pc.year.value += TM_YEAR_BASE;
1245 pc.month = tmp->tm_mon + 1;
1246 pc.day = tmp->tm_mday;
1247 pc.hour = tmp->tm_hour;
1248 pc.minutes = tmp->tm_min;
1249 pc.seconds.tv_sec = tmp->tm_sec;
1250 pc.seconds.tv_nsec = Start_ns;
1251 tm.tm_isdst = tmp->tm_isdst;
1253 pc.meridian = MER24;
1254 pc.rel = RELATIVE_TIME_0;
1255 pc.timespec_seen = false;
1256 pc.rels_seen = false;
1260 pc.local_zones_seen = 0;
1264 #if HAVE_STRUCT_TM_TM_ZONE
1265 pc.local_time_zone_table[0].name = tmp->tm_zone;
1266 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1267 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1268 pc.local_time_zone_table[1].name = NULL;
1270 /* Probe the names used in the next three calendar quarters, looking
1271 for a tm_isdst different from the one we already have. */
1274 for (quarter = 1; quarter <= 3; quarter++)
1276 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1277 struct tm const *probe_tm = localtime (&probe);
1278 if (probe_tm && probe_tm->tm_zone
1279 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1282 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1283 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1284 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1285 pc.local_time_zone_table[2].name = NULL;
1295 extern char *tzname[];
1298 for (i = 0; i < 2; i++)
1300 pc.local_time_zone_table[i].name = tzname[i];
1301 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1302 pc.local_time_zone_table[i].value = i;
1304 pc.local_time_zone_table[i].name = NULL;
1307 pc.local_time_zone_table[0].name = NULL;
1311 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1312 && ! strcmp (pc.local_time_zone_table[0].name,
1313 pc.local_time_zone_table[1].name))
1315 /* This locale uses the same abbrevation for standard and
1316 daylight times. So if we see that abbreviation, we don't
1317 know whether it's daylight time. */
1318 pc.local_time_zone_table[0].value = -1;
1319 pc.local_time_zone_table[1].name = NULL;
1322 if (yyparse (&pc) != 0)
1325 if (pc.timespec_seen)
1326 *result = pc.seconds;
1329 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1330 | (pc.local_zones_seen + pc.zones_seen)))
1333 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1334 tm.tm_mon = pc.month - 1;
1335 tm.tm_mday = pc.day;
1336 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1338 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1341 tm.tm_min = pc.minutes;
1342 tm.tm_sec = pc.seconds.tv_sec;
1346 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1347 pc.seconds.tv_nsec = 0;
1350 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1351 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1354 /* But if the input explicitly specifies local time with or without
1355 DST, give mktime that information. */
1356 if (pc.local_zones_seen)
1357 tm.tm_isdst = pc.local_isdst;
1361 Start = mktime (&tm);
1363 if (! mktime_ok (&tm0, &tm, Start))
1365 if (! pc.zones_seen)
1369 /* Guard against falsely reporting errors near the time_t
1370 boundaries when parsing times in other time zones. For
1371 example, suppose the input string "1969-12-31 23:00:00 -0100",
1372 the current time zone is 8 hours ahead of UTC, and the min
1373 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1374 localtime value is 1970-01-01 08:00:00, and mktime will
1375 therefore fail on 1969-12-31 23:00:00. To work around the
1376 problem, set the time zone to 1 hour behind UTC temporarily
1377 by setting TZ="XXX1:00" and try mktime again. */
1379 long int time_zone = pc.time_zone;
1380 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1381 long int abs_time_zone_hour = abs_time_zone / 60;
1382 int abs_time_zone_min = abs_time_zone % 60;
1383 char tz1buf[sizeof "XXX+0:00"
1384 + sizeof pc.time_zone * CHAR_BIT / 3];
1385 if (!tz_was_altered)
1386 tz0 = get_tz (tz0buf);
1387 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1388 abs_time_zone_hour, abs_time_zone_min);
1389 if (setenv ("TZ", tz1buf, 1) != 0)
1391 tz_was_altered = true;
1393 Start = mktime (&tm);
1394 if (! mktime_ok (&tm0, &tm, Start))
1399 if (pc.days_seen && ! pc.dates_seen)
1401 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1402 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1404 Start = mktime (&tm);
1405 if (Start == (time_t) -1)
1411 long int delta = pc.time_zone * 60;
1413 #ifdef HAVE_TM_GMTOFF
1414 delta -= tm.tm_gmtoff;
1417 struct tm const *gmt = gmtime (&t);
1420 delta -= tm_diff (&tm, gmt);
1423 if ((Start < t1) != (delta < 0))
1424 goto fail; /* time_t overflow */
1428 /* Add relative date. */
1429 if (pc.rel.year | pc.rel.month | pc.rel.day)
1431 int year = tm.tm_year + pc.rel.year;
1432 int month = tm.tm_mon + pc.rel.month;
1433 int day = tm.tm_mday + pc.rel.day;
1434 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1435 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1436 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1441 tm.tm_hour = tm0.tm_hour;
1442 tm.tm_min = tm0.tm_min;
1443 tm.tm_sec = tm0.tm_sec;
1444 tm.tm_isdst = tm0.tm_isdst;
1445 Start = mktime (&tm);
1446 if (Start == (time_t) -1)
1450 /* Add relative hours, minutes, and seconds. On hosts that support
1451 leap seconds, ignore the possibility of leap seconds; e.g.,
1452 "+ 10 minutes" adds 600 seconds, even if one of them is a
1453 leap second. Typically this is not what the user wants, but it's
1454 too hard to do it the other way, because the time zone indicator
1455 must be applied before relative times, and if mktime is applied
1456 again the time zone will be lost. */
1458 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1459 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1461 long int d1 = 60 * 60 * pc.rel.hour;
1462 time_t t1 = t0 + d1;
1463 long int d2 = 60 * pc.rel.minutes;
1464 time_t t2 = t1 + d2;
1465 long int d3 = pc.rel.seconds;
1466 time_t t3 = t2 + d3;
1467 long int d4 = (sum_ns - normalized_ns) / BILLION;
1468 time_t t4 = t3 + d4;
1470 if ((d1 / (60 * 60) ^ pc.rel.hour)
1471 | (d2 / 60 ^ pc.rel.minutes)
1472 | ((t1 < t0) ^ (d1 < 0))
1473 | ((t2 < t1) ^ (d2 < 0))
1474 | ((t3 < t2) ^ (d3 < 0))
1475 | ((t4 < t3) ^ (d4 < 0)))
1478 result->tv_sec = t4;
1479 result->tv_nsec = normalized_ns;
1489 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1498 main (int ac, char **av)
1502 printf ("Enter date, or blank line to exit.\n\t> ");
1505 buff[BUFSIZ - 1] = '\0';
1506 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1509 struct tm const *tm;
1510 if (! get_date (&d, buff, NULL))
1511 printf ("Bad format - couldn't convert.\n");
1512 else if (! (tm = localtime (&d.tv_sec)))
1514 long int sec = d.tv_sec;
1515 printf ("localtime (%ld) failed\n", sec);
1520 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1521 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1522 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);