2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
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, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
21 /* Originally written by Steven M. Bellovin <smb@research.att.com> while
22 at the University of North Carolina at Chapel Hill. Later tweaked by
23 a couple of people on Usenet. Completely overhauled by Rich $alz
24 <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
26 Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
27 the right thing about local DST. Also modified by Paul Eggert
28 <eggert@cs.ucla.edu> in February 2004 to support
29 nanosecond-resolution time stamps, and in October 2004 to support
30 TZ strings in dates. */
32 /* FIXME: Check for arithmetic overflow in all cases, not just
43 /* There's no need to extend the stack, so there's no need to involve
45 #define YYSTACK_USE_ALLOCA 0
47 /* Tell Bison how much stack space is needed. 20 should be plenty for
48 this grammar, which is not right recursive. Beware setting it too
49 high, since that might cause problems on machines whose
50 implementations have lame stack-overflow checking. */
52 #define YYINITDEPTH YYMAXDEPTH
54 /* Since the code of getdate.y is not included in the Emacs executable
55 itself, there is no need to #define static in this file. Even if
56 the code were included in the Emacs executable, it probably
57 wouldn't do any harm to #undef it here; this will only cause
58 problems if we try to write to a static variable, which I don't
59 think this code needs to do. */
74 /* ISDIGIT differs from isdigit, as follows:
75 - Its arg may be any int or unsigned int; it need not be an unsigned char
77 - It's typically faster.
78 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
79 isdigit unless it's important to use the locale's definition
80 of `digit' even when the host does not conform to POSIX. */
81 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
84 # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
85 # define __attribute__(x)
89 #ifndef ATTRIBUTE_UNUSED
90 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
93 /* Shift A right by B bits portably, by dividing A by 2**B and
94 truncating towards minus infinity. A and B should be free of side
95 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
96 INT_BITS is the number of useful bits in an int. GNU code can
97 assume that INT_BITS is at least 32.
99 ISO C99 says that A >> B is implementation-defined if A < 0. Some
100 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
101 right in the usual way when A < 0, so SHR falls back on division if
102 ordinary A >> B doesn't seem to be the usual signed shift. */
106 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
108 #define EPOCH_YEAR 1970
109 #define TM_YEAR_BASE 1900
111 #define HOUR(x) ((x) * 60)
113 /* Lots of this code assumes time_t and time_t-like values fit into
114 long int. It also assumes that signed integer overflow silently
115 wraps around, but there's no portable way to check for that at
117 verify (TYPE_IS_INTEGER (time_t));
118 verify (LONG_MIN <= TYPE_MINIMUM (time_t) && TYPE_MAXIMUM (time_t) <= LONG_MAX);
120 /* An integer value, and the number of digits in its textual
129 /* An entry in the lexical lookup table. */
137 /* Meridian: am, pm, or 24-hour style. */
138 enum { MERam, MERpm, MER24 };
140 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
142 /* Relative times. */
145 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
155 #if HAVE_COMPOUND_LITERALS
156 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
158 static relative_time const RELATIVE_TIME_0;
161 /* Information passed to and from the parser. */
164 /* The input string remaining to be parsed. */
167 /* N, if this is the Nth Tuesday. */
168 long int day_ordinal;
170 /* Day of week; Sunday is 0. */
173 /* tm_isdst flag for the local zone. */
176 /* Time zone, in minutes east of UTC. */
179 /* Style used for time. */
182 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
188 struct timespec seconds; /* includes nanoseconds */
190 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
193 /* Presence or counts of nonterminals of various flavors parsed so far. */
198 size_t local_zones_seen;
203 /* Table of local time zone abbrevations, terminated by a null entry. */
204 table local_time_zone_table[3];
208 static int yylex (union YYSTYPE *, parser_control *);
209 static int yyerror (parser_control const *, char const *);
210 static long int time_zone_hhmm (textint, long int);
214 /* We want a reentrant parser, even if the TZ manipulation and the calls to
215 localtime and gmtime are not reentrant. */
217 %parse-param { parser_control *pc }
218 %lex-param { parser_control *pc }
220 /* This grammar has 20 shift/reduce conflicts. */
227 struct timespec timespec;
233 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
234 %token <intval> tDAY_UNIT
236 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
237 %token <intval> tMONTH tORDINAL tZONE
239 %token <textintval> tSNUMBER tUNUMBER
240 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
242 %type <intval> o_colon_minutes o_merid
243 %type <timespec> seconds signed_seconds unsigned_seconds
245 %type <rel> relunit relunit_snumber
258 pc->timespec_seen = true;
269 { pc->times_seen++; }
271 { pc->local_zones_seen++; }
273 { pc->zones_seen++; }
275 { pc->dates_seen++; }
279 { pc->rels_seen = true; }
288 pc->seconds.tv_sec = 0;
289 pc->seconds.tv_nsec = 0;
292 | tUNUMBER ':' tUNUMBER o_merid
295 pc->minutes = $3.value;
296 pc->seconds.tv_sec = 0;
297 pc->seconds.tv_nsec = 0;
300 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
303 pc->minutes = $3.value;
304 pc->seconds.tv_sec = 0;
305 pc->seconds.tv_nsec = 0;
306 pc->meridian = MER24;
308 pc->time_zone = time_zone_hhmm ($4, $5);
310 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
313 pc->minutes = $3.value;
317 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
320 pc->minutes = $3.value;
322 pc->meridian = MER24;
324 pc->time_zone = time_zone_hhmm ($6, $7);
331 pc->local_isdst = $1;
332 pc->dsts_seen += (0 < $1);
337 pc->dsts_seen += (0 < $1) + 1;
343 { pc->time_zone = $1; }
344 | tZONE relunit_snumber
345 { pc->time_zone = $1;
347 pc->rel.seconds += $2.seconds;
348 pc->rel.minutes += $2.minutes;
349 pc->rel.hour += $2.hour;
350 pc->rel.day += $2.day;
351 pc->rel.month += $2.month;
352 pc->rel.year += $2.year;
353 pc->rels_seen = true; }
354 | tZONE tSNUMBER o_colon_minutes
355 { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
357 { pc->time_zone = $1 + 60; }
359 { pc->time_zone = $1 + 60; }
375 pc->day_ordinal = $1;
380 pc->day_ordinal = $1.value;
386 tUNUMBER '/' tUNUMBER
388 pc->month = $1.value;
391 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
393 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
394 otherwise as MM/DD/YY.
395 The goal in recognizing YYYY/MM/DD is solely to support legacy
396 machine-generated dates like those in an RCS log listing. If
397 you want portability, use the ISO 8601 format. */
401 pc->month = $3.value;
406 pc->month = $1.value;
411 | tUNUMBER tSNUMBER tSNUMBER
413 /* ISO 8601 format. YYYY-MM-DD. */
415 pc->month = -$2.value;
418 | tUNUMBER tMONTH tSNUMBER
420 /* e.g. 17-JUN-1992. */
423 pc->year.value = -$3.value;
424 pc->year.digits = $3.digits;
426 | tMONTH tSNUMBER tSNUMBER
428 /* e.g. JUN-17-1992. */
431 pc->year.value = -$3.value;
432 pc->year.digits = $3.digits;
439 | tMONTH tUNUMBER ',' tUNUMBER
450 | tUNUMBER tMONTH tUNUMBER
462 pc->rel.seconds -= $1.seconds;
463 pc->rel.minutes -= $1.minutes;
464 pc->rel.hour -= $1.hour;
465 pc->rel.day -= $1.day;
466 pc->rel.month -= $1.month;
467 pc->rel.year -= $1.year;
472 pc->rel.seconds += $1.seconds;
473 pc->rel.minutes += $1.minutes;
474 pc->rel.hour += $1.hour;
475 pc->rel.day += $1.day;
476 pc->rel.month += $1.month;
477 pc->rel.year += $1.year;
483 { $$ = RELATIVE_TIME_0; $$.year = $1; }
484 | tUNUMBER tYEAR_UNIT
485 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
487 { $$ = RELATIVE_TIME_0; $$.year = 1; }
488 | tORDINAL tMONTH_UNIT
489 { $$ = RELATIVE_TIME_0; $$.month = $1; }
490 | tUNUMBER tMONTH_UNIT
491 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
493 { $$ = RELATIVE_TIME_0; $$.month = 1; }
495 { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
497 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
499 { $$ = RELATIVE_TIME_0; $$.day = $1; }
500 | tORDINAL tHOUR_UNIT
501 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
502 | tUNUMBER tHOUR_UNIT
503 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
505 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
506 | tORDINAL tMINUTE_UNIT
507 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
508 | tUNUMBER tMINUTE_UNIT
509 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
511 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
513 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
515 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
516 | tSDECIMAL_NUMBER tSEC_UNIT
517 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
518 | tUDECIMAL_NUMBER tSEC_UNIT
519 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
521 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
527 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
528 | tSNUMBER tMONTH_UNIT
529 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
531 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
532 | tSNUMBER tHOUR_UNIT
533 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
534 | tSNUMBER tMINUTE_UNIT
535 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
537 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
540 seconds: signed_seconds | unsigned_seconds;
545 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
551 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
557 if (pc->dates_seen && ! pc->year.digits
558 && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
565 pc->day = $1.value % 100;
566 pc->month = ($1.value / 100) % 100;
567 pc->year.value = $1.value / 10000;
568 pc->year.digits = $1.digits - 4;
580 pc->hour = $1.value / 100;
581 pc->minutes = $1.value % 100;
583 pc->seconds.tv_sec = 0;
584 pc->seconds.tv_nsec = 0;
585 pc->meridian = MER24;
607 static table const meridian_table[] =
609 { "AM", tMERIDIAN, MERam },
610 { "A.M.", tMERIDIAN, MERam },
611 { "PM", tMERIDIAN, MERpm },
612 { "P.M.", tMERIDIAN, MERpm },
616 static table const dst_table[] =
621 static table const month_and_day_table[] =
623 { "JANUARY", tMONTH, 1 },
624 { "FEBRUARY", tMONTH, 2 },
625 { "MARCH", tMONTH, 3 },
626 { "APRIL", tMONTH, 4 },
627 { "MAY", tMONTH, 5 },
628 { "JUNE", tMONTH, 6 },
629 { "JULY", tMONTH, 7 },
630 { "AUGUST", tMONTH, 8 },
631 { "SEPTEMBER",tMONTH, 9 },
632 { "SEPT", tMONTH, 9 },
633 { "OCTOBER", tMONTH, 10 },
634 { "NOVEMBER", tMONTH, 11 },
635 { "DECEMBER", tMONTH, 12 },
636 { "SUNDAY", tDAY, 0 },
637 { "MONDAY", tDAY, 1 },
638 { "TUESDAY", tDAY, 2 },
640 { "WEDNESDAY",tDAY, 3 },
641 { "WEDNES", tDAY, 3 },
642 { "THURSDAY", tDAY, 4 },
644 { "THURS", tDAY, 4 },
645 { "FRIDAY", tDAY, 5 },
646 { "SATURDAY", tDAY, 6 },
650 static table const time_units_table[] =
652 { "YEAR", tYEAR_UNIT, 1 },
653 { "MONTH", tMONTH_UNIT, 1 },
654 { "FORTNIGHT",tDAY_UNIT, 14 },
655 { "WEEK", tDAY_UNIT, 7 },
656 { "DAY", tDAY_UNIT, 1 },
657 { "HOUR", tHOUR_UNIT, 1 },
658 { "MINUTE", tMINUTE_UNIT, 1 },
659 { "MIN", tMINUTE_UNIT, 1 },
660 { "SECOND", tSEC_UNIT, 1 },
661 { "SEC", tSEC_UNIT, 1 },
665 /* Assorted relative-time words. */
666 static table const relative_time_table[] =
668 { "TOMORROW", tDAY_UNIT, 1 },
669 { "YESTERDAY",tDAY_UNIT, -1 },
670 { "TODAY", tDAY_UNIT, 0 },
671 { "NOW", tDAY_UNIT, 0 },
672 { "LAST", tORDINAL, -1 },
673 { "THIS", tORDINAL, 0 },
674 { "NEXT", tORDINAL, 1 },
675 { "FIRST", tORDINAL, 1 },
676 /*{ "SECOND", tORDINAL, 2 }, */
677 { "THIRD", tORDINAL, 3 },
678 { "FOURTH", tORDINAL, 4 },
679 { "FIFTH", tORDINAL, 5 },
680 { "SIXTH", tORDINAL, 6 },
681 { "SEVENTH", tORDINAL, 7 },
682 { "EIGHTH", tORDINAL, 8 },
683 { "NINTH", tORDINAL, 9 },
684 { "TENTH", tORDINAL, 10 },
685 { "ELEVENTH", tORDINAL, 11 },
686 { "TWELFTH", tORDINAL, 12 },
691 /* The universal time zone table. These labels can be used even for
692 time stamps that would not otherwise be valid, e.g., GMT time
693 stamps in London during summer. */
694 static table const universal_time_zone_table[] =
696 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
697 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
698 { "UTC", tZONE, HOUR ( 0) },
702 /* The time zone table. This table is necessarily incomplete, as time
703 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
704 as Eastern time in Australia, not as US Eastern Standard Time.
705 You cannot rely on getdate to handle arbitrary time zone
706 abbreviations; use numeric abbreviations like `-0500' instead. */
707 static table const time_zone_table[] =
709 { "WET", tZONE, HOUR ( 0) }, /* Western European */
710 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
711 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
712 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
713 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
714 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
715 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
716 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
717 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
718 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
719 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
720 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
721 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
722 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
723 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
724 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
725 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
726 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
727 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
728 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
729 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
730 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
731 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
732 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
733 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
734 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
735 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
736 { "CET", tZONE, HOUR ( 1) }, /* Central European */
737 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
738 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
739 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
740 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
741 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
742 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
743 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
744 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
745 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
746 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
747 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
748 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
749 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
750 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
751 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
752 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
753 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
754 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
755 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
759 /* Military time zone table. */
760 static table const military_table[] =
762 { "A", tZONE, -HOUR ( 1) },
763 { "B", tZONE, -HOUR ( 2) },
764 { "C", tZONE, -HOUR ( 3) },
765 { "D", tZONE, -HOUR ( 4) },
766 { "E", tZONE, -HOUR ( 5) },
767 { "F", tZONE, -HOUR ( 6) },
768 { "G", tZONE, -HOUR ( 7) },
769 { "H", tZONE, -HOUR ( 8) },
770 { "I", tZONE, -HOUR ( 9) },
771 { "K", tZONE, -HOUR (10) },
772 { "L", tZONE, -HOUR (11) },
773 { "M", tZONE, -HOUR (12) },
774 { "N", tZONE, HOUR ( 1) },
775 { "O", tZONE, HOUR ( 2) },
776 { "P", tZONE, HOUR ( 3) },
777 { "Q", tZONE, HOUR ( 4) },
778 { "R", tZONE, HOUR ( 5) },
779 { "S", tZONE, HOUR ( 6) },
780 { "T", tZONE, HOUR ( 7) },
781 { "U", tZONE, HOUR ( 8) },
782 { "V", tZONE, HOUR ( 9) },
783 { "W", tZONE, HOUR (10) },
784 { "X", tZONE, HOUR (11) },
785 { "Y", tZONE, HOUR (12) },
786 { "Z", tZONE, HOUR ( 0) },
792 /* Convert a time zone expressed as HH:MM into an integer count of
793 minutes. If MM is negative, then S is of the form HHMM and needs
794 to be picked apart; otherwise, S is of the form HH. */
797 time_zone_hhmm (textint s, long int mm)
800 return (s.value / 100) * 60 + s.value % 100;
802 return s.value * 60 + (s.negative ? -mm : mm);
806 to_hour (long int hours, int meridian)
810 default: /* Pacify GCC. */
812 return 0 <= hours && hours < 24 ? hours : -1;
814 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
816 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
821 to_year (textint textyear)
823 long int year = textyear.value;
828 /* XPG4 suggests that years 00-68 map to 2000-2068, and
829 years 69-99 map to 1969-1999. */
830 else if (textyear.digits == 2)
831 year += year < 69 ? 2000 : 1900;
837 lookup_zone (parser_control const *pc, char const *name)
841 for (tp = universal_time_zone_table; tp->name; tp++)
842 if (strcmp (name, tp->name) == 0)
845 /* Try local zone abbreviations before those in time_zone_table, as
846 the local ones are more likely to be right. */
847 for (tp = pc->local_time_zone_table; tp->name; tp++)
848 if (strcmp (name, tp->name) == 0)
851 for (tp = time_zone_table; tp->name; tp++)
852 if (strcmp (name, tp->name) == 0)
859 /* Yield the difference between *A and *B,
860 measured in seconds, ignoring leap seconds.
861 The body of this function is taken directly from the GNU C Library;
862 see src/strftime.c. */
864 tm_diff (struct tm const *a, struct tm const *b)
866 /* Compute intervening leap days correctly even if year is negative.
867 Take care to avoid int overflow in leap day calculations. */
868 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
869 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
870 int a100 = a4 / 25 - (a4 % 25 < 0);
871 int b100 = b4 / 25 - (b4 % 25 < 0);
872 int a400 = SHR (a100, 2);
873 int b400 = SHR (b100, 2);
874 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
875 long int ayear = a->tm_year;
876 long int years = ayear - b->tm_year;
877 long int days = (365 * years + intervening_leap_days
878 + (a->tm_yday - b->tm_yday));
879 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
880 + (a->tm_min - b->tm_min))
881 + (a->tm_sec - b->tm_sec));
883 #endif /* ! HAVE_TM_GMTOFF */
886 lookup_word (parser_control const *pc, char *word)
895 /* Make it uppercase. */
896 for (p = word; *p; p++)
898 unsigned char ch = *p;
902 for (tp = meridian_table; tp->name; tp++)
903 if (strcmp (word, tp->name) == 0)
906 /* See if we have an abbreviation for a month. */
907 wordlen = strlen (word);
908 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
910 for (tp = month_and_day_table; tp->name; tp++)
911 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
914 if ((tp = lookup_zone (pc, word)))
917 if (strcmp (word, dst_table[0].name) == 0)
920 for (tp = time_units_table; tp->name; tp++)
921 if (strcmp (word, tp->name) == 0)
924 /* Strip off any plural and try the units table again. */
925 if (word[wordlen - 1] == 'S')
927 word[wordlen - 1] = '\0';
928 for (tp = time_units_table; tp->name; tp++)
929 if (strcmp (word, tp->name) == 0)
931 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
934 for (tp = relative_time_table; tp->name; tp++)
935 if (strcmp (word, tp->name) == 0)
938 /* Military time zones. */
940 for (tp = military_table; tp->name; tp++)
941 if (word[0] == tp->name[0])
944 /* Drop out any periods and try the time zone table again. */
945 for (period_found = false, p = q = word; (*p = *q); q++)
950 if (period_found && (tp = lookup_zone (pc, word)))
957 yylex (YYSTYPE *lvalp, parser_control *pc)
964 while (c = *pc->input, isspace (c))
967 if (ISDIGIT (c) || c == '-' || c == '+')
971 unsigned long int value;
972 if (c == '-' || c == '+')
974 sign = c == '-' ? -1 : 1;
975 while (c = *++pc->input, isspace (c))
978 /* skip the '-' sign */
984 for (value = 0; ; value *= 10)
986 unsigned long int value1 = value + (c - '0');
993 if (ULONG_MAX / 10 < value)
996 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1001 unsigned long int value1;
1003 /* Check for overflow when converting value to time_t. */
1018 if (value != value1)
1021 /* Accumulate fraction, to ns precision. */
1024 for (digits = 2; digits <= LOG10_BILLION; digits++)
1031 /* Skip excess digits, truncating toward -Infinity. */
1033 for (; ISDIGIT (*p); p++)
1039 while (ISDIGIT (*p))
1042 /* Adjust to the timespec convention, which is that
1043 tv_nsec is always a positive offset even if tv_sec is
1053 lvalp->timespec.tv_sec = s;
1054 lvalp->timespec.tv_nsec = ns;
1056 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1060 lvalp->textintval.negative = sign < 0;
1063 lvalp->textintval.value = - value;
1064 if (0 < lvalp->textintval.value)
1069 lvalp->textintval.value = value;
1070 if (lvalp->textintval.value < 0)
1073 lvalp->textintval.digits = p - pc->input;
1075 return sign ? tSNUMBER : tUNUMBER;
1087 if (p < buff + sizeof buff - 1)
1091 while (isalpha (c) || c == '.');
1094 tp = lookup_word (pc, buff);
1097 lvalp->intval = tp->value;
1102 return *pc->input++;
1118 /* Do nothing if the parser reports an error. */
1120 yyerror (parser_control const *pc ATTRIBUTE_UNUSED,
1121 char const *s ATTRIBUTE_UNUSED)
1126 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1127 passing it to mktime, return true if it's OK that mktime returned T.
1128 It's not OK if *TM0 has out-of-range members. */
1131 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1133 if (t == (time_t) -1)
1135 /* Guard against falsely reporting an error when parsing a time
1136 stamp that happens to equal (time_t) -1, on a host that
1137 supports such a time stamp. */
1138 tm1 = localtime (&t);
1143 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1144 | (tm0->tm_min ^ tm1->tm_min)
1145 | (tm0->tm_hour ^ tm1->tm_hour)
1146 | (tm0->tm_mday ^ tm1->tm_mday)
1147 | (tm0->tm_mon ^ tm1->tm_mon)
1148 | (tm0->tm_year ^ tm1->tm_year));
1151 /* A reasonable upper bound for the size of ordinary TZ strings.
1152 Use heap allocation if TZ's length exceeds this. */
1153 enum { TZBUFSIZE = 100 };
1155 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1158 get_tz (char tzbuf[TZBUFSIZE])
1160 char *tz = getenv ("TZ");
1163 size_t tzsize = strlen (tz) + 1;
1164 tz = (tzsize <= TZBUFSIZE
1165 ? memcpy (tzbuf, tz, tzsize)
1166 : xmemdup (tz, tzsize));
1171 /* Parse a date/time string, storing the resulting time value into *RESULT.
1172 The string itself is pointed to by P. Return true if successful.
1173 P can be an incomplete or relative time specification; if so, use
1174 *NOW as the basis for the returned time. */
1176 get_date (struct timespec *result, char const *p, struct timespec const *now)
1180 struct tm const *tmp;
1184 struct timespec gettime_buffer;
1186 bool tz_was_altered = false;
1188 char tz0buf[TZBUFSIZE];
1193 gettime (&gettime_buffer);
1194 now = &gettime_buffer;
1197 Start = now->tv_sec;
1198 Start_ns = now->tv_nsec;
1200 tmp = localtime (&now->tv_sec);
1204 while (c = *p, isspace (c))
1207 if (strncmp (p, "TZ=\"", 4) == 0)
1209 char const *tzbase = p + 4;
1213 for (s = tzbase; *s; s++, tzsize++)
1217 if (! (*s == '\\' || *s == '"'))
1224 char tz1buf[TZBUFSIZE];
1225 bool large_tz = TZBUFSIZE < tzsize;
1227 tz0 = get_tz (tz0buf);
1228 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1229 for (s = tzbase; *s != '"'; s++)
1230 *z++ = *(s += *s == '\\');
1232 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1237 tz_was_altered = true;
1243 pc.year.value = tmp->tm_year;
1244 pc.year.value += TM_YEAR_BASE;
1246 pc.month = tmp->tm_mon + 1;
1247 pc.day = tmp->tm_mday;
1248 pc.hour = tmp->tm_hour;
1249 pc.minutes = tmp->tm_min;
1250 pc.seconds.tv_sec = tmp->tm_sec;
1251 pc.seconds.tv_nsec = Start_ns;
1252 tm.tm_isdst = tmp->tm_isdst;
1254 pc.meridian = MER24;
1255 pc.rel = RELATIVE_TIME_0;
1256 pc.timespec_seen = false;
1257 pc.rels_seen = false;
1261 pc.local_zones_seen = 0;
1265 #if HAVE_STRUCT_TM_TM_ZONE
1266 pc.local_time_zone_table[0].name = tmp->tm_zone;
1267 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1268 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1269 pc.local_time_zone_table[1].name = NULL;
1271 /* Probe the names used in the next three calendar quarters, looking
1272 for a tm_isdst different from the one we already have. */
1275 for (quarter = 1; quarter <= 3; quarter++)
1277 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1278 struct tm const *probe_tm = localtime (&probe);
1279 if (probe_tm && probe_tm->tm_zone
1280 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1283 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1284 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1285 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1286 pc.local_time_zone_table[2].name = NULL;
1296 extern char *tzname[];
1299 for (i = 0; i < 2; i++)
1301 pc.local_time_zone_table[i].name = tzname[i];
1302 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1303 pc.local_time_zone_table[i].value = i;
1305 pc.local_time_zone_table[i].name = NULL;
1308 pc.local_time_zone_table[0].name = NULL;
1312 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1313 && ! strcmp (pc.local_time_zone_table[0].name,
1314 pc.local_time_zone_table[1].name))
1316 /* This locale uses the same abbrevation for standard and
1317 daylight times. So if we see that abbreviation, we don't
1318 know whether it's daylight time. */
1319 pc.local_time_zone_table[0].value = -1;
1320 pc.local_time_zone_table[1].name = NULL;
1323 if (yyparse (&pc) != 0)
1326 if (pc.timespec_seen)
1327 *result = pc.seconds;
1330 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1331 | (pc.local_zones_seen + pc.zones_seen)))
1334 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1335 tm.tm_mon = pc.month - 1;
1336 tm.tm_mday = pc.day;
1337 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1339 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1342 tm.tm_min = pc.minutes;
1343 tm.tm_sec = pc.seconds.tv_sec;
1347 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1348 pc.seconds.tv_nsec = 0;
1351 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1352 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1355 /* But if the input explicitly specifies local time with or without
1356 DST, give mktime that information. */
1357 if (pc.local_zones_seen)
1358 tm.tm_isdst = pc.local_isdst;
1362 Start = mktime (&tm);
1364 if (! mktime_ok (&tm0, &tm, Start))
1366 if (! pc.zones_seen)
1370 /* Guard against falsely reporting errors near the time_t
1371 boundaries when parsing times in other time zones. For
1372 example, suppose the input string "1969-12-31 23:00:00 -0100",
1373 the current time zone is 8 hours ahead of UTC, and the min
1374 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1375 localtime value is 1970-01-01 08:00:00, and mktime will
1376 therefore fail on 1969-12-31 23:00:00. To work around the
1377 problem, set the time zone to 1 hour behind UTC temporarily
1378 by setting TZ="XXX1:00" and try mktime again. */
1380 long int time_zone = pc.time_zone;
1381 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1382 long int abs_time_zone_hour = abs_time_zone / 60;
1383 int abs_time_zone_min = abs_time_zone % 60;
1384 char tz1buf[sizeof "XXX+0:00"
1385 + sizeof pc.time_zone * CHAR_BIT / 3];
1386 if (!tz_was_altered)
1387 tz0 = get_tz (tz0buf);
1388 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1389 abs_time_zone_hour, abs_time_zone_min);
1390 if (setenv ("TZ", tz1buf, 1) != 0)
1392 tz_was_altered = true;
1394 Start = mktime (&tm);
1395 if (! mktime_ok (&tm0, &tm, Start))
1400 if (pc.days_seen && ! pc.dates_seen)
1402 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1403 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1405 Start = mktime (&tm);
1406 if (Start == (time_t) -1)
1412 long int delta = pc.time_zone * 60;
1414 #ifdef HAVE_TM_GMTOFF
1415 delta -= tm.tm_gmtoff;
1418 struct tm const *gmt = gmtime (&t);
1421 delta -= tm_diff (&tm, gmt);
1424 if ((Start < t1) != (delta < 0))
1425 goto fail; /* time_t overflow */
1429 /* Add relative date. */
1430 if (pc.rel.year | pc.rel.month | pc.rel.day)
1432 int year = tm.tm_year + pc.rel.year;
1433 int month = tm.tm_mon + pc.rel.month;
1434 int day = tm.tm_mday + pc.rel.day;
1435 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1436 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1437 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1442 tm.tm_hour = tm0.tm_hour;
1443 tm.tm_min = tm0.tm_min;
1444 tm.tm_sec = tm0.tm_sec;
1445 tm.tm_isdst = tm0.tm_isdst;
1446 Start = mktime (&tm);
1447 if (Start == (time_t) -1)
1451 /* Add relative hours, minutes, and seconds. On hosts that support
1452 leap seconds, ignore the possibility of leap seconds; e.g.,
1453 "+ 10 minutes" adds 600 seconds, even if one of them is a
1454 leap second. Typically this is not what the user wants, but it's
1455 too hard to do it the other way, because the time zone indicator
1456 must be applied before relative times, and if mktime is applied
1457 again the time zone will be lost. */
1459 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1460 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1462 long int d1 = 60 * 60 * pc.rel.hour;
1463 time_t t1 = t0 + d1;
1464 long int d2 = 60 * pc.rel.minutes;
1465 time_t t2 = t1 + d2;
1466 long int d3 = pc.rel.seconds;
1467 time_t t3 = t2 + d3;
1468 long int d4 = (sum_ns - normalized_ns) / BILLION;
1469 time_t t4 = t3 + d4;
1471 if ((d1 / (60 * 60) ^ pc.rel.hour)
1472 | (d2 / 60 ^ pc.rel.minutes)
1473 | ((t1 < t0) ^ (d1 < 0))
1474 | ((t2 < t1) ^ (d2 < 0))
1475 | ((t3 < t2) ^ (d3 < 0))
1476 | ((t4 < t3) ^ (d4 < 0)))
1479 result->tv_sec = t4;
1480 result->tv_nsec = normalized_ns;
1490 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1499 main (int ac, char **av)
1503 printf ("Enter date, or blank line to exit.\n\t> ");
1506 buff[BUFSIZ - 1] = '\0';
1507 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1510 struct tm const *tm;
1511 if (! get_date (&d, buff, NULL))
1512 printf ("Bad format - couldn't convert.\n");
1513 else if (! (tm = localtime (&d.tv_sec)))
1515 long int sec = d.tv_sec;
1516 printf ("localtime (%ld) failed\n", sec);
1521 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1522 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1523 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);