2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999-2000, 2002-2011 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 abbrevations, 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;
301 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
302 %token <intval> tDAY_UNIT tDAY_SHIFT
304 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
305 %token <intval> tMONTH tORDINAL tZONE
307 %token <textintval> tSNUMBER tUNUMBER
308 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
310 %type <intval> o_colon_minutes
311 %type <timespec> seconds signed_seconds unsigned_seconds
313 %type <rel> relunit relunit_snumber dayshift
326 pc->timespec_seen = true;
337 { pc->times_seen++; pc->dates_seen++; }
339 { pc->times_seen++; }
341 { pc->local_zones_seen++; }
343 { pc->zones_seen++; }
345 { pc->dates_seen++; }
358 iso_8601_date 'T' iso_8601_time
364 set_hhmmss (pc, $1.value, 0, 0, 0);
367 | tUNUMBER ':' tUNUMBER tMERIDIAN
369 set_hhmmss (pc, $1.value, $3.value, 0, 0);
372 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN
374 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
383 set_hhmmss (pc, $1.value, 0, 0, 0);
384 pc->meridian = MER24;
386 | tUNUMBER ':' tUNUMBER o_zone_offset
388 set_hhmmss (pc, $1.value, $3.value, 0, 0);
389 pc->meridian = MER24;
391 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_zone_offset
393 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
394 pc->meridian = MER24;
404 tSNUMBER o_colon_minutes
407 pc->time_zone = time_zone_hhmm (pc, $1, $2);
414 pc->local_isdst = $1;
415 pc->dsts_seen += (0 < $1);
420 pc->dsts_seen += (0 < $1) + 1;
424 /* Note 'T' is a special case, as it is used as the separator in ISO
425 8601 date and time of day representation. */
428 { pc->time_zone = $1; }
430 { pc->time_zone = HOUR(7); }
431 | tZONE relunit_snumber
432 { pc->time_zone = $1;
433 apply_relative_time (pc, $2, 1); }
434 | 'T' relunit_snumber
435 { pc->time_zone = HOUR(7);
436 apply_relative_time (pc, $2, 1); }
437 | tZONE tSNUMBER o_colon_minutes
438 { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
440 { pc->time_zone = $1 + 60; }
442 { pc->time_zone = $1 + 60; }
458 pc->day_ordinal = $1;
463 pc->day_ordinal = $1.value;
469 tUNUMBER '/' tUNUMBER
471 pc->month = $1.value;
474 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
476 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
477 otherwise as MM/DD/YY.
478 The goal in recognizing YYYY/MM/DD is solely to support legacy
479 machine-generated dates like those in an RCS log listing. If
480 you want portability, use the ISO 8601 format. */
484 pc->month = $3.value;
489 pc->month = $1.value;
494 | tUNUMBER tMONTH tSNUMBER
496 /* e.g. 17-JUN-1992. */
499 pc->year.value = -$3.value;
500 pc->year.digits = $3.digits;
502 | tMONTH tSNUMBER tSNUMBER
504 /* e.g. JUN-17-1992. */
507 pc->year.value = -$3.value;
508 pc->year.digits = $3.digits;
515 | tMONTH tUNUMBER ',' tUNUMBER
526 | tUNUMBER tMONTH tUNUMBER
536 tUNUMBER tSNUMBER tSNUMBER
538 /* ISO 8601 format. YYYY-MM-DD. */
540 pc->month = -$2.value;
547 { apply_relative_time (pc, $1, -1); }
549 { apply_relative_time (pc, $1, 1); }
551 { apply_relative_time (pc, $1, 1); }
556 { $$ = RELATIVE_TIME_0; $$.year = $1; }
557 | tUNUMBER tYEAR_UNIT
558 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
560 { $$ = RELATIVE_TIME_0; $$.year = 1; }
561 | tORDINAL tMONTH_UNIT
562 { $$ = RELATIVE_TIME_0; $$.month = $1; }
563 | tUNUMBER tMONTH_UNIT
564 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
566 { $$ = RELATIVE_TIME_0; $$.month = 1; }
568 { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
570 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
572 { $$ = RELATIVE_TIME_0; $$.day = $1; }
573 | tORDINAL tHOUR_UNIT
574 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
575 | tUNUMBER tHOUR_UNIT
576 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
578 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
579 | tORDINAL tMINUTE_UNIT
580 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
581 | tUNUMBER tMINUTE_UNIT
582 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
584 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
586 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
588 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
589 | tSDECIMAL_NUMBER tSEC_UNIT
590 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
591 | tUDECIMAL_NUMBER tSEC_UNIT
592 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
594 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
600 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
601 | tSNUMBER tMONTH_UNIT
602 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
604 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
605 | tSNUMBER tHOUR_UNIT
606 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
607 | tSNUMBER tMINUTE_UNIT
608 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
610 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
615 { $$ = RELATIVE_TIME_0; $$.day = $1; }
618 seconds: signed_seconds | unsigned_seconds;
623 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
629 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
634 { digits_to_date_time (pc, $1); }
638 tUNUMBER relunit_snumber
640 /* Hybrid all-digit and relative offset, so that we accept e.g.,
641 "YYYYMMDD +N days" as well as "YYYYMMDD N days". */
642 digits_to_date_time (pc, $1);
643 apply_relative_time (pc, $2, 1);
656 static table const meridian_table[] =
658 { "AM", tMERIDIAN, MERam },
659 { "A.M.", tMERIDIAN, MERam },
660 { "PM", tMERIDIAN, MERpm },
661 { "P.M.", tMERIDIAN, MERpm },
665 static table const dst_table[] =
670 static table const month_and_day_table[] =
672 { "JANUARY", tMONTH, 1 },
673 { "FEBRUARY", tMONTH, 2 },
674 { "MARCH", tMONTH, 3 },
675 { "APRIL", tMONTH, 4 },
676 { "MAY", tMONTH, 5 },
677 { "JUNE", tMONTH, 6 },
678 { "JULY", tMONTH, 7 },
679 { "AUGUST", tMONTH, 8 },
680 { "SEPTEMBER",tMONTH, 9 },
681 { "SEPT", tMONTH, 9 },
682 { "OCTOBER", tMONTH, 10 },
683 { "NOVEMBER", tMONTH, 11 },
684 { "DECEMBER", tMONTH, 12 },
685 { "SUNDAY", tDAY, 0 },
686 { "MONDAY", tDAY, 1 },
687 { "TUESDAY", tDAY, 2 },
689 { "WEDNESDAY",tDAY, 3 },
690 { "WEDNES", tDAY, 3 },
691 { "THURSDAY", tDAY, 4 },
693 { "THURS", tDAY, 4 },
694 { "FRIDAY", tDAY, 5 },
695 { "SATURDAY", tDAY, 6 },
699 static table const time_units_table[] =
701 { "YEAR", tYEAR_UNIT, 1 },
702 { "MONTH", tMONTH_UNIT, 1 },
703 { "FORTNIGHT",tDAY_UNIT, 14 },
704 { "WEEK", tDAY_UNIT, 7 },
705 { "DAY", tDAY_UNIT, 1 },
706 { "HOUR", tHOUR_UNIT, 1 },
707 { "MINUTE", tMINUTE_UNIT, 1 },
708 { "MIN", tMINUTE_UNIT, 1 },
709 { "SECOND", tSEC_UNIT, 1 },
710 { "SEC", tSEC_UNIT, 1 },
714 /* Assorted relative-time words. */
715 static table const relative_time_table[] =
717 { "TOMORROW", tDAY_SHIFT, 1 },
718 { "YESTERDAY",tDAY_SHIFT, -1 },
719 { "TODAY", tDAY_SHIFT, 0 },
720 { "NOW", tDAY_SHIFT, 0 },
721 { "LAST", tORDINAL, -1 },
722 { "THIS", tORDINAL, 0 },
723 { "NEXT", tORDINAL, 1 },
724 { "FIRST", tORDINAL, 1 },
725 /*{ "SECOND", tORDINAL, 2 }, */
726 { "THIRD", tORDINAL, 3 },
727 { "FOURTH", tORDINAL, 4 },
728 { "FIFTH", tORDINAL, 5 },
729 { "SIXTH", tORDINAL, 6 },
730 { "SEVENTH", tORDINAL, 7 },
731 { "EIGHTH", tORDINAL, 8 },
732 { "NINTH", tORDINAL, 9 },
733 { "TENTH", tORDINAL, 10 },
734 { "ELEVENTH", tORDINAL, 11 },
735 { "TWELFTH", tORDINAL, 12 },
740 /* The universal time zone table. These labels can be used even for
741 time stamps that would not otherwise be valid, e.g., GMT time
742 stamps in London during summer. */
743 static table const universal_time_zone_table[] =
745 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
746 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
747 { "UTC", tZONE, HOUR ( 0) },
751 /* The time zone table. This table is necessarily incomplete, as time
752 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
753 as Eastern time in Australia, not as US Eastern Standard Time.
754 You cannot rely on parse_datetime to handle arbitrary time zone
755 abbreviations; use numeric abbreviations like `-0500' instead. */
756 static table const time_zone_table[] =
758 { "WET", tZONE, HOUR ( 0) }, /* Western European */
759 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
760 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
761 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
762 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
763 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
764 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
765 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
766 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
767 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
768 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
769 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
770 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
771 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
772 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
773 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
774 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
775 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
776 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
777 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
778 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
779 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
780 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
781 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
782 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
783 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
784 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
785 { "CET", tZONE, HOUR ( 1) }, /* Central European */
786 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
787 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
788 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
789 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
790 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
791 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
792 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
793 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
794 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
795 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
796 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
797 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
798 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
799 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
800 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
801 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
802 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
803 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
804 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
808 /* Military time zone table.
810 Note 'T' is a special case, as it is used as the separator in ISO
811 8601 date and time of day representation. */
812 static table const military_table[] =
814 { "A", tZONE, -HOUR ( 1) },
815 { "B", tZONE, -HOUR ( 2) },
816 { "C", tZONE, -HOUR ( 3) },
817 { "D", tZONE, -HOUR ( 4) },
818 { "E", tZONE, -HOUR ( 5) },
819 { "F", tZONE, -HOUR ( 6) },
820 { "G", tZONE, -HOUR ( 7) },
821 { "H", tZONE, -HOUR ( 8) },
822 { "I", tZONE, -HOUR ( 9) },
823 { "K", tZONE, -HOUR (10) },
824 { "L", tZONE, -HOUR (11) },
825 { "M", tZONE, -HOUR (12) },
826 { "N", tZONE, HOUR ( 1) },
827 { "O", tZONE, HOUR ( 2) },
828 { "P", tZONE, HOUR ( 3) },
829 { "Q", tZONE, HOUR ( 4) },
830 { "R", tZONE, HOUR ( 5) },
831 { "S", tZONE, HOUR ( 6) },
833 { "U", tZONE, HOUR ( 8) },
834 { "V", tZONE, HOUR ( 9) },
835 { "W", tZONE, HOUR (10) },
836 { "X", tZONE, HOUR (11) },
837 { "Y", tZONE, HOUR (12) },
838 { "Z", tZONE, HOUR ( 0) },
844 /* Convert a time zone expressed as HH:MM into an integer count of
845 minutes. If MM is negative, then S is of the form HHMM and needs
846 to be picked apart; otherwise, S is of the form HH. As specified in
847 http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
848 only valid TZ range, and consider first two digits as hours, if no
849 minutes specified. */
852 time_zone_hhmm (parser_control *pc, textint s, long int mm)
856 /* If the length of S is 1 or 2 and no minutes are specified,
857 interpret it as a number of hours. */
858 if (s.digits <= 2 && mm < 0)
862 n_minutes = (s.value / 100) * 60 + s.value % 100;
864 n_minutes = s.value * 60 + (s.negative ? -mm : mm);
866 /* If the absolute number of minutes is larger than 24 hours,
867 arrange to reject it by incrementing pc->zones_seen. Thus,
868 we allow only values in the range UTC-24:00 to UTC+24:00. */
869 if (24 * 60 < abs (n_minutes))
876 to_hour (long int hours, int meridian)
880 default: /* Pacify GCC. */
882 return 0 <= hours && hours < 24 ? hours : -1;
884 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
886 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
891 to_year (textint textyear)
893 long int year = textyear.value;
898 /* XPG4 suggests that years 00-68 map to 2000-2068, and
899 years 69-99 map to 1969-1999. */
900 else if (textyear.digits == 2)
901 year += year < 69 ? 2000 : 1900;
907 lookup_zone (parser_control const *pc, char const *name)
911 for (tp = universal_time_zone_table; tp->name; tp++)
912 if (strcmp (name, tp->name) == 0)
915 /* Try local zone abbreviations before those in time_zone_table, as
916 the local ones are more likely to be right. */
917 for (tp = pc->local_time_zone_table; tp->name; tp++)
918 if (strcmp (name, tp->name) == 0)
921 for (tp = time_zone_table; tp->name; tp++)
922 if (strcmp (name, tp->name) == 0)
929 /* Yield the difference between *A and *B,
930 measured in seconds, ignoring leap seconds.
931 The body of this function is taken directly from the GNU C Library;
932 see src/strftime.c. */
934 tm_diff (struct tm const *a, struct tm const *b)
936 /* Compute intervening leap days correctly even if year is negative.
937 Take care to avoid int overflow in leap day calculations. */
938 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
939 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
940 int a100 = a4 / 25 - (a4 % 25 < 0);
941 int b100 = b4 / 25 - (b4 % 25 < 0);
942 int a400 = SHR (a100, 2);
943 int b400 = SHR (b100, 2);
944 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
945 long int ayear = a->tm_year;
946 long int years = ayear - b->tm_year;
947 long int days = (365 * years + intervening_leap_days
948 + (a->tm_yday - b->tm_yday));
949 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
950 + (a->tm_min - b->tm_min))
951 + (a->tm_sec - b->tm_sec));
953 #endif /* ! HAVE_TM_GMTOFF */
956 lookup_word (parser_control const *pc, char *word)
965 /* Make it uppercase. */
966 for (p = word; *p; p++)
968 unsigned char ch = *p;
972 for (tp = meridian_table; tp->name; tp++)
973 if (strcmp (word, tp->name) == 0)
976 /* See if we have an abbreviation for a month. */
977 wordlen = strlen (word);
978 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
980 for (tp = month_and_day_table; tp->name; tp++)
981 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
984 if ((tp = lookup_zone (pc, word)))
987 if (strcmp (word, dst_table[0].name) == 0)
990 for (tp = time_units_table; tp->name; tp++)
991 if (strcmp (word, tp->name) == 0)
994 /* Strip off any plural and try the units table again. */
995 if (word[wordlen - 1] == 'S')
997 word[wordlen - 1] = '\0';
998 for (tp = time_units_table; tp->name; tp++)
999 if (strcmp (word, tp->name) == 0)
1001 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
1004 for (tp = relative_time_table; tp->name; tp++)
1005 if (strcmp (word, tp->name) == 0)
1008 /* Military time zones. */
1010 for (tp = military_table; tp->name; tp++)
1011 if (word[0] == tp->name[0])
1014 /* Drop out any periods and try the time zone table again. */
1015 for (period_found = false, p = q = word; (*p = *q); q++)
1017 period_found = true;
1020 if (period_found && (tp = lookup_zone (pc, word)))
1027 yylex (YYSTYPE *lvalp, parser_control *pc)
1034 while (c = *pc->input, c_isspace (c))
1037 if (ISDIGIT (c) || c == '-' || c == '+')
1041 unsigned long int value;
1042 if (c == '-' || c == '+')
1044 sign = c == '-' ? -1 : 1;
1045 while (c = *++pc->input, c_isspace (c))
1048 /* skip the '-' sign */
1054 for (value = 0; ; value *= 10)
1056 unsigned long int value1 = value + (c - '0');
1063 if (ULONG_MAX / 10 < value)
1066 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1071 unsigned long int value1;
1073 /* Check for overflow when converting value to time_t. */
1088 if (value != value1)
1091 /* Accumulate fraction, to ns precision. */
1094 for (digits = 2; digits <= LOG10_BILLION; digits++)
1101 /* Skip excess digits, truncating toward -Infinity. */
1103 for (; ISDIGIT (*p); p++)
1109 while (ISDIGIT (*p))
1112 /* Adjust to the timespec convention, which is that
1113 tv_nsec is always a positive offset even if tv_sec is
1123 lvalp->timespec.tv_sec = s;
1124 lvalp->timespec.tv_nsec = ns;
1126 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1130 lvalp->textintval.negative = sign < 0;
1133 lvalp->textintval.value = - value;
1134 if (0 < lvalp->textintval.value)
1139 lvalp->textintval.value = value;
1140 if (lvalp->textintval.value < 0)
1143 lvalp->textintval.digits = p - pc->input;
1145 return sign ? tSNUMBER : tUNUMBER;
1157 if (p - buff < sizeof buff - 1)
1161 while (c_isalpha (c) || c == '.');
1164 tp = lookup_word (pc, buff);
1167 lvalp->intval = tp->value;
1172 return *pc->input++;
1188 /* Do nothing if the parser reports an error. */
1190 yyerror (parser_control const *pc _GL_UNUSED,
1191 char const *s _GL_UNUSED)
1196 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1197 passing it to mktime, return true if it's OK that mktime returned T.
1198 It's not OK if *TM0 has out-of-range members. */
1201 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1203 if (t == (time_t) -1)
1205 /* Guard against falsely reporting an error when parsing a time
1206 stamp that happens to equal (time_t) -1, on a host that
1207 supports such a time stamp. */
1208 tm1 = localtime (&t);
1213 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1214 | (tm0->tm_min ^ tm1->tm_min)
1215 | (tm0->tm_hour ^ tm1->tm_hour)
1216 | (tm0->tm_mday ^ tm1->tm_mday)
1217 | (tm0->tm_mon ^ tm1->tm_mon)
1218 | (tm0->tm_year ^ tm1->tm_year));
1221 /* A reasonable upper bound for the size of ordinary TZ strings.
1222 Use heap allocation if TZ's length exceeds this. */
1223 enum { TZBUFSIZE = 100 };
1225 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1228 get_tz (char tzbuf[TZBUFSIZE])
1230 char *tz = getenv ("TZ");
1233 size_t tzsize = strlen (tz) + 1;
1234 tz = (tzsize <= TZBUFSIZE
1235 ? memcpy (tzbuf, tz, tzsize)
1236 : xmemdup (tz, tzsize));
1241 /* Parse a date/time string, storing the resulting time value into *RESULT.
1242 The string itself is pointed to by P. Return true if successful.
1243 P can be an incomplete or relative time specification; if so, use
1244 *NOW as the basis for the returned time. */
1246 parse_datetime (struct timespec *result, char const *p,
1247 struct timespec const *now)
1251 struct tm const *tmp;
1255 struct timespec gettime_buffer;
1257 bool tz_was_altered = false;
1259 char tz0buf[TZBUFSIZE];
1264 gettime (&gettime_buffer);
1265 now = &gettime_buffer;
1268 Start = now->tv_sec;
1269 Start_ns = now->tv_nsec;
1271 tmp = localtime (&now->tv_sec);
1275 while (c = *p, c_isspace (c))
1278 if (strncmp (p, "TZ=\"", 4) == 0)
1280 char const *tzbase = p + 4;
1284 for (s = tzbase; *s; s++, tzsize++)
1288 if (! (*s == '\\' || *s == '"'))
1295 char tz1buf[TZBUFSIZE];
1296 bool large_tz = TZBUFSIZE < tzsize;
1298 /* Free tz0, in case this is the 2nd or subsequent time through. */
1300 tz0 = get_tz (tz0buf);
1301 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1302 for (s = tzbase; *s != '"'; s++)
1303 *z++ = *(s += *s == '\\');
1305 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1310 tz_was_altered = true;
1315 /* As documented, be careful to treat the empty string just like
1316 a date string of "0". Without this, an empty string would be
1317 declared invalid when parsed during a DST transition. */
1322 pc.year.value = tmp->tm_year;
1323 pc.year.value += TM_YEAR_BASE;
1325 pc.month = tmp->tm_mon + 1;
1326 pc.day = tmp->tm_mday;
1327 pc.hour = tmp->tm_hour;
1328 pc.minutes = tmp->tm_min;
1329 pc.seconds.tv_sec = tmp->tm_sec;
1330 pc.seconds.tv_nsec = Start_ns;
1331 tm.tm_isdst = tmp->tm_isdst;
1333 pc.meridian = MER24;
1334 pc.rel = RELATIVE_TIME_0;
1335 pc.timespec_seen = false;
1336 pc.rels_seen = false;
1340 pc.local_zones_seen = 0;
1344 #if HAVE_STRUCT_TM_TM_ZONE
1345 pc.local_time_zone_table[0].name = tmp->tm_zone;
1346 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1347 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1348 pc.local_time_zone_table[1].name = NULL;
1350 /* Probe the names used in the next three calendar quarters, looking
1351 for a tm_isdst different from the one we already have. */
1354 for (quarter = 1; quarter <= 3; quarter++)
1356 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1357 struct tm const *probe_tm = localtime (&probe);
1358 if (probe_tm && probe_tm->tm_zone
1359 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1362 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1363 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1364 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1365 pc.local_time_zone_table[2].name = NULL;
1374 # if !HAVE_DECL_TZNAME
1375 extern char *tzname[];
1378 for (i = 0; i < 2; i++)
1380 pc.local_time_zone_table[i].name = tzname[i];
1381 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1382 pc.local_time_zone_table[i].value = i;
1384 pc.local_time_zone_table[i].name = NULL;
1387 pc.local_time_zone_table[0].name = NULL;
1391 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1392 && ! strcmp (pc.local_time_zone_table[0].name,
1393 pc.local_time_zone_table[1].name))
1395 /* This locale uses the same abbrevation for standard and
1396 daylight times. So if we see that abbreviation, we don't
1397 know whether it's daylight time. */
1398 pc.local_time_zone_table[0].value = -1;
1399 pc.local_time_zone_table[1].name = NULL;
1402 if (yyparse (&pc) != 0)
1405 if (pc.timespec_seen)
1406 *result = pc.seconds;
1409 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1410 | (pc.local_zones_seen + pc.zones_seen)))
1413 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1414 tm.tm_mon = pc.month - 1;
1415 tm.tm_mday = pc.day;
1416 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1418 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1421 tm.tm_min = pc.minutes;
1422 tm.tm_sec = pc.seconds.tv_sec;
1426 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1427 pc.seconds.tv_nsec = 0;
1430 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1431 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1434 /* But if the input explicitly specifies local time with or without
1435 DST, give mktime that information. */
1436 if (pc.local_zones_seen)
1437 tm.tm_isdst = pc.local_isdst;
1441 Start = mktime (&tm);
1443 if (! mktime_ok (&tm0, &tm, Start))
1445 if (! pc.zones_seen)
1449 /* Guard against falsely reporting errors near the time_t
1450 boundaries when parsing times in other time zones. For
1451 example, suppose the input string "1969-12-31 23:00:00 -0100",
1452 the current time zone is 8 hours ahead of UTC, and the min
1453 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1454 localtime value is 1970-01-01 08:00:00, and mktime will
1455 therefore fail on 1969-12-31 23:00:00. To work around the
1456 problem, set the time zone to 1 hour behind UTC temporarily
1457 by setting TZ="XXX1:00" and try mktime again. */
1459 long int time_zone = pc.time_zone;
1460 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1461 long int abs_time_zone_hour = abs_time_zone / 60;
1462 int abs_time_zone_min = abs_time_zone % 60;
1463 char tz1buf[sizeof "XXX+0:00"
1464 + sizeof pc.time_zone * CHAR_BIT / 3];
1465 if (!tz_was_altered)
1466 tz0 = get_tz (tz0buf);
1467 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1468 abs_time_zone_hour, abs_time_zone_min);
1469 if (setenv ("TZ", tz1buf, 1) != 0)
1471 tz_was_altered = true;
1473 Start = mktime (&tm);
1474 if (! mktime_ok (&tm0, &tm, Start))
1479 if (pc.days_seen && ! pc.dates_seen)
1481 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1482 + 7 * (pc.day_ordinal
1483 - (0 < pc.day_ordinal
1484 && tm.tm_wday != pc.day_number)));
1486 Start = mktime (&tm);
1487 if (Start == (time_t) -1)
1491 /* Add relative date. */
1492 if (pc.rel.year | pc.rel.month | pc.rel.day)
1494 int year = tm.tm_year + pc.rel.year;
1495 int month = tm.tm_mon + pc.rel.month;
1496 int day = tm.tm_mday + pc.rel.day;
1497 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1498 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1499 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1504 tm.tm_hour = tm0.tm_hour;
1505 tm.tm_min = tm0.tm_min;
1506 tm.tm_sec = tm0.tm_sec;
1507 tm.tm_isdst = tm0.tm_isdst;
1508 Start = mktime (&tm);
1509 if (Start == (time_t) -1)
1513 /* The only "output" of this if-block is an updated Start value,
1514 so this block must follow others that clobber Start. */
1517 long int delta = pc.time_zone * 60;
1519 #ifdef HAVE_TM_GMTOFF
1520 delta -= tm.tm_gmtoff;
1523 struct tm const *gmt = gmtime (&t);
1526 delta -= tm_diff (&tm, gmt);
1529 if ((Start < t1) != (delta < 0))
1530 goto fail; /* time_t overflow */
1534 /* Add relative hours, minutes, and seconds. On hosts that support
1535 leap seconds, ignore the possibility of leap seconds; e.g.,
1536 "+ 10 minutes" adds 600 seconds, even if one of them is a
1537 leap second. Typically this is not what the user wants, but it's
1538 too hard to do it the other way, because the time zone indicator
1539 must be applied before relative times, and if mktime is applied
1540 again the time zone will be lost. */
1542 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1543 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1545 long int d1 = 60 * 60 * pc.rel.hour;
1546 time_t t1 = t0 + d1;
1547 long int d2 = 60 * pc.rel.minutes;
1548 time_t t2 = t1 + d2;
1549 long_time_t d3 = pc.rel.seconds;
1550 long_time_t t3 = t2 + d3;
1551 long int d4 = (sum_ns - normalized_ns) / BILLION;
1552 long_time_t t4 = t3 + d4;
1555 if ((d1 / (60 * 60) ^ pc.rel.hour)
1556 | (d2 / 60 ^ pc.rel.minutes)
1557 | ((t1 < t0) ^ (d1 < 0))
1558 | ((t2 < t1) ^ (d2 < 0))
1559 | ((t3 < t2) ^ (d3 < 0))
1560 | ((t4 < t3) ^ (d4 < 0))
1564 result->tv_sec = t5;
1565 result->tv_nsec = normalized_ns;
1575 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1584 main (int ac, char **av)
1588 printf ("Enter date, or blank line to exit.\n\t> ");
1591 buff[BUFSIZ - 1] = '\0';
1592 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1595 struct tm const *tm;
1596 if (! parse_datetime (&d, buff, NULL))
1597 printf ("Bad format - couldn't convert.\n");
1598 else if (! (tm = localtime (&d.tv_sec)))
1600 long int sec = d.tv_sec;
1601 printf ("localtime (%ld) failed\n", sec);
1606 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1607 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1608 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);