2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
5 2010 Free Software Foundation, Inc.
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. */
72 /* ISDIGIT differs from isdigit, as follows:
73 - Its arg may be any int or unsigned int; it need not be an unsigned char
75 - It's typically faster.
76 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
77 isdigit unless it's important to use the locale's definition
78 of `digit' even when the host does not conform to POSIX. */
79 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
81 /* Shift A right by B bits portably, by dividing A by 2**B and
82 truncating towards minus infinity. A and B should be free of side
83 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
84 INT_BITS is the number of useful bits in an int. GNU code can
85 assume that INT_BITS is at least 32.
87 ISO C99 says that A >> B is implementation-defined if A < 0. Some
88 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
89 right in the usual way when A < 0, so SHR falls back on division if
90 ordinary A >> B doesn't seem to be the usual signed shift. */
94 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
96 #define EPOCH_YEAR 1970
97 #define TM_YEAR_BASE 1900
99 #define HOUR(x) ((x) * 60)
101 /* long_time_t is a signed integer type that contains all time_t values. */
102 verify (TYPE_IS_INTEGER (time_t));
103 #if TIME_T_FITS_IN_LONG_INT
104 typedef long int long_time_t;
106 typedef time_t long_time_t;
109 /* Lots of this code assumes time_t and time_t-like values fit into
111 verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t)
112 && TYPE_MAXIMUM (time_t) <= TYPE_MAXIMUM (long_time_t));
114 /* FIXME: It also assumes that signed integer overflow silently wraps around,
115 but this is not true any more with recent versions of GCC 4. */
117 /* An integer value, and the number of digits in its textual
126 /* An entry in the lexical lookup table. */
134 /* Meridian: am, pm, or 24-hour style. */
135 enum { MERam, MERpm, MER24 };
137 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
139 /* Relative times. */
142 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
152 #if HAVE_COMPOUND_LITERALS
153 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
155 static relative_time const RELATIVE_TIME_0;
158 /* Information passed to and from the parser. */
161 /* The input string remaining to be parsed. */
164 /* N, if this is the Nth Tuesday. */
165 long int day_ordinal;
167 /* Day of week; Sunday is 0. */
170 /* tm_isdst flag for the local zone. */
173 /* Time zone, in minutes east of UTC. */
176 /* Style used for time. */
179 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
185 struct timespec seconds; /* includes nanoseconds */
187 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
190 /* Presence or counts of nonterminals of various flavors parsed so far. */
195 size_t local_zones_seen;
200 /* Table of local time zone abbrevations, terminated by a null entry. */
201 table local_time_zone_table[3];
205 static int yylex (union YYSTYPE *, parser_control *);
206 static int yyerror (parser_control const *, char const *);
207 static long int time_zone_hhmm (parser_control *, textint, long int);
209 /* Extract into *PC any date and time info from a string of digits
210 of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
213 digits_to_date_time (parser_control *pc, textint text_int)
215 if (pc->dates_seen && ! pc->year.digits
216 && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
220 if (4 < text_int.digits)
223 pc->day = text_int.value % 100;
224 pc->month = (text_int.value / 100) % 100;
225 pc->year.value = text_int.value / 10000;
226 pc->year.digits = text_int.digits - 4;
231 if (text_int.digits <= 2)
233 pc->hour = text_int.value;
238 pc->hour = text_int.value / 100;
239 pc->minutes = text_int.value % 100;
241 pc->seconds.tv_sec = 0;
242 pc->seconds.tv_nsec = 0;
243 pc->meridian = MER24;
248 /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). */
250 apply_relative_time (parser_control *pc, relative_time rel, int factor)
252 pc->rel.ns += factor * rel.ns;
253 pc->rel.seconds += factor * rel.seconds;
254 pc->rel.minutes += factor * rel.minutes;
255 pc->rel.hour += factor * rel.hour;
256 pc->rel.day += factor * rel.day;
257 pc->rel.month += factor * rel.month;
258 pc->rel.year += factor * rel.year;
259 pc->rels_seen = true;
262 /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */
264 set_hhmmss (parser_control *pc, long int hour, long int minutes,
265 time_t sec, long int nsec)
268 pc->minutes = minutes;
269 pc->seconds.tv_sec = sec;
270 pc->seconds.tv_nsec = nsec;
275 /* We want a reentrant parser, even if the TZ manipulation and the calls to
276 localtime and gmtime are not reentrant. */
278 %parse-param { parser_control *pc }
279 %lex-param { parser_control *pc }
281 /* This grammar has 20 shift/reduce conflicts. */
288 struct timespec timespec;
294 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
295 %token <intval> tDAY_UNIT tDAY_SHIFT
297 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
298 %token <intval> tMONTH tORDINAL tZONE
300 %token <textintval> tSNUMBER tUNUMBER
301 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
303 %type <intval> o_colon_minutes o_merid
304 %type <timespec> seconds signed_seconds unsigned_seconds
306 %type <rel> relunit relunit_snumber dayshift
319 pc->timespec_seen = true;
330 { pc->times_seen++; }
332 { pc->local_zones_seen++; }
334 { pc->zones_seen++; }
336 { pc->dates_seen++; }
347 set_hhmmss (pc, $1.value, 0, 0, 0);
350 | tUNUMBER ':' tUNUMBER o_merid
352 set_hhmmss (pc, $1.value, $3.value, 0, 0);
355 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
357 set_hhmmss (pc, $1.value, $3.value, 0, 0);
358 pc->meridian = MER24;
360 pc->time_zone = time_zone_hhmm (pc, $4, $5);
362 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
364 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
367 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
369 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
370 pc->meridian = MER24;
372 pc->time_zone = time_zone_hhmm (pc, $6, $7);
379 pc->local_isdst = $1;
380 pc->dsts_seen += (0 < $1);
385 pc->dsts_seen += (0 < $1) + 1;
391 { pc->time_zone = $1; }
392 | tZONE relunit_snumber
393 { pc->time_zone = $1;
394 apply_relative_time (pc, $2, 1); }
395 | tZONE tSNUMBER o_colon_minutes
396 { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
398 { pc->time_zone = $1 + 60; }
400 { pc->time_zone = $1 + 60; }
416 pc->day_ordinal = $1;
421 pc->day_ordinal = $1.value;
427 tUNUMBER '/' tUNUMBER
429 pc->month = $1.value;
432 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
434 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
435 otherwise as MM/DD/YY.
436 The goal in recognizing YYYY/MM/DD is solely to support legacy
437 machine-generated dates like those in an RCS log listing. If
438 you want portability, use the ISO 8601 format. */
442 pc->month = $3.value;
447 pc->month = $1.value;
452 | tUNUMBER tSNUMBER tSNUMBER
454 /* ISO 8601 format. YYYY-MM-DD. */
456 pc->month = -$2.value;
459 | tUNUMBER tMONTH tSNUMBER
461 /* e.g. 17-JUN-1992. */
464 pc->year.value = -$3.value;
465 pc->year.digits = $3.digits;
467 | tMONTH tSNUMBER tSNUMBER
469 /* e.g. JUN-17-1992. */
472 pc->year.value = -$3.value;
473 pc->year.digits = $3.digits;
480 | tMONTH tUNUMBER ',' tUNUMBER
491 | tUNUMBER tMONTH tUNUMBER
501 { apply_relative_time (pc, $1, -1); }
503 { apply_relative_time (pc, $1, 1); }
505 { apply_relative_time (pc, $1, 1); }
510 { $$ = RELATIVE_TIME_0; $$.year = $1; }
511 | tUNUMBER tYEAR_UNIT
512 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
514 { $$ = RELATIVE_TIME_0; $$.year = 1; }
515 | tORDINAL tMONTH_UNIT
516 { $$ = RELATIVE_TIME_0; $$.month = $1; }
517 | tUNUMBER tMONTH_UNIT
518 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
520 { $$ = RELATIVE_TIME_0; $$.month = 1; }
522 { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
524 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
526 { $$ = RELATIVE_TIME_0; $$.day = $1; }
527 | tORDINAL tHOUR_UNIT
528 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
529 | tUNUMBER tHOUR_UNIT
530 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
532 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
533 | tORDINAL tMINUTE_UNIT
534 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
535 | tUNUMBER tMINUTE_UNIT
536 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
538 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
540 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
542 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
543 | tSDECIMAL_NUMBER tSEC_UNIT
544 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
545 | tUDECIMAL_NUMBER tSEC_UNIT
546 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
548 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
554 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
555 | tSNUMBER tMONTH_UNIT
556 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
558 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
559 | tSNUMBER tHOUR_UNIT
560 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
561 | tSNUMBER tMINUTE_UNIT
562 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
564 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
569 { $$ = RELATIVE_TIME_0; $$.day = $1; }
572 seconds: signed_seconds | unsigned_seconds;
577 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
583 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
588 { digits_to_date_time (pc, $1); }
592 tUNUMBER relunit_snumber
594 /* Hybrid all-digit and relative offset, so that we accept e.g.,
595 "YYYYMMDD +N days" as well as "YYYYMMDD N days". */
596 digits_to_date_time (pc, $1);
597 apply_relative_time (pc, $2, 1);
617 static table const meridian_table[] =
619 { "AM", tMERIDIAN, MERam },
620 { "A.M.", tMERIDIAN, MERam },
621 { "PM", tMERIDIAN, MERpm },
622 { "P.M.", tMERIDIAN, MERpm },
626 static table const dst_table[] =
631 static table const month_and_day_table[] =
633 { "JANUARY", tMONTH, 1 },
634 { "FEBRUARY", tMONTH, 2 },
635 { "MARCH", tMONTH, 3 },
636 { "APRIL", tMONTH, 4 },
637 { "MAY", tMONTH, 5 },
638 { "JUNE", tMONTH, 6 },
639 { "JULY", tMONTH, 7 },
640 { "AUGUST", tMONTH, 8 },
641 { "SEPTEMBER",tMONTH, 9 },
642 { "SEPT", tMONTH, 9 },
643 { "OCTOBER", tMONTH, 10 },
644 { "NOVEMBER", tMONTH, 11 },
645 { "DECEMBER", tMONTH, 12 },
646 { "SUNDAY", tDAY, 0 },
647 { "MONDAY", tDAY, 1 },
648 { "TUESDAY", tDAY, 2 },
650 { "WEDNESDAY",tDAY, 3 },
651 { "WEDNES", tDAY, 3 },
652 { "THURSDAY", tDAY, 4 },
654 { "THURS", tDAY, 4 },
655 { "FRIDAY", tDAY, 5 },
656 { "SATURDAY", tDAY, 6 },
660 static table const time_units_table[] =
662 { "YEAR", tYEAR_UNIT, 1 },
663 { "MONTH", tMONTH_UNIT, 1 },
664 { "FORTNIGHT",tDAY_UNIT, 14 },
665 { "WEEK", tDAY_UNIT, 7 },
666 { "DAY", tDAY_UNIT, 1 },
667 { "HOUR", tHOUR_UNIT, 1 },
668 { "MINUTE", tMINUTE_UNIT, 1 },
669 { "MIN", tMINUTE_UNIT, 1 },
670 { "SECOND", tSEC_UNIT, 1 },
671 { "SEC", tSEC_UNIT, 1 },
675 /* Assorted relative-time words. */
676 static table const relative_time_table[] =
678 { "TOMORROW", tDAY_SHIFT, 1 },
679 { "YESTERDAY",tDAY_SHIFT, -1 },
680 { "TODAY", tDAY_SHIFT, 0 },
681 { "NOW", tDAY_SHIFT, 0 },
682 { "LAST", tORDINAL, -1 },
683 { "THIS", tORDINAL, 0 },
684 { "NEXT", tORDINAL, 1 },
685 { "FIRST", tORDINAL, 1 },
686 /*{ "SECOND", tORDINAL, 2 }, */
687 { "THIRD", tORDINAL, 3 },
688 { "FOURTH", tORDINAL, 4 },
689 { "FIFTH", tORDINAL, 5 },
690 { "SIXTH", tORDINAL, 6 },
691 { "SEVENTH", tORDINAL, 7 },
692 { "EIGHTH", tORDINAL, 8 },
693 { "NINTH", tORDINAL, 9 },
694 { "TENTH", tORDINAL, 10 },
695 { "ELEVENTH", tORDINAL, 11 },
696 { "TWELFTH", tORDINAL, 12 },
701 /* The universal time zone table. These labels can be used even for
702 time stamps that would not otherwise be valid, e.g., GMT time
703 stamps in London during summer. */
704 static table const universal_time_zone_table[] =
706 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
707 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
708 { "UTC", tZONE, HOUR ( 0) },
712 /* The time zone table. This table is necessarily incomplete, as time
713 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
714 as Eastern time in Australia, not as US Eastern Standard Time.
715 You cannot rely on getdate to handle arbitrary time zone
716 abbreviations; use numeric abbreviations like `-0500' instead. */
717 static table const time_zone_table[] =
719 { "WET", tZONE, HOUR ( 0) }, /* Western European */
720 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
721 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
722 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
723 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
724 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
725 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
726 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
727 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
728 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
729 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
730 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
731 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
732 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
733 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
734 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
735 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
736 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
737 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
738 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
739 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
740 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
741 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
742 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
743 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
744 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
745 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
746 { "CET", tZONE, HOUR ( 1) }, /* Central European */
747 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
748 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
749 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
750 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
751 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
752 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
753 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
754 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
755 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
756 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
757 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
758 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
759 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
760 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
761 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
762 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
763 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
764 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
765 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
769 /* Military time zone table. */
770 static table const military_table[] =
772 { "A", tZONE, -HOUR ( 1) },
773 { "B", tZONE, -HOUR ( 2) },
774 { "C", tZONE, -HOUR ( 3) },
775 { "D", tZONE, -HOUR ( 4) },
776 { "E", tZONE, -HOUR ( 5) },
777 { "F", tZONE, -HOUR ( 6) },
778 { "G", tZONE, -HOUR ( 7) },
779 { "H", tZONE, -HOUR ( 8) },
780 { "I", tZONE, -HOUR ( 9) },
781 { "K", tZONE, -HOUR (10) },
782 { "L", tZONE, -HOUR (11) },
783 { "M", tZONE, -HOUR (12) },
784 { "N", tZONE, HOUR ( 1) },
785 { "O", tZONE, HOUR ( 2) },
786 { "P", tZONE, HOUR ( 3) },
787 { "Q", tZONE, HOUR ( 4) },
788 { "R", tZONE, HOUR ( 5) },
789 { "S", tZONE, HOUR ( 6) },
790 { "T", tZONE, HOUR ( 7) },
791 { "U", tZONE, HOUR ( 8) },
792 { "V", tZONE, HOUR ( 9) },
793 { "W", tZONE, HOUR (10) },
794 { "X", tZONE, HOUR (11) },
795 { "Y", tZONE, HOUR (12) },
796 { "Z", tZONE, HOUR ( 0) },
802 /* Convert a time zone expressed as HH:MM into an integer count of
803 minutes. If MM is negative, then S is of the form HHMM and needs
804 to be picked apart; otherwise, S is of the form HH. As specified in
805 http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
806 only valid TZ range, and consider first two digits as hours, if no
807 minutes specified. */
810 time_zone_hhmm (parser_control *pc, textint s, long int mm)
814 /* If the length of S is 1 or 2 and no minutes are specified,
815 interpret it as a number of hours. */
816 if (s.digits <= 2 && mm < 0)
820 n_minutes = (s.value / 100) * 60 + s.value % 100;
822 n_minutes = s.value * 60 + (s.negative ? -mm : mm);
824 /* If the absolute number of minutes is larger than 24 hours,
825 arrange to reject it by incrementing pc->zones_seen. Thus,
826 we allow only values in the range UTC-24:00 to UTC+24:00. */
827 if (24 * 60 < abs (n_minutes))
834 to_hour (long int hours, int meridian)
838 default: /* Pacify GCC. */
840 return 0 <= hours && hours < 24 ? hours : -1;
842 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
844 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
849 to_year (textint textyear)
851 long int year = textyear.value;
856 /* XPG4 suggests that years 00-68 map to 2000-2068, and
857 years 69-99 map to 1969-1999. */
858 else if (textyear.digits == 2)
859 year += year < 69 ? 2000 : 1900;
865 lookup_zone (parser_control const *pc, char const *name)
869 for (tp = universal_time_zone_table; tp->name; tp++)
870 if (strcmp (name, tp->name) == 0)
873 /* Try local zone abbreviations before those in time_zone_table, as
874 the local ones are more likely to be right. */
875 for (tp = pc->local_time_zone_table; tp->name; tp++)
876 if (strcmp (name, tp->name) == 0)
879 for (tp = time_zone_table; tp->name; tp++)
880 if (strcmp (name, tp->name) == 0)
887 /* Yield the difference between *A and *B,
888 measured in seconds, ignoring leap seconds.
889 The body of this function is taken directly from the GNU C Library;
890 see src/strftime.c. */
892 tm_diff (struct tm const *a, struct tm const *b)
894 /* Compute intervening leap days correctly even if year is negative.
895 Take care to avoid int overflow in leap day calculations. */
896 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
897 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
898 int a100 = a4 / 25 - (a4 % 25 < 0);
899 int b100 = b4 / 25 - (b4 % 25 < 0);
900 int a400 = SHR (a100, 2);
901 int b400 = SHR (b100, 2);
902 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
903 long int ayear = a->tm_year;
904 long int years = ayear - b->tm_year;
905 long int days = (365 * years + intervening_leap_days
906 + (a->tm_yday - b->tm_yday));
907 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
908 + (a->tm_min - b->tm_min))
909 + (a->tm_sec - b->tm_sec));
911 #endif /* ! HAVE_TM_GMTOFF */
914 lookup_word (parser_control const *pc, char *word)
923 /* Make it uppercase. */
924 for (p = word; *p; p++)
926 unsigned char ch = *p;
930 for (tp = meridian_table; tp->name; tp++)
931 if (strcmp (word, tp->name) == 0)
934 /* See if we have an abbreviation for a month. */
935 wordlen = strlen (word);
936 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
938 for (tp = month_and_day_table; tp->name; tp++)
939 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
942 if ((tp = lookup_zone (pc, word)))
945 if (strcmp (word, dst_table[0].name) == 0)
948 for (tp = time_units_table; tp->name; tp++)
949 if (strcmp (word, tp->name) == 0)
952 /* Strip off any plural and try the units table again. */
953 if (word[wordlen - 1] == 'S')
955 word[wordlen - 1] = '\0';
956 for (tp = time_units_table; tp->name; tp++)
957 if (strcmp (word, tp->name) == 0)
959 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
962 for (tp = relative_time_table; tp->name; tp++)
963 if (strcmp (word, tp->name) == 0)
966 /* Military time zones. */
968 for (tp = military_table; tp->name; tp++)
969 if (word[0] == tp->name[0])
972 /* Drop out any periods and try the time zone table again. */
973 for (period_found = false, p = q = word; (*p = *q); q++)
978 if (period_found && (tp = lookup_zone (pc, word)))
985 yylex (YYSTYPE *lvalp, parser_control *pc)
992 while (c = *pc->input, c_isspace (c))
995 if (ISDIGIT (c) || c == '-' || c == '+')
999 unsigned long int value;
1000 if (c == '-' || c == '+')
1002 sign = c == '-' ? -1 : 1;
1003 while (c = *++pc->input, c_isspace (c))
1006 /* skip the '-' sign */
1012 for (value = 0; ; value *= 10)
1014 unsigned long int value1 = value + (c - '0');
1021 if (ULONG_MAX / 10 < value)
1024 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1029 unsigned long int value1;
1031 /* Check for overflow when converting value to time_t. */
1046 if (value != value1)
1049 /* Accumulate fraction, to ns precision. */
1052 for (digits = 2; digits <= LOG10_BILLION; digits++)
1059 /* Skip excess digits, truncating toward -Infinity. */
1061 for (; ISDIGIT (*p); p++)
1067 while (ISDIGIT (*p))
1070 /* Adjust to the timespec convention, which is that
1071 tv_nsec is always a positive offset even if tv_sec is
1081 lvalp->timespec.tv_sec = s;
1082 lvalp->timespec.tv_nsec = ns;
1084 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1088 lvalp->textintval.negative = sign < 0;
1091 lvalp->textintval.value = - value;
1092 if (0 < lvalp->textintval.value)
1097 lvalp->textintval.value = value;
1098 if (lvalp->textintval.value < 0)
1101 lvalp->textintval.digits = p - pc->input;
1103 return sign ? tSNUMBER : tUNUMBER;
1115 if (p < buff + sizeof buff - 1)
1119 while (c_isalpha (c) || c == '.');
1122 tp = lookup_word (pc, buff);
1125 lvalp->intval = tp->value;
1130 return *pc->input++;
1146 /* Do nothing if the parser reports an error. */
1148 yyerror (parser_control const *pc _GL_UNUSED,
1149 char const *s _GL_UNUSED)
1154 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1155 passing it to mktime, return true if it's OK that mktime returned T.
1156 It's not OK if *TM0 has out-of-range members. */
1159 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1161 if (t == (time_t) -1)
1163 /* Guard against falsely reporting an error when parsing a time
1164 stamp that happens to equal (time_t) -1, on a host that
1165 supports such a time stamp. */
1166 tm1 = localtime (&t);
1171 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1172 | (tm0->tm_min ^ tm1->tm_min)
1173 | (tm0->tm_hour ^ tm1->tm_hour)
1174 | (tm0->tm_mday ^ tm1->tm_mday)
1175 | (tm0->tm_mon ^ tm1->tm_mon)
1176 | (tm0->tm_year ^ tm1->tm_year));
1179 /* A reasonable upper bound for the size of ordinary TZ strings.
1180 Use heap allocation if TZ's length exceeds this. */
1181 enum { TZBUFSIZE = 100 };
1183 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1186 get_tz (char tzbuf[TZBUFSIZE])
1188 char *tz = getenv ("TZ");
1191 size_t tzsize = strlen (tz) + 1;
1192 tz = (tzsize <= TZBUFSIZE
1193 ? memcpy (tzbuf, tz, tzsize)
1194 : xmemdup (tz, tzsize));
1199 /* Parse a date/time string, storing the resulting time value into *RESULT.
1200 The string itself is pointed to by P. Return true if successful.
1201 P can be an incomplete or relative time specification; if so, use
1202 *NOW as the basis for the returned time. */
1204 get_date (struct timespec *result, char const *p, struct timespec const *now)
1208 struct tm const *tmp;
1212 struct timespec gettime_buffer;
1214 bool tz_was_altered = false;
1216 char tz0buf[TZBUFSIZE];
1221 gettime (&gettime_buffer);
1222 now = &gettime_buffer;
1225 Start = now->tv_sec;
1226 Start_ns = now->tv_nsec;
1228 tmp = localtime (&now->tv_sec);
1232 while (c = *p, c_isspace (c))
1235 if (strncmp (p, "TZ=\"", 4) == 0)
1237 char const *tzbase = p + 4;
1241 for (s = tzbase; *s; s++, tzsize++)
1245 if (! (*s == '\\' || *s == '"'))
1252 char tz1buf[TZBUFSIZE];
1253 bool large_tz = TZBUFSIZE < tzsize;
1255 /* Free tz0, in case this is the 2nd or subsequent time through. */
1257 tz0 = get_tz (tz0buf);
1258 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1259 for (s = tzbase; *s != '"'; s++)
1260 *z++ = *(s += *s == '\\');
1262 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1267 tz_was_altered = true;
1272 /* As documented, be careful to treat the empty string just like
1273 a date string of "0". Without this, an empty string would be
1274 declared invalid when parsed during a DST transition. */
1279 pc.year.value = tmp->tm_year;
1280 pc.year.value += TM_YEAR_BASE;
1282 pc.month = tmp->tm_mon + 1;
1283 pc.day = tmp->tm_mday;
1284 pc.hour = tmp->tm_hour;
1285 pc.minutes = tmp->tm_min;
1286 pc.seconds.tv_sec = tmp->tm_sec;
1287 pc.seconds.tv_nsec = Start_ns;
1288 tm.tm_isdst = tmp->tm_isdst;
1290 pc.meridian = MER24;
1291 pc.rel = RELATIVE_TIME_0;
1292 pc.timespec_seen = false;
1293 pc.rels_seen = false;
1297 pc.local_zones_seen = 0;
1301 #if HAVE_STRUCT_TM_TM_ZONE
1302 pc.local_time_zone_table[0].name = tmp->tm_zone;
1303 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1304 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1305 pc.local_time_zone_table[1].name = NULL;
1307 /* Probe the names used in the next three calendar quarters, looking
1308 for a tm_isdst different from the one we already have. */
1311 for (quarter = 1; quarter <= 3; quarter++)
1313 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1314 struct tm const *probe_tm = localtime (&probe);
1315 if (probe_tm && probe_tm->tm_zone
1316 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1319 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1320 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1321 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1322 pc.local_time_zone_table[2].name = NULL;
1331 # if !HAVE_DECL_TZNAME
1332 extern char *tzname[];
1335 for (i = 0; i < 2; i++)
1337 pc.local_time_zone_table[i].name = tzname[i];
1338 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1339 pc.local_time_zone_table[i].value = i;
1341 pc.local_time_zone_table[i].name = NULL;
1344 pc.local_time_zone_table[0].name = NULL;
1348 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1349 && ! strcmp (pc.local_time_zone_table[0].name,
1350 pc.local_time_zone_table[1].name))
1352 /* This locale uses the same abbrevation for standard and
1353 daylight times. So if we see that abbreviation, we don't
1354 know whether it's daylight time. */
1355 pc.local_time_zone_table[0].value = -1;
1356 pc.local_time_zone_table[1].name = NULL;
1359 if (yyparse (&pc) != 0)
1362 if (pc.timespec_seen)
1363 *result = pc.seconds;
1366 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1367 | (pc.local_zones_seen + pc.zones_seen)))
1370 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1371 tm.tm_mon = pc.month - 1;
1372 tm.tm_mday = pc.day;
1373 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1375 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1378 tm.tm_min = pc.minutes;
1379 tm.tm_sec = pc.seconds.tv_sec;
1383 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1384 pc.seconds.tv_nsec = 0;
1387 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1388 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1391 /* But if the input explicitly specifies local time with or without
1392 DST, give mktime that information. */
1393 if (pc.local_zones_seen)
1394 tm.tm_isdst = pc.local_isdst;
1398 Start = mktime (&tm);
1400 if (! mktime_ok (&tm0, &tm, Start))
1402 if (! pc.zones_seen)
1406 /* Guard against falsely reporting errors near the time_t
1407 boundaries when parsing times in other time zones. For
1408 example, suppose the input string "1969-12-31 23:00:00 -0100",
1409 the current time zone is 8 hours ahead of UTC, and the min
1410 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1411 localtime value is 1970-01-01 08:00:00, and mktime will
1412 therefore fail on 1969-12-31 23:00:00. To work around the
1413 problem, set the time zone to 1 hour behind UTC temporarily
1414 by setting TZ="XXX1:00" and try mktime again. */
1416 long int time_zone = pc.time_zone;
1417 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1418 long int abs_time_zone_hour = abs_time_zone / 60;
1419 int abs_time_zone_min = abs_time_zone % 60;
1420 char tz1buf[sizeof "XXX+0:00"
1421 + sizeof pc.time_zone * CHAR_BIT / 3];
1422 if (!tz_was_altered)
1423 tz0 = get_tz (tz0buf);
1424 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1425 abs_time_zone_hour, abs_time_zone_min);
1426 if (setenv ("TZ", tz1buf, 1) != 0)
1428 tz_was_altered = true;
1430 Start = mktime (&tm);
1431 if (! mktime_ok (&tm0, &tm, Start))
1436 if (pc.days_seen && ! pc.dates_seen)
1438 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1439 + 7 * (pc.day_ordinal
1440 - (0 < pc.day_ordinal
1441 && tm.tm_wday != pc.day_number)));
1443 Start = mktime (&tm);
1444 if (Start == (time_t) -1)
1448 /* Add relative date. */
1449 if (pc.rel.year | pc.rel.month | pc.rel.day)
1451 int year = tm.tm_year + pc.rel.year;
1452 int month = tm.tm_mon + pc.rel.month;
1453 int day = tm.tm_mday + pc.rel.day;
1454 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1455 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1456 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1461 tm.tm_hour = tm0.tm_hour;
1462 tm.tm_min = tm0.tm_min;
1463 tm.tm_sec = tm0.tm_sec;
1464 tm.tm_isdst = tm0.tm_isdst;
1465 Start = mktime (&tm);
1466 if (Start == (time_t) -1)
1470 /* The only "output" of this if-block is an updated Start value,
1471 so this block must follow others that clobber Start. */
1474 long int delta = pc.time_zone * 60;
1476 #ifdef HAVE_TM_GMTOFF
1477 delta -= tm.tm_gmtoff;
1480 struct tm const *gmt = gmtime (&t);
1483 delta -= tm_diff (&tm, gmt);
1486 if ((Start < t1) != (delta < 0))
1487 goto fail; /* time_t overflow */
1491 /* Add relative hours, minutes, and seconds. On hosts that support
1492 leap seconds, ignore the possibility of leap seconds; e.g.,
1493 "+ 10 minutes" adds 600 seconds, even if one of them is a
1494 leap second. Typically this is not what the user wants, but it's
1495 too hard to do it the other way, because the time zone indicator
1496 must be applied before relative times, and if mktime is applied
1497 again the time zone will be lost. */
1499 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1500 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1502 long int d1 = 60 * 60 * pc.rel.hour;
1503 time_t t1 = t0 + d1;
1504 long int d2 = 60 * pc.rel.minutes;
1505 time_t t2 = t1 + d2;
1506 long_time_t d3 = pc.rel.seconds;
1507 long_time_t t3 = t2 + d3;
1508 long int d4 = (sum_ns - normalized_ns) / BILLION;
1509 long_time_t t4 = t3 + d4;
1512 if ((d1 / (60 * 60) ^ pc.rel.hour)
1513 | (d2 / 60 ^ pc.rel.minutes)
1514 | ((t1 < t0) ^ (d1 < 0))
1515 | ((t2 < t1) ^ (d2 < 0))
1516 | ((t3 < t2) ^ (d3 < 0))
1517 | ((t4 < t3) ^ (d4 < 0))
1521 result->tv_sec = t5;
1522 result->tv_nsec = normalized_ns;
1532 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1541 main (int ac, char **av)
1545 printf ("Enter date, or blank line to exit.\n\t> ");
1548 buff[BUFSIZ - 1] = '\0';
1549 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1552 struct tm const *tm;
1553 if (! get_date (&d, buff, NULL))
1554 printf ("Bad format - couldn't convert.\n");
1555 else if (! (tm = localtime (&d.tv_sec)))
1557 long int sec = d.tv_sec;
1558 printf ("localtime (%ld) failed\n", sec);
1563 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1564 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1565 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);