2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999-2000, 2002-2012 Free Software Foundation, Inc.
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 /* Originally written by Steven M. Bellovin <smb@research.att.com> while
20 at the University of North Carolina at Chapel Hill. Later tweaked by
21 a couple of people on Usenet. Completely overhauled by Rich $alz
22 <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
24 Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
25 the right thing about local DST. Also modified by Paul Eggert
26 <eggert@cs.ucla.edu> in February 2004 to support
27 nanosecond-resolution time stamps, and in October 2004 to support
28 TZ strings in dates. */
30 /* FIXME: Check for arithmetic overflow in all cases, not just
35 #include "parse-datetime.h"
41 /* There's no need to extend the stack, so there's no need to involve
43 #define YYSTACK_USE_ALLOCA 0
45 /* Tell Bison how much stack space is needed. 20 should be plenty for
46 this grammar, which is not right recursive. Beware setting it too
47 high, since that might cause problems on machines whose
48 implementations have lame stack-overflow checking. */
50 #define YYINITDEPTH YYMAXDEPTH
52 /* Since the code of parse-datetime.y is not included in the Emacs executable
53 itself, there is no need to #define static in this file. Even if
54 the code were included in the Emacs executable, it probably
55 wouldn't do any harm to #undef it here; this will only cause
56 problems if we try to write to a static variable, which I don't
57 think this code needs to do. */
70 /* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
71 use _STDLIB_H_ as witness. Map the latter to the one bison uses. */
72 /* FIXME: this is temporary. Remove when we have a mechanism to ensure
73 that the version we're using is fixed, too. */
79 /* ISDIGIT differs from isdigit, as follows:
80 - Its arg may be any int or unsigned int; it need not be an unsigned char
82 - It's typically faster.
83 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
84 isdigit unless it's important to use the locale's definition
85 of "digit" even when the host does not conform to POSIX. */
86 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
88 /* Shift A right by B bits portably, by dividing A by 2**B and
89 truncating towards minus infinity. A and B should be free of side
90 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
91 INT_BITS is the number of useful bits in an int. GNU code can
92 assume that INT_BITS is at least 32.
94 ISO C99 says that A >> B is implementation-defined if A < 0. Some
95 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
96 right in the usual way when A < 0, so SHR falls back on division if
97 ordinary A >> B doesn't seem to be the usual signed shift. */
101 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
103 #define EPOCH_YEAR 1970
104 #define TM_YEAR_BASE 1900
106 #define HOUR(x) ((x) * 60)
108 /* long_time_t is a signed integer type that contains all time_t values. */
109 verify (TYPE_IS_INTEGER (time_t));
110 #if TIME_T_FITS_IN_LONG_INT
111 typedef long int long_time_t;
113 typedef time_t long_time_t;
116 /* Lots of this code assumes time_t and time_t-like values fit into
118 verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t)
119 && TYPE_MAXIMUM (time_t) <= TYPE_MAXIMUM (long_time_t));
121 /* FIXME: It also assumes that signed integer overflow silently wraps around,
122 but this is not true any more with recent versions of GCC 4. */
124 /* An integer value, and the number of digits in its textual
133 /* An entry in the lexical lookup table. */
141 /* Meridian: am, pm, or 24-hour style. */
142 enum { MERam, MERpm, MER24 };
144 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
146 /* Relative times. */
149 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
159 #if HAVE_COMPOUND_LITERALS
160 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
162 static relative_time const RELATIVE_TIME_0;
165 /* Information passed to and from the parser. */
168 /* The input string remaining to be parsed. */
171 /* N, if this is the Nth Tuesday. */
172 long int day_ordinal;
174 /* Day of week; Sunday is 0. */
177 /* tm_isdst flag for the local zone. */
180 /* Time zone, in minutes east of UTC. */
183 /* Style used for time. */
186 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
192 struct timespec seconds; /* includes nanoseconds */
194 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
197 /* Presence or counts of nonterminals of various flavors parsed so far. */
202 size_t local_zones_seen;
207 /* Table of local time zone abbreviations, terminated by a null entry. */
208 table local_time_zone_table[3];
212 static int yylex (union YYSTYPE *, parser_control *);
213 static int yyerror (parser_control const *, char const *);
214 static long int time_zone_hhmm (parser_control *, textint, long int);
216 /* Extract into *PC any date and time info from a string of digits
217 of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
220 digits_to_date_time (parser_control *pc, textint text_int)
222 if (pc->dates_seen && ! pc->year.digits
223 && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
227 if (4 < text_int.digits)
230 pc->day = text_int.value % 100;
231 pc->month = (text_int.value / 100) % 100;
232 pc->year.value = text_int.value / 10000;
233 pc->year.digits = text_int.digits - 4;
238 if (text_int.digits <= 2)
240 pc->hour = text_int.value;
245 pc->hour = text_int.value / 100;
246 pc->minutes = text_int.value % 100;
248 pc->seconds.tv_sec = 0;
249 pc->seconds.tv_nsec = 0;
250 pc->meridian = MER24;
255 /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). */
257 apply_relative_time (parser_control *pc, relative_time rel, int factor)
259 pc->rel.ns += factor * rel.ns;
260 pc->rel.seconds += factor * rel.seconds;
261 pc->rel.minutes += factor * rel.minutes;
262 pc->rel.hour += factor * rel.hour;
263 pc->rel.day += factor * rel.day;
264 pc->rel.month += factor * rel.month;
265 pc->rel.year += factor * rel.year;
266 pc->rels_seen = true;
269 /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */
271 set_hhmmss (parser_control *pc, long int hour, long int minutes,
272 time_t sec, long int nsec)
275 pc->minutes = minutes;
276 pc->seconds.tv_sec = sec;
277 pc->seconds.tv_nsec = nsec;
282 /* We want a reentrant parser, even if the TZ manipulation and the calls to
283 localtime and gmtime are not reentrant. */
285 %parse-param { parser_control *pc }
286 %lex-param { parser_control *pc }
288 /* This grammar has 31 shift/reduce conflicts. */
295 struct timespec timespec;
302 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
303 %token <intval> tDAY_UNIT tDAY_SHIFT
305 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
306 %token <intval> tMONTH tORDINAL tZONE
308 %token <textintval> tSNUMBER tUNUMBER
309 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
311 %type <intval> o_colon_minutes
312 %type <timespec> seconds signed_seconds unsigned_seconds
314 %type <rel> relunit relunit_snumber dayshift
327 pc->timespec_seen = true;
338 { pc->times_seen++; pc->dates_seen++; }
340 { pc->times_seen++; }
342 { pc->local_zones_seen++; }
344 { pc->zones_seen++; }
346 { pc->dates_seen++; }
359 iso_8601_date 'T' iso_8601_time
365 set_hhmmss (pc, $1.value, 0, 0, 0);
368 | tUNUMBER ':' tUNUMBER tMERIDIAN
370 set_hhmmss (pc, $1.value, $3.value, 0, 0);
373 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN
375 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
384 set_hhmmss (pc, $1.value, 0, 0, 0);
385 pc->meridian = MER24;
387 | tUNUMBER ':' tUNUMBER o_zone_offset
389 set_hhmmss (pc, $1.value, $3.value, 0, 0);
390 pc->meridian = MER24;
392 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_zone_offset
394 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
395 pc->meridian = MER24;
405 tSNUMBER o_colon_minutes
408 pc->time_zone = time_zone_hhmm (pc, $1, $2);
415 pc->local_isdst = $1;
416 pc->dsts_seen += (0 < $1);
421 pc->dsts_seen += (0 < $1) + 1;
425 /* Note 'T' is a special case, as it is used as the separator in ISO
426 8601 date and time of day representation. */
429 { pc->time_zone = $1; }
431 { pc->time_zone = HOUR(7); }
432 | tZONE relunit_snumber
433 { pc->time_zone = $1;
434 apply_relative_time (pc, $2, 1); }
435 | 'T' relunit_snumber
436 { pc->time_zone = HOUR(7);
437 apply_relative_time (pc, $2, 1); }
438 | tZONE tSNUMBER o_colon_minutes
439 { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
441 { pc->time_zone = $1 + 60; }
443 { pc->time_zone = $1 + 60; }
459 pc->day_ordinal = $1;
464 pc->day_ordinal = $1.value;
470 tUNUMBER '/' tUNUMBER
472 pc->month = $1.value;
475 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
477 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
478 otherwise as MM/DD/YY.
479 The goal in recognizing YYYY/MM/DD is solely to support legacy
480 machine-generated dates like those in an RCS log listing. If
481 you want portability, use the ISO 8601 format. */
485 pc->month = $3.value;
490 pc->month = $1.value;
495 | tUNUMBER tMONTH tSNUMBER
497 /* e.g. 17-JUN-1992. */
500 pc->year.value = -$3.value;
501 pc->year.digits = $3.digits;
503 | tMONTH tSNUMBER tSNUMBER
505 /* e.g. JUN-17-1992. */
508 pc->year.value = -$3.value;
509 pc->year.digits = $3.digits;
516 | tMONTH tUNUMBER ',' tUNUMBER
527 | tUNUMBER tMONTH tUNUMBER
537 tUNUMBER tSNUMBER tSNUMBER
539 /* ISO 8601 format. YYYY-MM-DD. */
541 pc->month = -$2.value;
548 { apply_relative_time (pc, $1, $2); }
550 { apply_relative_time (pc, $1, 1); }
552 { apply_relative_time (pc, $1, 1); }
557 { $$ = RELATIVE_TIME_0; $$.year = $1; }
558 | tUNUMBER tYEAR_UNIT
559 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
561 { $$ = RELATIVE_TIME_0; $$.year = 1; }
562 | tORDINAL tMONTH_UNIT
563 { $$ = RELATIVE_TIME_0; $$.month = $1; }
564 | tUNUMBER tMONTH_UNIT
565 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
567 { $$ = RELATIVE_TIME_0; $$.month = 1; }
569 { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
571 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
573 { $$ = RELATIVE_TIME_0; $$.day = $1; }
574 | tORDINAL tHOUR_UNIT
575 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
576 | tUNUMBER tHOUR_UNIT
577 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
579 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
580 | tORDINAL tMINUTE_UNIT
581 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
582 | tUNUMBER tMINUTE_UNIT
583 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
585 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
587 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
589 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
590 | tSDECIMAL_NUMBER tSEC_UNIT
591 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
592 | tUDECIMAL_NUMBER tSEC_UNIT
593 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
595 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
601 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
602 | tSNUMBER tMONTH_UNIT
603 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
605 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
606 | tSNUMBER tHOUR_UNIT
607 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
608 | tSNUMBER tMINUTE_UNIT
609 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
611 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
616 { $$ = RELATIVE_TIME_0; $$.day = $1; }
619 seconds: signed_seconds | unsigned_seconds;
624 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
630 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
635 { digits_to_date_time (pc, $1); }
639 tUNUMBER relunit_snumber
641 /* Hybrid all-digit and relative offset, so that we accept e.g.,
642 "YYYYMMDD +N days" as well as "YYYYMMDD N days". */
643 digits_to_date_time (pc, $1);
644 apply_relative_time (pc, $2, 1);
657 static table const meridian_table[] =
659 { "AM", tMERIDIAN, MERam },
660 { "A.M.", tMERIDIAN, MERam },
661 { "PM", tMERIDIAN, MERpm },
662 { "P.M.", tMERIDIAN, MERpm },
666 static table const dst_table[] =
671 static table const month_and_day_table[] =
673 { "JANUARY", tMONTH, 1 },
674 { "FEBRUARY", tMONTH, 2 },
675 { "MARCH", tMONTH, 3 },
676 { "APRIL", tMONTH, 4 },
677 { "MAY", tMONTH, 5 },
678 { "JUNE", tMONTH, 6 },
679 { "JULY", tMONTH, 7 },
680 { "AUGUST", tMONTH, 8 },
681 { "SEPTEMBER",tMONTH, 9 },
682 { "SEPT", tMONTH, 9 },
683 { "OCTOBER", tMONTH, 10 },
684 { "NOVEMBER", tMONTH, 11 },
685 { "DECEMBER", tMONTH, 12 },
686 { "SUNDAY", tDAY, 0 },
687 { "MONDAY", tDAY, 1 },
688 { "TUESDAY", tDAY, 2 },
690 { "WEDNESDAY",tDAY, 3 },
691 { "WEDNES", tDAY, 3 },
692 { "THURSDAY", tDAY, 4 },
694 { "THURS", tDAY, 4 },
695 { "FRIDAY", tDAY, 5 },
696 { "SATURDAY", tDAY, 6 },
700 static table const time_units_table[] =
702 { "YEAR", tYEAR_UNIT, 1 },
703 { "MONTH", tMONTH_UNIT, 1 },
704 { "FORTNIGHT",tDAY_UNIT, 14 },
705 { "WEEK", tDAY_UNIT, 7 },
706 { "DAY", tDAY_UNIT, 1 },
707 { "HOUR", tHOUR_UNIT, 1 },
708 { "MINUTE", tMINUTE_UNIT, 1 },
709 { "MIN", tMINUTE_UNIT, 1 },
710 { "SECOND", tSEC_UNIT, 1 },
711 { "SEC", tSEC_UNIT, 1 },
715 /* Assorted relative-time words. */
716 static table const relative_time_table[] =
718 { "TOMORROW", tDAY_SHIFT, 1 },
719 { "YESTERDAY",tDAY_SHIFT, -1 },
720 { "TODAY", tDAY_SHIFT, 0 },
721 { "NOW", tDAY_SHIFT, 0 },
722 { "LAST", tORDINAL, -1 },
723 { "THIS", tORDINAL, 0 },
724 { "NEXT", tORDINAL, 1 },
725 { "FIRST", tORDINAL, 1 },
726 /*{ "SECOND", tORDINAL, 2 }, */
727 { "THIRD", tORDINAL, 3 },
728 { "FOURTH", tORDINAL, 4 },
729 { "FIFTH", tORDINAL, 5 },
730 { "SIXTH", tORDINAL, 6 },
731 { "SEVENTH", tORDINAL, 7 },
732 { "EIGHTH", tORDINAL, 8 },
733 { "NINTH", tORDINAL, 9 },
734 { "TENTH", tORDINAL, 10 },
735 { "ELEVENTH", tORDINAL, 11 },
736 { "TWELFTH", tORDINAL, 12 },
738 { "HENCE", tAGO, 1 },
742 /* The universal time zone table. These labels can be used even for
743 time stamps that would not otherwise be valid, e.g., GMT time
744 stamps in London during summer. */
745 static table const universal_time_zone_table[] =
747 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
748 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
749 { "UTC", tZONE, HOUR ( 0) },
753 /* The time zone table. This table is necessarily incomplete, as time
754 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
755 as Eastern time in Australia, not as US Eastern Standard Time.
756 You cannot rely on parse_datetime to handle arbitrary time zone
757 abbreviations; use numeric abbreviations like "-0500" instead. */
758 static table const time_zone_table[] =
760 { "WET", tZONE, HOUR ( 0) }, /* Western European */
761 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
762 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
763 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
764 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
765 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
766 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
767 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
768 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
769 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
770 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
771 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
772 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
773 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
774 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
775 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
776 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
777 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
778 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
779 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
780 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
781 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
782 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
783 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
784 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
785 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
786 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
787 { "CET", tZONE, HOUR ( 1) }, /* Central European */
788 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
789 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
790 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
791 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
792 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
793 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
794 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
795 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
796 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
797 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
798 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
799 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
800 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
801 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
802 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
803 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
804 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
805 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
806 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
810 /* Military time zone table.
812 Note 'T' is a special case, as it is used as the separator in ISO
813 8601 date and time of day representation. */
814 static table const military_table[] =
816 { "A", tZONE, -HOUR ( 1) },
817 { "B", tZONE, -HOUR ( 2) },
818 { "C", tZONE, -HOUR ( 3) },
819 { "D", tZONE, -HOUR ( 4) },
820 { "E", tZONE, -HOUR ( 5) },
821 { "F", tZONE, -HOUR ( 6) },
822 { "G", tZONE, -HOUR ( 7) },
823 { "H", tZONE, -HOUR ( 8) },
824 { "I", tZONE, -HOUR ( 9) },
825 { "K", tZONE, -HOUR (10) },
826 { "L", tZONE, -HOUR (11) },
827 { "M", tZONE, -HOUR (12) },
828 { "N", tZONE, HOUR ( 1) },
829 { "O", tZONE, HOUR ( 2) },
830 { "P", tZONE, HOUR ( 3) },
831 { "Q", tZONE, HOUR ( 4) },
832 { "R", tZONE, HOUR ( 5) },
833 { "S", tZONE, HOUR ( 6) },
835 { "U", tZONE, HOUR ( 8) },
836 { "V", tZONE, HOUR ( 9) },
837 { "W", tZONE, HOUR (10) },
838 { "X", tZONE, HOUR (11) },
839 { "Y", tZONE, HOUR (12) },
840 { "Z", tZONE, HOUR ( 0) },
846 /* Convert a time zone expressed as HH:MM into an integer count of
847 minutes. If MM is negative, then S is of the form HHMM and needs
848 to be picked apart; otherwise, S is of the form HH. As specified in
849 http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
850 only valid TZ range, and consider first two digits as hours, if no
851 minutes specified. */
854 time_zone_hhmm (parser_control *pc, textint s, long int mm)
858 /* If the length of S is 1 or 2 and no minutes are specified,
859 interpret it as a number of hours. */
860 if (s.digits <= 2 && mm < 0)
864 n_minutes = (s.value / 100) * 60 + s.value % 100;
866 n_minutes = s.value * 60 + (s.negative ? -mm : mm);
868 /* If the absolute number of minutes is larger than 24 hours,
869 arrange to reject it by incrementing pc->zones_seen. Thus,
870 we allow only values in the range UTC-24:00 to UTC+24:00. */
871 if (24 * 60 < abs (n_minutes))
878 to_hour (long int hours, int meridian)
882 default: /* Pacify GCC. */
884 return 0 <= hours && hours < 24 ? hours : -1;
886 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
888 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
893 to_year (textint textyear)
895 long int year = textyear.value;
900 /* XPG4 suggests that years 00-68 map to 2000-2068, and
901 years 69-99 map to 1969-1999. */
902 else if (textyear.digits == 2)
903 year += year < 69 ? 2000 : 1900;
908 static table const * _GL_ATTRIBUTE_PURE
909 lookup_zone (parser_control const *pc, char const *name)
913 for (tp = universal_time_zone_table; tp->name; tp++)
914 if (strcmp (name, tp->name) == 0)
917 /* Try local zone abbreviations before those in time_zone_table, as
918 the local ones are more likely to be right. */
919 for (tp = pc->local_time_zone_table; tp->name; tp++)
920 if (strcmp (name, tp->name) == 0)
923 for (tp = time_zone_table; tp->name; tp++)
924 if (strcmp (name, tp->name) == 0)
931 /* Yield the difference between *A and *B,
932 measured in seconds, ignoring leap seconds.
933 The body of this function is taken directly from the GNU C Library;
934 see src/strftime.c. */
936 tm_diff (struct tm const *a, struct tm const *b)
938 /* Compute intervening leap days correctly even if year is negative.
939 Take care to avoid int overflow in leap day calculations. */
940 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
941 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
942 int a100 = a4 / 25 - (a4 % 25 < 0);
943 int b100 = b4 / 25 - (b4 % 25 < 0);
944 int a400 = SHR (a100, 2);
945 int b400 = SHR (b100, 2);
946 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
947 long int ayear = a->tm_year;
948 long int years = ayear - b->tm_year;
949 long int days = (365 * years + intervening_leap_days
950 + (a->tm_yday - b->tm_yday));
951 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
952 + (a->tm_min - b->tm_min))
953 + (a->tm_sec - b->tm_sec));
955 #endif /* ! HAVE_TM_GMTOFF */
958 lookup_word (parser_control const *pc, char *word)
967 /* Make it uppercase. */
968 for (p = word; *p; p++)
970 unsigned char ch = *p;
974 for (tp = meridian_table; tp->name; tp++)
975 if (strcmp (word, tp->name) == 0)
978 /* See if we have an abbreviation for a month. */
979 wordlen = strlen (word);
980 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
982 for (tp = month_and_day_table; tp->name; tp++)
983 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
986 if ((tp = lookup_zone (pc, word)))
989 if (strcmp (word, dst_table[0].name) == 0)
992 for (tp = time_units_table; tp->name; tp++)
993 if (strcmp (word, tp->name) == 0)
996 /* Strip off any plural and try the units table again. */
997 if (word[wordlen - 1] == 'S')
999 word[wordlen - 1] = '\0';
1000 for (tp = time_units_table; tp->name; tp++)
1001 if (strcmp (word, tp->name) == 0)
1003 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
1006 for (tp = relative_time_table; tp->name; tp++)
1007 if (strcmp (word, tp->name) == 0)
1010 /* Military time zones. */
1012 for (tp = military_table; tp->name; tp++)
1013 if (word[0] == tp->name[0])
1016 /* Drop out any periods and try the time zone table again. */
1017 for (period_found = false, p = q = word; (*p = *q); q++)
1019 period_found = true;
1022 if (period_found && (tp = lookup_zone (pc, word)))
1029 yylex (YYSTYPE *lvalp, parser_control *pc)
1036 while (c = *pc->input, c_isspace (c))
1039 if (ISDIGIT (c) || c == '-' || c == '+')
1043 unsigned long int value;
1044 if (c == '-' || c == '+')
1046 sign = c == '-' ? -1 : 1;
1047 while (c = *++pc->input, c_isspace (c))
1050 /* skip the '-' sign */
1056 for (value = 0; ; value *= 10)
1058 unsigned long int value1 = value + (c - '0');
1065 if (ULONG_MAX / 10 < value)
1068 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1073 unsigned long int value1;
1075 /* Check for overflow when converting value to time_t. */
1090 if (value != value1)
1093 /* Accumulate fraction, to ns precision. */
1096 for (digits = 2; digits <= LOG10_BILLION; digits++)
1103 /* Skip excess digits, truncating toward -Infinity. */
1105 for (; ISDIGIT (*p); p++)
1111 while (ISDIGIT (*p))
1114 /* Adjust to the timespec convention, which is that
1115 tv_nsec is always a positive offset even if tv_sec is
1125 lvalp->timespec.tv_sec = s;
1126 lvalp->timespec.tv_nsec = ns;
1128 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1132 lvalp->textintval.negative = sign < 0;
1135 lvalp->textintval.value = - value;
1136 if (0 < lvalp->textintval.value)
1141 lvalp->textintval.value = value;
1142 if (lvalp->textintval.value < 0)
1145 lvalp->textintval.digits = p - pc->input;
1147 return sign ? tSNUMBER : tUNUMBER;
1159 if (p - buff < sizeof buff - 1)
1163 while (c_isalpha (c) || c == '.');
1166 tp = lookup_word (pc, buff);
1169 lvalp->intval = tp->value;
1174 return *pc->input++;
1190 /* Do nothing if the parser reports an error. */
1192 yyerror (parser_control const *pc _GL_UNUSED,
1193 char const *s _GL_UNUSED)
1198 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1199 passing it to mktime, return true if it's OK that mktime returned T.
1200 It's not OK if *TM0 has out-of-range members. */
1203 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1205 if (t == (time_t) -1)
1207 /* Guard against falsely reporting an error when parsing a time
1208 stamp that happens to equal (time_t) -1, on a host that
1209 supports such a time stamp. */
1210 tm1 = localtime (&t);
1215 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1216 | (tm0->tm_min ^ tm1->tm_min)
1217 | (tm0->tm_hour ^ tm1->tm_hour)
1218 | (tm0->tm_mday ^ tm1->tm_mday)
1219 | (tm0->tm_mon ^ tm1->tm_mon)
1220 | (tm0->tm_year ^ tm1->tm_year));
1223 /* A reasonable upper bound for the size of ordinary TZ strings.
1224 Use heap allocation if TZ's length exceeds this. */
1225 enum { TZBUFSIZE = 100 };
1227 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1230 get_tz (char tzbuf[TZBUFSIZE])
1232 char *tz = getenv ("TZ");
1235 size_t tzsize = strlen (tz) + 1;
1236 tz = (tzsize <= TZBUFSIZE
1237 ? memcpy (tzbuf, tz, tzsize)
1238 : xmemdup (tz, tzsize));
1243 /* Parse a date/time string, storing the resulting time value into *RESULT.
1244 The string itself is pointed to by P. Return true if successful.
1245 P can be an incomplete or relative time specification; if so, use
1246 *NOW as the basis for the returned time. */
1248 parse_datetime (struct timespec *result, char const *p,
1249 struct timespec const *now)
1253 struct tm const *tmp;
1257 struct timespec gettime_buffer;
1259 bool tz_was_altered = false;
1261 char tz0buf[TZBUFSIZE];
1266 gettime (&gettime_buffer);
1267 now = &gettime_buffer;
1270 Start = now->tv_sec;
1271 Start_ns = now->tv_nsec;
1273 tmp = localtime (&now->tv_sec);
1277 while (c = *p, c_isspace (c))
1280 if (strncmp (p, "TZ=\"", 4) == 0)
1282 char const *tzbase = p + 4;
1286 for (s = tzbase; *s; s++, tzsize++)
1290 if (! (*s == '\\' || *s == '"'))
1297 char tz1buf[TZBUFSIZE];
1298 bool large_tz = TZBUFSIZE < tzsize;
1300 /* Free tz0, in case this is the 2nd or subsequent time through. */
1302 tz0 = get_tz (tz0buf);
1303 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1304 for (s = tzbase; *s != '"'; s++)
1305 *z++ = *(s += *s == '\\');
1307 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1312 tz_was_altered = true;
1317 /* As documented, be careful to treat the empty string just like
1318 a date string of "0". Without this, an empty string would be
1319 declared invalid when parsed during a DST transition. */
1324 pc.year.value = tmp->tm_year;
1325 pc.year.value += TM_YEAR_BASE;
1327 pc.month = tmp->tm_mon + 1;
1328 pc.day = tmp->tm_mday;
1329 pc.hour = tmp->tm_hour;
1330 pc.minutes = tmp->tm_min;
1331 pc.seconds.tv_sec = tmp->tm_sec;
1332 pc.seconds.tv_nsec = Start_ns;
1333 tm.tm_isdst = tmp->tm_isdst;
1335 pc.meridian = MER24;
1336 pc.rel = RELATIVE_TIME_0;
1337 pc.timespec_seen = false;
1338 pc.rels_seen = false;
1342 pc.local_zones_seen = 0;
1346 #if HAVE_STRUCT_TM_TM_ZONE
1347 pc.local_time_zone_table[0].name = tmp->tm_zone;
1348 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1349 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1350 pc.local_time_zone_table[1].name = NULL;
1352 /* Probe the names used in the next three calendar quarters, looking
1353 for a tm_isdst different from the one we already have. */
1356 for (quarter = 1; quarter <= 3; quarter++)
1358 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1359 struct tm const *probe_tm = localtime (&probe);
1360 if (probe_tm && probe_tm->tm_zone
1361 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1364 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1365 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1366 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1367 pc.local_time_zone_table[2].name = NULL;
1376 # if !HAVE_DECL_TZNAME
1377 extern char *tzname[];
1380 for (i = 0; i < 2; i++)
1382 pc.local_time_zone_table[i].name = tzname[i];
1383 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1384 pc.local_time_zone_table[i].value = i;
1386 pc.local_time_zone_table[i].name = NULL;
1389 pc.local_time_zone_table[0].name = NULL;
1393 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1394 && ! strcmp (pc.local_time_zone_table[0].name,
1395 pc.local_time_zone_table[1].name))
1397 /* This locale uses the same abbreviation for standard and
1398 daylight times. So if we see that abbreviation, we don't
1399 know whether it's daylight time. */
1400 pc.local_time_zone_table[0].value = -1;
1401 pc.local_time_zone_table[1].name = NULL;
1404 if (yyparse (&pc) != 0)
1407 if (pc.timespec_seen)
1408 *result = pc.seconds;
1411 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1412 | (pc.local_zones_seen + pc.zones_seen)))
1415 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1416 tm.tm_mon = pc.month - 1;
1417 tm.tm_mday = pc.day;
1418 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1420 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1423 tm.tm_min = pc.minutes;
1424 tm.tm_sec = pc.seconds.tv_sec;
1428 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1429 pc.seconds.tv_nsec = 0;
1432 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1433 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1436 /* But if the input explicitly specifies local time with or without
1437 DST, give mktime that information. */
1438 if (pc.local_zones_seen)
1439 tm.tm_isdst = pc.local_isdst;
1443 Start = mktime (&tm);
1445 if (! mktime_ok (&tm0, &tm, Start))
1447 if (! pc.zones_seen)
1451 /* Guard against falsely reporting errors near the time_t
1452 boundaries when parsing times in other time zones. For
1453 example, suppose the input string "1969-12-31 23:00:00 -0100",
1454 the current time zone is 8 hours ahead of UTC, and the min
1455 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1456 localtime value is 1970-01-01 08:00:00, and mktime will
1457 therefore fail on 1969-12-31 23:00:00. To work around the
1458 problem, set the time zone to 1 hour behind UTC temporarily
1459 by setting TZ="XXX1:00" and try mktime again. */
1461 long int time_zone = pc.time_zone;
1462 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1463 long int abs_time_zone_hour = abs_time_zone / 60;
1464 int abs_time_zone_min = abs_time_zone % 60;
1465 char tz1buf[sizeof "XXX+0:00"
1466 + sizeof pc.time_zone * CHAR_BIT / 3];
1467 if (!tz_was_altered)
1468 tz0 = get_tz (tz0buf);
1469 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1470 abs_time_zone_hour, abs_time_zone_min);
1471 if (setenv ("TZ", tz1buf, 1) != 0)
1473 tz_was_altered = true;
1475 Start = mktime (&tm);
1476 if (! mktime_ok (&tm0, &tm, Start))
1481 if (pc.days_seen && ! pc.dates_seen)
1483 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1484 + 7 * (pc.day_ordinal
1485 - (0 < pc.day_ordinal
1486 && tm.tm_wday != pc.day_number)));
1488 Start = mktime (&tm);
1489 if (Start == (time_t) -1)
1493 /* Add relative date. */
1494 if (pc.rel.year | pc.rel.month | pc.rel.day)
1496 int year = tm.tm_year + pc.rel.year;
1497 int month = tm.tm_mon + pc.rel.month;
1498 int day = tm.tm_mday + pc.rel.day;
1499 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1500 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1501 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1506 tm.tm_hour = tm0.tm_hour;
1507 tm.tm_min = tm0.tm_min;
1508 tm.tm_sec = tm0.tm_sec;
1509 tm.tm_isdst = tm0.tm_isdst;
1510 Start = mktime (&tm);
1511 if (Start == (time_t) -1)
1515 /* The only "output" of this if-block is an updated Start value,
1516 so this block must follow others that clobber Start. */
1519 long int delta = pc.time_zone * 60;
1521 #ifdef HAVE_TM_GMTOFF
1522 delta -= tm.tm_gmtoff;
1525 struct tm const *gmt = gmtime (&t);
1528 delta -= tm_diff (&tm, gmt);
1531 if ((Start < t1) != (delta < 0))
1532 goto fail; /* time_t overflow */
1536 /* Add relative hours, minutes, and seconds. On hosts that support
1537 leap seconds, ignore the possibility of leap seconds; e.g.,
1538 "+ 10 minutes" adds 600 seconds, even if one of them is a
1539 leap second. Typically this is not what the user wants, but it's
1540 too hard to do it the other way, because the time zone indicator
1541 must be applied before relative times, and if mktime is applied
1542 again the time zone will be lost. */
1544 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1545 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1547 long int d1 = 60 * 60 * pc.rel.hour;
1548 time_t t1 = t0 + d1;
1549 long int d2 = 60 * pc.rel.minutes;
1550 time_t t2 = t1 + d2;
1551 long_time_t d3 = pc.rel.seconds;
1552 long_time_t t3 = t2 + d3;
1553 long int d4 = (sum_ns - normalized_ns) / BILLION;
1554 long_time_t t4 = t3 + d4;
1557 if ((d1 / (60 * 60) ^ pc.rel.hour)
1558 | (d2 / 60 ^ pc.rel.minutes)
1559 | ((t1 < t0) ^ (d1 < 0))
1560 | ((t2 < t1) ^ (d2 < 0))
1561 | ((t3 < t2) ^ (d3 < 0))
1562 | ((t4 < t3) ^ (d4 < 0))
1566 result->tv_sec = t5;
1567 result->tv_nsec = normalized_ns;
1577 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1586 main (int ac, char **av)
1590 printf ("Enter date, or blank line to exit.\n\t> ");
1593 buff[BUFSIZ - 1] = '\0';
1594 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1597 struct tm const *tm;
1598 if (! parse_datetime (&d, buff, NULL))
1599 printf ("Bad format - couldn't convert.\n");
1600 else if (! (tm = localtime (&d.tv_sec)))
1602 long int sec = d.tv_sec;
1603 printf ("localtime (%ld) failed\n", sec);
1608 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1609 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1610 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);