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;
1241 /* As documented, be careful to treat the empty string just like
1242 a date string of "0". Without this, an empty string would be
1243 declared invalid when parsed during a DST transition. */
1248 pc.year.value = tmp->tm_year;
1249 pc.year.value += TM_YEAR_BASE;
1251 pc.month = tmp->tm_mon + 1;
1252 pc.day = tmp->tm_mday;
1253 pc.hour = tmp->tm_hour;
1254 pc.minutes = tmp->tm_min;
1255 pc.seconds.tv_sec = tmp->tm_sec;
1256 pc.seconds.tv_nsec = Start_ns;
1257 tm.tm_isdst = tmp->tm_isdst;
1259 pc.meridian = MER24;
1260 pc.rel = RELATIVE_TIME_0;
1261 pc.timespec_seen = false;
1262 pc.rels_seen = false;
1266 pc.local_zones_seen = 0;
1270 #if HAVE_STRUCT_TM_TM_ZONE
1271 pc.local_time_zone_table[0].name = tmp->tm_zone;
1272 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1273 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1274 pc.local_time_zone_table[1].name = NULL;
1276 /* Probe the names used in the next three calendar quarters, looking
1277 for a tm_isdst different from the one we already have. */
1280 for (quarter = 1; quarter <= 3; quarter++)
1282 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1283 struct tm const *probe_tm = localtime (&probe);
1284 if (probe_tm && probe_tm->tm_zone
1285 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1288 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1289 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1290 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1291 pc.local_time_zone_table[2].name = NULL;
1301 extern char *tzname[];
1304 for (i = 0; i < 2; i++)
1306 pc.local_time_zone_table[i].name = tzname[i];
1307 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1308 pc.local_time_zone_table[i].value = i;
1310 pc.local_time_zone_table[i].name = NULL;
1313 pc.local_time_zone_table[0].name = NULL;
1317 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1318 && ! strcmp (pc.local_time_zone_table[0].name,
1319 pc.local_time_zone_table[1].name))
1321 /* This locale uses the same abbrevation for standard and
1322 daylight times. So if we see that abbreviation, we don't
1323 know whether it's daylight time. */
1324 pc.local_time_zone_table[0].value = -1;
1325 pc.local_time_zone_table[1].name = NULL;
1328 if (yyparse (&pc) != 0)
1331 if (pc.timespec_seen)
1332 *result = pc.seconds;
1335 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1336 | (pc.local_zones_seen + pc.zones_seen)))
1339 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1340 tm.tm_mon = pc.month - 1;
1341 tm.tm_mday = pc.day;
1342 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1344 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1347 tm.tm_min = pc.minutes;
1348 tm.tm_sec = pc.seconds.tv_sec;
1352 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1353 pc.seconds.tv_nsec = 0;
1356 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1357 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1360 /* But if the input explicitly specifies local time with or without
1361 DST, give mktime that information. */
1362 if (pc.local_zones_seen)
1363 tm.tm_isdst = pc.local_isdst;
1367 Start = mktime (&tm);
1369 if (! mktime_ok (&tm0, &tm, Start))
1371 if (! pc.zones_seen)
1375 /* Guard against falsely reporting errors near the time_t
1376 boundaries when parsing times in other time zones. For
1377 example, suppose the input string "1969-12-31 23:00:00 -0100",
1378 the current time zone is 8 hours ahead of UTC, and the min
1379 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1380 localtime value is 1970-01-01 08:00:00, and mktime will
1381 therefore fail on 1969-12-31 23:00:00. To work around the
1382 problem, set the time zone to 1 hour behind UTC temporarily
1383 by setting TZ="XXX1:00" and try mktime again. */
1385 long int time_zone = pc.time_zone;
1386 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1387 long int abs_time_zone_hour = abs_time_zone / 60;
1388 int abs_time_zone_min = abs_time_zone % 60;
1389 char tz1buf[sizeof "XXX+0:00"
1390 + sizeof pc.time_zone * CHAR_BIT / 3];
1391 if (!tz_was_altered)
1392 tz0 = get_tz (tz0buf);
1393 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1394 abs_time_zone_hour, abs_time_zone_min);
1395 if (setenv ("TZ", tz1buf, 1) != 0)
1397 tz_was_altered = true;
1399 Start = mktime (&tm);
1400 if (! mktime_ok (&tm0, &tm, Start))
1405 if (pc.days_seen && ! pc.dates_seen)
1407 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1408 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1410 Start = mktime (&tm);
1411 if (Start == (time_t) -1)
1417 long int delta = pc.time_zone * 60;
1419 #ifdef HAVE_TM_GMTOFF
1420 delta -= tm.tm_gmtoff;
1423 struct tm const *gmt = gmtime (&t);
1426 delta -= tm_diff (&tm, gmt);
1429 if ((Start < t1) != (delta < 0))
1430 goto fail; /* time_t overflow */
1434 /* Add relative date. */
1435 if (pc.rel.year | pc.rel.month | pc.rel.day)
1437 int year = tm.tm_year + pc.rel.year;
1438 int month = tm.tm_mon + pc.rel.month;
1439 int day = tm.tm_mday + pc.rel.day;
1440 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1441 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1442 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1447 tm.tm_hour = tm0.tm_hour;
1448 tm.tm_min = tm0.tm_min;
1449 tm.tm_sec = tm0.tm_sec;
1450 tm.tm_isdst = tm0.tm_isdst;
1451 Start = mktime (&tm);
1452 if (Start == (time_t) -1)
1456 /* Add relative hours, minutes, and seconds. On hosts that support
1457 leap seconds, ignore the possibility of leap seconds; e.g.,
1458 "+ 10 minutes" adds 600 seconds, even if one of them is a
1459 leap second. Typically this is not what the user wants, but it's
1460 too hard to do it the other way, because the time zone indicator
1461 must be applied before relative times, and if mktime is applied
1462 again the time zone will be lost. */
1464 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1465 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1467 long int d1 = 60 * 60 * pc.rel.hour;
1468 time_t t1 = t0 + d1;
1469 long int d2 = 60 * pc.rel.minutes;
1470 time_t t2 = t1 + d2;
1471 long int d3 = pc.rel.seconds;
1472 time_t t3 = t2 + d3;
1473 long int d4 = (sum_ns - normalized_ns) / BILLION;
1474 time_t t4 = t3 + d4;
1476 if ((d1 / (60 * 60) ^ pc.rel.hour)
1477 | (d2 / 60 ^ pc.rel.minutes)
1478 | ((t1 < t0) ^ (d1 < 0))
1479 | ((t2 < t1) ^ (d2 < 0))
1480 | ((t3 < t2) ^ (d3 < 0))
1481 | ((t4 < t3) ^ (d4 < 0)))
1484 result->tv_sec = t4;
1485 result->tv_nsec = normalized_ns;
1495 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1504 main (int ac, char **av)
1508 printf ("Enter date, or blank line to exit.\n\t> ");
1511 buff[BUFSIZ - 1] = '\0';
1512 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1515 struct tm const *tm;
1516 if (! get_date (&d, buff, NULL))
1517 printf ("Bad format - couldn't convert.\n");
1518 else if (! (tm = localtime (&d.tv_sec)))
1520 long int sec = d.tv_sec;
1521 printf ("localtime (%ld) failed\n", sec);
1526 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1527 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1528 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);