2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008
5 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)
82 # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
83 # define __attribute__(x)
87 #ifndef ATTRIBUTE_UNUSED
88 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
91 /* Shift A right by B bits portably, by dividing A by 2**B and
92 truncating towards minus infinity. A and B should be free of side
93 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
94 INT_BITS is the number of useful bits in an int. GNU code can
95 assume that INT_BITS is at least 32.
97 ISO C99 says that A >> B is implementation-defined if A < 0. Some
98 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
99 right in the usual way when A < 0, so SHR falls back on division if
100 ordinary A >> B doesn't seem to be the usual signed shift. */
104 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
106 #define EPOCH_YEAR 1970
107 #define TM_YEAR_BASE 1900
109 #define HOUR(x) ((x) * 60)
111 /* Lots of this code assumes time_t and time_t-like values fit into
112 long int. It also assumes that signed integer overflow silently
113 wraps around, but there's no portable way to check for that at
115 verify (TYPE_IS_INTEGER (time_t));
116 verify (LONG_MIN <= TYPE_MINIMUM (time_t) && TYPE_MAXIMUM (time_t) <= LONG_MAX);
118 /* An integer value, and the number of digits in its textual
127 /* An entry in the lexical lookup table. */
135 /* Meridian: am, pm, or 24-hour style. */
136 enum { MERam, MERpm, MER24 };
138 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
140 /* Relative times. */
143 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
153 #if HAVE_COMPOUND_LITERALS
154 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
156 static relative_time const RELATIVE_TIME_0;
159 /* Information passed to and from the parser. */
162 /* The input string remaining to be parsed. */
165 /* N, if this is the Nth Tuesday. */
166 long int day_ordinal;
168 /* Day of week; Sunday is 0. */
171 /* tm_isdst flag for the local zone. */
174 /* Time zone, in minutes east of UTC. */
177 /* Style used for time. */
180 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
186 struct timespec seconds; /* includes nanoseconds */
188 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
191 /* Presence or counts of nonterminals of various flavors parsed so far. */
196 size_t local_zones_seen;
201 /* Table of local time zone abbrevations, terminated by a null entry. */
202 table local_time_zone_table[3];
206 static int yylex (union YYSTYPE *, parser_control *);
207 static int yyerror (parser_control const *, char const *);
208 static long int time_zone_hhmm (parser_control *, textint, long int);
210 /* Extract into *PC any date and time info from a string of digits
211 of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
214 digits_to_date_time (parser_control *pc, textint text_int)
216 if (pc->dates_seen && ! pc->year.digits
217 && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
221 if (4 < text_int.digits)
224 pc->day = text_int.value % 100;
225 pc->month = (text_int.value / 100) % 100;
226 pc->year.value = text_int.value / 10000;
227 pc->year.digits = text_int.digits - 4;
232 if (text_int.digits <= 2)
234 pc->hour = text_int.value;
239 pc->hour = text_int.value / 100;
240 pc->minutes = text_int.value % 100;
242 pc->seconds.tv_sec = 0;
243 pc->seconds.tv_nsec = 0;
244 pc->meridian = MER24;
249 /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). */
251 apply_relative_time (parser_control *pc, relative_time rel, int factor)
253 pc->rel.ns += factor * rel.ns;
254 pc->rel.seconds += factor * rel.seconds;
255 pc->rel.minutes += factor * rel.minutes;
256 pc->rel.hour += factor * rel.hour;
257 pc->rel.day += factor * rel.day;
258 pc->rel.month += factor * rel.month;
259 pc->rel.year += factor * rel.year;
260 pc->rels_seen = true;
263 /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */
265 set_hhmmss (parser_control *pc, long int hour, long int minutes,
266 time_t sec, long int nsec)
269 pc->minutes = minutes;
270 pc->seconds.tv_sec = sec;
271 pc->seconds.tv_nsec = nsec;
276 /* We want a reentrant parser, even if the TZ manipulation and the calls to
277 localtime and gmtime are not reentrant. */
279 %parse-param { parser_control *pc }
280 %lex-param { parser_control *pc }
282 /* This grammar has 20 shift/reduce conflicts. */
289 struct timespec timespec;
295 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
296 %token <intval> tDAY_UNIT tDAY_SHIFT
298 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
299 %token <intval> tMONTH tORDINAL tZONE
301 %token <textintval> tSNUMBER tUNUMBER
302 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
304 %type <intval> o_colon_minutes o_merid
305 %type <timespec> seconds signed_seconds unsigned_seconds
307 %type <rel> relunit relunit_snumber dayshift
320 pc->timespec_seen = true;
331 { pc->times_seen++; }
333 { pc->local_zones_seen++; }
335 { pc->zones_seen++; }
337 { pc->dates_seen++; }
348 set_hhmmss (pc, $1.value, 0, 0, 0);
351 | tUNUMBER ':' tUNUMBER o_merid
353 set_hhmmss (pc, $1.value, $3.value, 0, 0);
356 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
358 set_hhmmss (pc, $1.value, $3.value, 0, 0);
359 pc->meridian = MER24;
361 pc->time_zone = time_zone_hhmm (pc, $4, $5);
363 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
365 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
368 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
370 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
371 pc->meridian = MER24;
373 pc->time_zone = time_zone_hhmm (pc, $6, $7);
380 pc->local_isdst = $1;
381 pc->dsts_seen += (0 < $1);
386 pc->dsts_seen += (0 < $1) + 1;
392 { pc->time_zone = $1; }
393 | tZONE relunit_snumber
394 { pc->time_zone = $1;
395 apply_relative_time (pc, $2, 1); }
396 | tZONE tSNUMBER o_colon_minutes
397 { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
399 { pc->time_zone = $1 + 60; }
401 { pc->time_zone = $1 + 60; }
417 pc->day_ordinal = $1;
422 pc->day_ordinal = $1.value;
428 tUNUMBER '/' tUNUMBER
430 pc->month = $1.value;
433 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
435 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
436 otherwise as MM/DD/YY.
437 The goal in recognizing YYYY/MM/DD is solely to support legacy
438 machine-generated dates like those in an RCS log listing. If
439 you want portability, use the ISO 8601 format. */
443 pc->month = $3.value;
448 pc->month = $1.value;
453 | tUNUMBER tSNUMBER tSNUMBER
455 /* ISO 8601 format. YYYY-MM-DD. */
457 pc->month = -$2.value;
460 | tUNUMBER tMONTH tSNUMBER
462 /* e.g. 17-JUN-1992. */
465 pc->year.value = -$3.value;
466 pc->year.digits = $3.digits;
468 | tMONTH tSNUMBER tSNUMBER
470 /* e.g. JUN-17-1992. */
473 pc->year.value = -$3.value;
474 pc->year.digits = $3.digits;
481 | tMONTH tUNUMBER ',' tUNUMBER
492 | tUNUMBER tMONTH tUNUMBER
502 { apply_relative_time (pc, $1, -1); }
504 { apply_relative_time (pc, $1, 1); }
506 { apply_relative_time (pc, $1, 1); }
511 { $$ = RELATIVE_TIME_0; $$.year = $1; }
512 | tUNUMBER tYEAR_UNIT
513 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
515 { $$ = RELATIVE_TIME_0; $$.year = 1; }
516 | tORDINAL tMONTH_UNIT
517 { $$ = RELATIVE_TIME_0; $$.month = $1; }
518 | tUNUMBER tMONTH_UNIT
519 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
521 { $$ = RELATIVE_TIME_0; $$.month = 1; }
523 { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
525 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
527 { $$ = RELATIVE_TIME_0; $$.day = $1; }
528 | tORDINAL tHOUR_UNIT
529 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
530 | tUNUMBER tHOUR_UNIT
531 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
533 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
534 | tORDINAL tMINUTE_UNIT
535 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
536 | tUNUMBER tMINUTE_UNIT
537 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
539 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
541 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
543 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
544 | tSDECIMAL_NUMBER tSEC_UNIT
545 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
546 | tUDECIMAL_NUMBER tSEC_UNIT
547 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
549 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
555 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
556 | tSNUMBER tMONTH_UNIT
557 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
559 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
560 | tSNUMBER tHOUR_UNIT
561 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
562 | tSNUMBER tMINUTE_UNIT
563 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
565 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
570 { $$ = RELATIVE_TIME_0; $$.day = $1; }
573 seconds: signed_seconds | unsigned_seconds;
578 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
584 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
589 { digits_to_date_time (pc, $1); }
593 tUNUMBER relunit_snumber
595 /* Hybrid all-digit and relative offset, so that we accept e.g.,
596 "YYYYMMDD +N days" as well as "YYYYMMDD N days". */
597 digits_to_date_time (pc, $1);
598 apply_relative_time (pc, $2, 1);
618 static table const meridian_table[] =
620 { "AM", tMERIDIAN, MERam },
621 { "A.M.", tMERIDIAN, MERam },
622 { "PM", tMERIDIAN, MERpm },
623 { "P.M.", tMERIDIAN, MERpm },
627 static table const dst_table[] =
632 static table const month_and_day_table[] =
634 { "JANUARY", tMONTH, 1 },
635 { "FEBRUARY", tMONTH, 2 },
636 { "MARCH", tMONTH, 3 },
637 { "APRIL", tMONTH, 4 },
638 { "MAY", tMONTH, 5 },
639 { "JUNE", tMONTH, 6 },
640 { "JULY", tMONTH, 7 },
641 { "AUGUST", tMONTH, 8 },
642 { "SEPTEMBER",tMONTH, 9 },
643 { "SEPT", tMONTH, 9 },
644 { "OCTOBER", tMONTH, 10 },
645 { "NOVEMBER", tMONTH, 11 },
646 { "DECEMBER", tMONTH, 12 },
647 { "SUNDAY", tDAY, 0 },
648 { "MONDAY", tDAY, 1 },
649 { "TUESDAY", tDAY, 2 },
651 { "WEDNESDAY",tDAY, 3 },
652 { "WEDNES", tDAY, 3 },
653 { "THURSDAY", tDAY, 4 },
655 { "THURS", tDAY, 4 },
656 { "FRIDAY", tDAY, 5 },
657 { "SATURDAY", tDAY, 6 },
661 static table const time_units_table[] =
663 { "YEAR", tYEAR_UNIT, 1 },
664 { "MONTH", tMONTH_UNIT, 1 },
665 { "FORTNIGHT",tDAY_UNIT, 14 },
666 { "WEEK", tDAY_UNIT, 7 },
667 { "DAY", tDAY_UNIT, 1 },
668 { "HOUR", tHOUR_UNIT, 1 },
669 { "MINUTE", tMINUTE_UNIT, 1 },
670 { "MIN", tMINUTE_UNIT, 1 },
671 { "SECOND", tSEC_UNIT, 1 },
672 { "SEC", tSEC_UNIT, 1 },
676 /* Assorted relative-time words. */
677 static table const relative_time_table[] =
679 { "TOMORROW", tDAY_SHIFT, 1 },
680 { "YESTERDAY",tDAY_SHIFT, -1 },
681 { "TODAY", tDAY_SHIFT, 0 },
682 { "NOW", tDAY_SHIFT, 0 },
683 { "LAST", tORDINAL, -1 },
684 { "THIS", tORDINAL, 0 },
685 { "NEXT", tORDINAL, 1 },
686 { "FIRST", tORDINAL, 1 },
687 /*{ "SECOND", tORDINAL, 2 }, */
688 { "THIRD", tORDINAL, 3 },
689 { "FOURTH", tORDINAL, 4 },
690 { "FIFTH", tORDINAL, 5 },
691 { "SIXTH", tORDINAL, 6 },
692 { "SEVENTH", tORDINAL, 7 },
693 { "EIGHTH", tORDINAL, 8 },
694 { "NINTH", tORDINAL, 9 },
695 { "TENTH", tORDINAL, 10 },
696 { "ELEVENTH", tORDINAL, 11 },
697 { "TWELFTH", tORDINAL, 12 },
702 /* The universal time zone table. These labels can be used even for
703 time stamps that would not otherwise be valid, e.g., GMT time
704 stamps in London during summer. */
705 static table const universal_time_zone_table[] =
707 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
708 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
709 { "UTC", tZONE, HOUR ( 0) },
713 /* The time zone table. This table is necessarily incomplete, as time
714 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
715 as Eastern time in Australia, not as US Eastern Standard Time.
716 You cannot rely on getdate to handle arbitrary time zone
717 abbreviations; use numeric abbreviations like `-0500' instead. */
718 static table const time_zone_table[] =
720 { "WET", tZONE, HOUR ( 0) }, /* Western European */
721 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
722 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
723 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
724 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
725 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
726 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
727 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
728 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
729 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
730 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
731 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
732 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
733 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
734 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
735 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
736 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
737 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
738 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
739 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
740 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
741 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
742 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
743 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
744 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
745 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
746 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
747 { "CET", tZONE, HOUR ( 1) }, /* Central European */
748 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
749 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
750 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
751 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
752 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
753 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
754 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
755 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
756 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
757 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
758 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
759 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
760 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
761 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
762 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
763 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
764 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
765 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
766 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
770 /* Military time zone table. */
771 static table const military_table[] =
773 { "A", tZONE, -HOUR ( 1) },
774 { "B", tZONE, -HOUR ( 2) },
775 { "C", tZONE, -HOUR ( 3) },
776 { "D", tZONE, -HOUR ( 4) },
777 { "E", tZONE, -HOUR ( 5) },
778 { "F", tZONE, -HOUR ( 6) },
779 { "G", tZONE, -HOUR ( 7) },
780 { "H", tZONE, -HOUR ( 8) },
781 { "I", tZONE, -HOUR ( 9) },
782 { "K", tZONE, -HOUR (10) },
783 { "L", tZONE, -HOUR (11) },
784 { "M", tZONE, -HOUR (12) },
785 { "N", tZONE, HOUR ( 1) },
786 { "O", tZONE, HOUR ( 2) },
787 { "P", tZONE, HOUR ( 3) },
788 { "Q", tZONE, HOUR ( 4) },
789 { "R", tZONE, HOUR ( 5) },
790 { "S", tZONE, HOUR ( 6) },
791 { "T", tZONE, HOUR ( 7) },
792 { "U", tZONE, HOUR ( 8) },
793 { "V", tZONE, HOUR ( 9) },
794 { "W", tZONE, HOUR (10) },
795 { "X", tZONE, HOUR (11) },
796 { "Y", tZONE, HOUR (12) },
797 { "Z", tZONE, HOUR ( 0) },
803 /* Convert a time zone expressed as HH:MM into an integer count of
804 minutes. If MM is negative, then S is of the form HHMM and needs
805 to be picked apart; otherwise, S is of the form HH. As specified in
806 http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
807 only valid TZ range, and consider first two digits as hours, if no
808 minutes specified. */
811 time_zone_hhmm (parser_control *pc, textint s, long int mm)
815 /* If the length of S is 1 or 2 and no minutes are specified,
816 interpret it as a number of hours. */
817 if (s.digits <= 2 && mm < 0)
821 n_minutes = (s.value / 100) * 60 + s.value % 100;
823 n_minutes = s.value * 60 + (s.negative ? -mm : mm);
825 /* If the absolute number of minutes is larger than 24 hours,
826 arrange to reject it by incrementing pc->zones_seen. Thus,
827 we allow only values in the range UTC-24:00 to UTC+24:00. */
828 if (24 * 60 < abs (n_minutes))
835 to_hour (long int hours, int meridian)
839 default: /* Pacify GCC. */
841 return 0 <= hours && hours < 24 ? hours : -1;
843 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
845 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
850 to_year (textint textyear)
852 long int year = textyear.value;
857 /* XPG4 suggests that years 00-68 map to 2000-2068, and
858 years 69-99 map to 1969-1999. */
859 else if (textyear.digits == 2)
860 year += year < 69 ? 2000 : 1900;
866 lookup_zone (parser_control const *pc, char const *name)
870 for (tp = universal_time_zone_table; tp->name; tp++)
871 if (strcmp (name, tp->name) == 0)
874 /* Try local zone abbreviations before those in time_zone_table, as
875 the local ones are more likely to be right. */
876 for (tp = pc->local_time_zone_table; tp->name; tp++)
877 if (strcmp (name, tp->name) == 0)
880 for (tp = time_zone_table; tp->name; tp++)
881 if (strcmp (name, tp->name) == 0)
888 /* Yield the difference between *A and *B,
889 measured in seconds, ignoring leap seconds.
890 The body of this function is taken directly from the GNU C Library;
891 see src/strftime.c. */
893 tm_diff (struct tm const *a, struct tm const *b)
895 /* Compute intervening leap days correctly even if year is negative.
896 Take care to avoid int overflow in leap day calculations. */
897 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
898 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
899 int a100 = a4 / 25 - (a4 % 25 < 0);
900 int b100 = b4 / 25 - (b4 % 25 < 0);
901 int a400 = SHR (a100, 2);
902 int b400 = SHR (b100, 2);
903 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
904 long int ayear = a->tm_year;
905 long int years = ayear - b->tm_year;
906 long int days = (365 * years + intervening_leap_days
907 + (a->tm_yday - b->tm_yday));
908 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
909 + (a->tm_min - b->tm_min))
910 + (a->tm_sec - b->tm_sec));
912 #endif /* ! HAVE_TM_GMTOFF */
915 lookup_word (parser_control const *pc, char *word)
924 /* Make it uppercase. */
925 for (p = word; *p; p++)
927 unsigned char ch = *p;
931 for (tp = meridian_table; tp->name; tp++)
932 if (strcmp (word, tp->name) == 0)
935 /* See if we have an abbreviation for a month. */
936 wordlen = strlen (word);
937 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
939 for (tp = month_and_day_table; tp->name; tp++)
940 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
943 if ((tp = lookup_zone (pc, word)))
946 if (strcmp (word, dst_table[0].name) == 0)
949 for (tp = time_units_table; tp->name; tp++)
950 if (strcmp (word, tp->name) == 0)
953 /* Strip off any plural and try the units table again. */
954 if (word[wordlen - 1] == 'S')
956 word[wordlen - 1] = '\0';
957 for (tp = time_units_table; tp->name; tp++)
958 if (strcmp (word, tp->name) == 0)
960 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
963 for (tp = relative_time_table; tp->name; tp++)
964 if (strcmp (word, tp->name) == 0)
967 /* Military time zones. */
969 for (tp = military_table; tp->name; tp++)
970 if (word[0] == tp->name[0])
973 /* Drop out any periods and try the time zone table again. */
974 for (period_found = false, p = q = word; (*p = *q); q++)
979 if (period_found && (tp = lookup_zone (pc, word)))
986 yylex (YYSTYPE *lvalp, parser_control *pc)
993 while (c = *pc->input, c_isspace (c))
996 if (ISDIGIT (c) || c == '-' || c == '+')
1000 unsigned long int value;
1001 if (c == '-' || c == '+')
1003 sign = c == '-' ? -1 : 1;
1004 while (c = *++pc->input, c_isspace (c))
1007 /* skip the '-' sign */
1013 for (value = 0; ; value *= 10)
1015 unsigned long int value1 = value + (c - '0');
1022 if (ULONG_MAX / 10 < value)
1025 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1030 unsigned long int value1;
1032 /* Check for overflow when converting value to time_t. */
1047 if (value != value1)
1050 /* Accumulate fraction, to ns precision. */
1053 for (digits = 2; digits <= LOG10_BILLION; digits++)
1060 /* Skip excess digits, truncating toward -Infinity. */
1062 for (; ISDIGIT (*p); p++)
1068 while (ISDIGIT (*p))
1071 /* Adjust to the timespec convention, which is that
1072 tv_nsec is always a positive offset even if tv_sec is
1082 lvalp->timespec.tv_sec = s;
1083 lvalp->timespec.tv_nsec = ns;
1085 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1089 lvalp->textintval.negative = sign < 0;
1092 lvalp->textintval.value = - value;
1093 if (0 < lvalp->textintval.value)
1098 lvalp->textintval.value = value;
1099 if (lvalp->textintval.value < 0)
1102 lvalp->textintval.digits = p - pc->input;
1104 return sign ? tSNUMBER : tUNUMBER;
1116 if (p < buff + sizeof buff - 1)
1120 while (c_isalpha (c) || c == '.');
1123 tp = lookup_word (pc, buff);
1126 lvalp->intval = tp->value;
1131 return *pc->input++;
1147 /* Do nothing if the parser reports an error. */
1149 yyerror (parser_control const *pc ATTRIBUTE_UNUSED,
1150 char const *s ATTRIBUTE_UNUSED)
1155 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1156 passing it to mktime, return true if it's OK that mktime returned T.
1157 It's not OK if *TM0 has out-of-range members. */
1160 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1162 if (t == (time_t) -1)
1164 /* Guard against falsely reporting an error when parsing a time
1165 stamp that happens to equal (time_t) -1, on a host that
1166 supports such a time stamp. */
1167 tm1 = localtime (&t);
1172 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1173 | (tm0->tm_min ^ tm1->tm_min)
1174 | (tm0->tm_hour ^ tm1->tm_hour)
1175 | (tm0->tm_mday ^ tm1->tm_mday)
1176 | (tm0->tm_mon ^ tm1->tm_mon)
1177 | (tm0->tm_year ^ tm1->tm_year));
1180 /* A reasonable upper bound for the size of ordinary TZ strings.
1181 Use heap allocation if TZ's length exceeds this. */
1182 enum { TZBUFSIZE = 100 };
1184 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1187 get_tz (char tzbuf[TZBUFSIZE])
1189 char *tz = getenv ("TZ");
1192 size_t tzsize = strlen (tz) + 1;
1193 tz = (tzsize <= TZBUFSIZE
1194 ? memcpy (tzbuf, tz, tzsize)
1195 : xmemdup (tz, tzsize));
1200 /* Parse a date/time string, storing the resulting time value into *RESULT.
1201 The string itself is pointed to by P. Return true if successful.
1202 P can be an incomplete or relative time specification; if so, use
1203 *NOW as the basis for the returned time. */
1205 get_date (struct timespec *result, char const *p, struct timespec const *now)
1209 struct tm const *tmp;
1213 struct timespec gettime_buffer;
1215 bool tz_was_altered = false;
1217 char tz0buf[TZBUFSIZE];
1222 gettime (&gettime_buffer);
1223 now = &gettime_buffer;
1226 Start = now->tv_sec;
1227 Start_ns = now->tv_nsec;
1229 tmp = localtime (&now->tv_sec);
1233 while (c = *p, c_isspace (c))
1236 if (strncmp (p, "TZ=\"", 4) == 0)
1238 char const *tzbase = p + 4;
1242 for (s = tzbase; *s; s++, tzsize++)
1246 if (! (*s == '\\' || *s == '"'))
1253 char tz1buf[TZBUFSIZE];
1254 bool large_tz = TZBUFSIZE < tzsize;
1256 tz0 = get_tz (tz0buf);
1257 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1258 for (s = tzbase; *s != '"'; s++)
1259 *z++ = *(s += *s == '\\');
1261 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1266 tz_was_altered = true;
1271 /* As documented, be careful to treat the empty string just like
1272 a date string of "0". Without this, an empty string would be
1273 declared invalid when parsed during a DST transition. */
1278 pc.year.value = tmp->tm_year;
1279 pc.year.value += TM_YEAR_BASE;
1281 pc.month = tmp->tm_mon + 1;
1282 pc.day = tmp->tm_mday;
1283 pc.hour = tmp->tm_hour;
1284 pc.minutes = tmp->tm_min;
1285 pc.seconds.tv_sec = tmp->tm_sec;
1286 pc.seconds.tv_nsec = Start_ns;
1287 tm.tm_isdst = tmp->tm_isdst;
1289 pc.meridian = MER24;
1290 pc.rel = RELATIVE_TIME_0;
1291 pc.timespec_seen = false;
1292 pc.rels_seen = false;
1296 pc.local_zones_seen = 0;
1300 #if HAVE_STRUCT_TM_TM_ZONE
1301 pc.local_time_zone_table[0].name = tmp->tm_zone;
1302 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1303 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1304 pc.local_time_zone_table[1].name = NULL;
1306 /* Probe the names used in the next three calendar quarters, looking
1307 for a tm_isdst different from the one we already have. */
1310 for (quarter = 1; quarter <= 3; quarter++)
1312 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1313 struct tm const *probe_tm = localtime (&probe);
1314 if (probe_tm && probe_tm->tm_zone
1315 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1318 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1319 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1320 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1321 pc.local_time_zone_table[2].name = NULL;
1330 # if !HAVE_DECL_TZNAME
1331 extern char *tzname[];
1334 for (i = 0; i < 2; i++)
1336 pc.local_time_zone_table[i].name = tzname[i];
1337 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1338 pc.local_time_zone_table[i].value = i;
1340 pc.local_time_zone_table[i].name = NULL;
1343 pc.local_time_zone_table[0].name = NULL;
1347 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1348 && ! strcmp (pc.local_time_zone_table[0].name,
1349 pc.local_time_zone_table[1].name))
1351 /* This locale uses the same abbrevation for standard and
1352 daylight times. So if we see that abbreviation, we don't
1353 know whether it's daylight time. */
1354 pc.local_time_zone_table[0].value = -1;
1355 pc.local_time_zone_table[1].name = NULL;
1358 if (yyparse (&pc) != 0)
1361 if (pc.timespec_seen)
1362 *result = pc.seconds;
1365 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1366 | (pc.local_zones_seen + pc.zones_seen)))
1369 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1370 tm.tm_mon = pc.month - 1;
1371 tm.tm_mday = pc.day;
1372 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1374 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1377 tm.tm_min = pc.minutes;
1378 tm.tm_sec = pc.seconds.tv_sec;
1382 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1383 pc.seconds.tv_nsec = 0;
1386 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1387 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1390 /* But if the input explicitly specifies local time with or without
1391 DST, give mktime that information. */
1392 if (pc.local_zones_seen)
1393 tm.tm_isdst = pc.local_isdst;
1397 Start = mktime (&tm);
1399 if (! mktime_ok (&tm0, &tm, Start))
1401 if (! pc.zones_seen)
1405 /* Guard against falsely reporting errors near the time_t
1406 boundaries when parsing times in other time zones. For
1407 example, suppose the input string "1969-12-31 23:00:00 -0100",
1408 the current time zone is 8 hours ahead of UTC, and the min
1409 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1410 localtime value is 1970-01-01 08:00:00, and mktime will
1411 therefore fail on 1969-12-31 23:00:00. To work around the
1412 problem, set the time zone to 1 hour behind UTC temporarily
1413 by setting TZ="XXX1:00" and try mktime again. */
1415 long int time_zone = pc.time_zone;
1416 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1417 long int abs_time_zone_hour = abs_time_zone / 60;
1418 int abs_time_zone_min = abs_time_zone % 60;
1419 char tz1buf[sizeof "XXX+0:00"
1420 + sizeof pc.time_zone * CHAR_BIT / 3];
1421 if (!tz_was_altered)
1422 tz0 = get_tz (tz0buf);
1423 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1424 abs_time_zone_hour, abs_time_zone_min);
1425 if (setenv ("TZ", tz1buf, 1) != 0)
1427 tz_was_altered = true;
1429 Start = mktime (&tm);
1430 if (! mktime_ok (&tm0, &tm, Start))
1435 if (pc.days_seen && ! pc.dates_seen)
1437 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1438 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1440 Start = mktime (&tm);
1441 if (Start == (time_t) -1)
1445 /* Add relative date. */
1446 if (pc.rel.year | pc.rel.month | pc.rel.day)
1448 int year = tm.tm_year + pc.rel.year;
1449 int month = tm.tm_mon + pc.rel.month;
1450 int day = tm.tm_mday + pc.rel.day;
1451 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1452 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1453 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1458 tm.tm_hour = tm0.tm_hour;
1459 tm.tm_min = tm0.tm_min;
1460 tm.tm_sec = tm0.tm_sec;
1461 tm.tm_isdst = tm0.tm_isdst;
1462 Start = mktime (&tm);
1463 if (Start == (time_t) -1)
1467 /* The only "output" of this if-block is an updated Start value,
1468 so this block must follow others that clobber Start. */
1471 long int delta = pc.time_zone * 60;
1473 #ifdef HAVE_TM_GMTOFF
1474 delta -= tm.tm_gmtoff;
1477 struct tm const *gmt = gmtime (&t);
1480 delta -= tm_diff (&tm, gmt);
1483 if ((Start < t1) != (delta < 0))
1484 goto fail; /* time_t overflow */
1488 /* Add relative hours, minutes, and seconds. On hosts that support
1489 leap seconds, ignore the possibility of leap seconds; e.g.,
1490 "+ 10 minutes" adds 600 seconds, even if one of them is a
1491 leap second. Typically this is not what the user wants, but it's
1492 too hard to do it the other way, because the time zone indicator
1493 must be applied before relative times, and if mktime is applied
1494 again the time zone will be lost. */
1496 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1497 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1499 long int d1 = 60 * 60 * pc.rel.hour;
1500 time_t t1 = t0 + d1;
1501 long int d2 = 60 * pc.rel.minutes;
1502 time_t t2 = t1 + d2;
1503 long int d3 = pc.rel.seconds;
1504 time_t t3 = t2 + d3;
1505 long int d4 = (sum_ns - normalized_ns) / BILLION;
1506 time_t t4 = t3 + d4;
1508 if ((d1 / (60 * 60) ^ pc.rel.hour)
1509 | (d2 / 60 ^ pc.rel.minutes)
1510 | ((t1 < t0) ^ (d1 < 0))
1511 | ((t2 < t1) ^ (d2 < 0))
1512 | ((t3 < t2) ^ (d3 < 0))
1513 | ((t4 < t3) ^ (d4 < 0)))
1516 result->tv_sec = t4;
1517 result->tv_nsec = normalized_ns;
1527 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1536 main (int ac, char **av)
1540 printf ("Enter date, or blank line to exit.\n\t> ");
1543 buff[BUFSIZ - 1] = '\0';
1544 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1547 struct tm const *tm;
1548 if (! get_date (&d, buff, NULL))
1549 printf ("Bad format - couldn't convert.\n");
1550 else if (! (tm = localtime (&d.tv_sec)))
1552 long int sec = d.tv_sec;
1553 printf ("localtime (%ld) failed\n", sec);
1558 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1559 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1560 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);