2 /* Parse a string into an internal time stamp.
3 Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 /* Originally written by Steven M. Bellovin <smb@research.att.com> while
20 at the University of North Carolina at Chapel Hill. Later tweaked by
21 a couple of people on Usenet. Completely overhauled by Rich $alz
22 <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
24 Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
25 the right thing about local DST. Also modified by Paul Eggert
26 <eggert@cs.ucla.edu> in February 2004 to support
27 nanosecond-resolution time stamps, and in October 2004 to support
28 TZ strings in dates. */
30 /* FIXME: Check for arithmetic overflow in all cases, not just
39 /* Use alloca only if it is known to be builtin. */
43 # 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 alloca
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 #if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
73 # define IN_CTYPE_DOMAIN(c) 1
75 # define IN_CTYPE_DOMAIN(c) isascii (c)
78 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
79 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
80 #define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
82 /* ISDIGIT differs from isdigit, as follows:
83 - Its arg may be any int or unsigned int; it need not be an unsigned char.
84 - It's guaranteed to evaluate its argument exactly once.
85 - It's typically faster.
86 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
87 isdigit unless it's important to use the locale's definition
88 of `digit' even when the host does not conform to POSIX. */
89 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
91 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
92 # define __attribute__(x)
95 #ifndef ATTRIBUTE_UNUSED
96 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
99 /* Shift A right by B bits portably, by dividing A by 2**B and
100 truncating towards minus infinity. A and B should be free of side
101 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
102 INT_BITS is the number of useful bits in an int. GNU code can
103 assume that INT_BITS is at least 32.
105 ISO C99 says that A >> B is implementation-defined if A < 0. Some
106 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
107 right in the usual way when A < 0, so SHR falls back on division if
108 ordinary A >> B doesn't seem to be the usual signed shift. */
112 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
114 #define EPOCH_YEAR 1970
115 #define TM_YEAR_BASE 1900
117 #define HOUR(x) ((x) * 60)
119 /* An integer value, and the number of digits in its textual
128 /* An entry in the lexical lookup table. */
136 /* Meridian: am, pm, or 24-hour style. */
137 enum { MERam, MERpm, MER24 };
139 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
141 /* Information passed to and from the parser. */
144 /* The input string remaining to be parsed. */
147 /* N, if this is the Nth Tuesday. */
148 long int day_ordinal;
150 /* Day of week; Sunday is 0. */
153 /* tm_isdst flag for the local zone. */
156 /* Time zone, in minutes east of UTC. */
159 /* Style used for time. */
162 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
168 struct timespec seconds; /* includes nanoseconds */
170 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
175 long int rel_minutes;
176 long int rel_seconds;
179 /* Counts of nonterminals of various flavors parsed so far. */
183 size_t local_zones_seen;
188 /* Table of local time zone abbrevations, terminated by a null entry. */
189 table local_time_zone_table[3];
193 static int yylex (union YYSTYPE *, parser_control *);
194 static int yyerror (parser_control *, char *);
195 static long int time_zone_hhmm (textint, long int);
199 /* We want a reentrant parser, even if the TZ manipulation and the calls to
200 localtime and gmtime are not reentrant. */
202 %parse-param { parser_control *pc }
203 %lex-param { parser_control *pc }
205 /* This grammar has 14 shift/reduce conflicts. */
212 struct timespec timespec;
217 %token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN
218 %token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tORDINAL
219 %token <intval> tSEC_UNIT tYEAR_UNIT tZONE
221 %token <textintval> tSNUMBER tUNUMBER
222 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
224 %type <intval> o_colon_minutes o_merid
225 %type <timespec> seconds signed_seconds unsigned_seconds
238 pc->timespec_seen = true;
249 { pc->times_seen++; }
251 { pc->local_zones_seen++; }
253 { pc->zones_seen++; }
255 { pc->dates_seen++; }
268 pc->seconds.tv_sec = 0;
269 pc->seconds.tv_nsec = 0;
272 | tUNUMBER ':' tUNUMBER o_merid
275 pc->minutes = $3.value;
276 pc->seconds.tv_sec = 0;
277 pc->seconds.tv_nsec = 0;
280 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
283 pc->minutes = $3.value;
284 pc->seconds.tv_sec = 0;
285 pc->seconds.tv_nsec = 0;
286 pc->meridian = MER24;
288 pc->time_zone = time_zone_hhmm ($4, $5);
290 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
293 pc->minutes = $3.value;
297 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
300 pc->minutes = $3.value;
302 pc->meridian = MER24;
304 pc->time_zone = time_zone_hhmm ($6, $7);
310 { pc->local_isdst = $1; }
312 { pc->local_isdst = $1 < 0 ? 1 : $1 + 1; }
317 { pc->time_zone = $1; }
318 | tZONE tSNUMBER o_colon_minutes
319 { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
321 { pc->time_zone = $1 + 60; }
323 { pc->time_zone = $1 + 60; }
339 pc->day_ordinal = $1;
344 pc->day_ordinal = $1.value;
350 tUNUMBER '/' tUNUMBER
352 pc->month = $1.value;
355 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
357 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
358 otherwise as MM/DD/YY.
359 The goal in recognizing YYYY/MM/DD is solely to support legacy
360 machine-generated dates like those in an RCS log listing. If
361 you want portability, use the ISO 8601 format. */
365 pc->month = $3.value;
370 pc->month = $1.value;
375 | tUNUMBER tSNUMBER tSNUMBER
377 /* ISO 8601 format. YYYY-MM-DD. */
379 pc->month = -$2.value;
382 | tUNUMBER tMONTH tSNUMBER
384 /* e.g. 17-JUN-1992. */
387 pc->year.value = -$3.value;
388 pc->year.digits = $3.digits;
390 | tMONTH tSNUMBER tSNUMBER
392 /* e.g. JUN-17-1992. */
395 pc->year.value = -$3.value;
396 pc->year.digits = $3.digits;
403 | tMONTH tUNUMBER ',' tUNUMBER
414 | tUNUMBER tMONTH tUNUMBER
425 pc->rel_ns = -pc->rel_ns;
426 pc->rel_seconds = -pc->rel_seconds;
427 pc->rel_minutes = -pc->rel_minutes;
428 pc->rel_hour = -pc->rel_hour;
429 pc->rel_day = -pc->rel_day;
430 pc->rel_month = -pc->rel_month;
431 pc->rel_year = -pc->rel_year;
438 { pc->rel_year += $1 * $2; }
439 | tUNUMBER tYEAR_UNIT
440 { pc->rel_year += $1.value * $2; }
441 | tSNUMBER tYEAR_UNIT
442 { pc->rel_year += $1.value * $2; }
444 { pc->rel_year += $1; }
445 | tORDINAL tMONTH_UNIT
446 { pc->rel_month += $1 * $2; }
447 | tUNUMBER tMONTH_UNIT
448 { pc->rel_month += $1.value * $2; }
449 | tSNUMBER tMONTH_UNIT
450 { pc->rel_month += $1.value * $2; }
452 { pc->rel_month += $1; }
454 { pc->rel_day += $1 * $2; }
456 { pc->rel_day += $1.value * $2; }
458 { pc->rel_day += $1.value * $2; }
460 { pc->rel_day += $1; }
461 | tORDINAL tHOUR_UNIT
462 { pc->rel_hour += $1 * $2; }
463 | tUNUMBER tHOUR_UNIT
464 { pc->rel_hour += $1.value * $2; }
465 | tSNUMBER tHOUR_UNIT
466 { pc->rel_hour += $1.value * $2; }
468 { pc->rel_hour += $1; }
469 | tORDINAL tMINUTE_UNIT
470 { pc->rel_minutes += $1 * $2; }
471 | tUNUMBER tMINUTE_UNIT
472 { pc->rel_minutes += $1.value * $2; }
473 | tSNUMBER tMINUTE_UNIT
474 { pc->rel_minutes += $1.value * $2; }
476 { pc->rel_minutes += $1; }
478 { pc->rel_seconds += $1 * $2; }
480 { pc->rel_seconds += $1.value * $2; }
482 { pc->rel_seconds += $1.value * $2; }
483 | tSDECIMAL_NUMBER tSEC_UNIT
484 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
485 | tUDECIMAL_NUMBER tSEC_UNIT
486 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
488 { pc->rel_seconds += $1; }
491 seconds: signed_seconds | unsigned_seconds;
496 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
502 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
509 && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
516 pc->day = $1.value % 100;
517 pc->month = ($1.value / 100) % 100;
518 pc->year.value = $1.value / 10000;
519 pc->year.digits = $1.digits - 4;
531 pc->hour = $1.value / 100;
532 pc->minutes = $1.value % 100;
534 pc->seconds.tv_sec = 0;
535 pc->seconds.tv_nsec = 0;
536 pc->meridian = MER24;
558 static table const meridian_table[] =
560 { "AM", tMERIDIAN, MERam },
561 { "A.M.", tMERIDIAN, MERam },
562 { "PM", tMERIDIAN, MERpm },
563 { "P.M.", tMERIDIAN, MERpm },
567 static table const dst_table[] =
572 static table const month_and_day_table[] =
574 { "JANUARY", tMONTH, 1 },
575 { "FEBRUARY", tMONTH, 2 },
576 { "MARCH", tMONTH, 3 },
577 { "APRIL", tMONTH, 4 },
578 { "MAY", tMONTH, 5 },
579 { "JUNE", tMONTH, 6 },
580 { "JULY", tMONTH, 7 },
581 { "AUGUST", tMONTH, 8 },
582 { "SEPTEMBER",tMONTH, 9 },
583 { "SEPT", tMONTH, 9 },
584 { "OCTOBER", tMONTH, 10 },
585 { "NOVEMBER", tMONTH, 11 },
586 { "DECEMBER", tMONTH, 12 },
587 { "SUNDAY", tDAY, 0 },
588 { "MONDAY", tDAY, 1 },
589 { "TUESDAY", tDAY, 2 },
591 { "WEDNESDAY",tDAY, 3 },
592 { "WEDNES", tDAY, 3 },
593 { "THURSDAY", tDAY, 4 },
595 { "THURS", tDAY, 4 },
596 { "FRIDAY", tDAY, 5 },
597 { "SATURDAY", tDAY, 6 },
601 static table const time_units_table[] =
603 { "YEAR", tYEAR_UNIT, 1 },
604 { "MONTH", tMONTH_UNIT, 1 },
605 { "FORTNIGHT",tDAY_UNIT, 14 },
606 { "WEEK", tDAY_UNIT, 7 },
607 { "DAY", tDAY_UNIT, 1 },
608 { "HOUR", tHOUR_UNIT, 1 },
609 { "MINUTE", tMINUTE_UNIT, 1 },
610 { "MIN", tMINUTE_UNIT, 1 },
611 { "SECOND", tSEC_UNIT, 1 },
612 { "SEC", tSEC_UNIT, 1 },
616 /* Assorted relative-time words. */
617 static table const relative_time_table[] =
619 { "TOMORROW", tDAY_UNIT, 1 },
620 { "YESTERDAY",tDAY_UNIT, -1 },
621 { "TODAY", tDAY_UNIT, 0 },
622 { "NOW", tDAY_UNIT, 0 },
623 { "LAST", tORDINAL, -1 },
624 { "THIS", tORDINAL, 0 },
625 { "NEXT", tORDINAL, 1 },
626 { "FIRST", tORDINAL, 1 },
627 /*{ "SECOND", tORDINAL, 2 }, */
628 { "THIRD", tORDINAL, 3 },
629 { "FOURTH", tORDINAL, 4 },
630 { "FIFTH", tORDINAL, 5 },
631 { "SIXTH", tORDINAL, 6 },
632 { "SEVENTH", tORDINAL, 7 },
633 { "EIGHTH", tORDINAL, 8 },
634 { "NINTH", tORDINAL, 9 },
635 { "TENTH", tORDINAL, 10 },
636 { "ELEVENTH", tORDINAL, 11 },
637 { "TWELFTH", tORDINAL, 12 },
642 /* The time zone table. This table is necessarily incomplete, as time
643 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
644 as Eastern time in Australia, not as US Eastern Standard Time.
645 You cannot rely on getdate to handle arbitrary time zone
646 abbreviations; use numeric abbreviations like `-0500' instead. */
647 static table const time_zone_table[] =
649 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
650 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
651 { "UTC", tZONE, HOUR ( 0) },
652 { "WET", tZONE, HOUR ( 0) }, /* Western European */
653 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
654 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
655 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
656 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
657 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
658 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
659 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
660 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
661 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
662 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
663 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
664 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
665 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
666 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
667 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
668 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
669 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
670 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
671 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
672 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
673 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
674 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
675 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
676 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
677 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
678 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
679 { "CET", tZONE, HOUR ( 1) }, /* Central European */
680 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
681 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
682 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
683 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
684 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
685 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
686 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
687 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
688 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
689 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
690 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
691 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
692 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
693 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
694 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
695 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
696 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
697 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
698 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
702 /* Military time zone table. */
703 static table const military_table[] =
705 { "A", tZONE, -HOUR ( 1) },
706 { "B", tZONE, -HOUR ( 2) },
707 { "C", tZONE, -HOUR ( 3) },
708 { "D", tZONE, -HOUR ( 4) },
709 { "E", tZONE, -HOUR ( 5) },
710 { "F", tZONE, -HOUR ( 6) },
711 { "G", tZONE, -HOUR ( 7) },
712 { "H", tZONE, -HOUR ( 8) },
713 { "I", tZONE, -HOUR ( 9) },
714 { "K", tZONE, -HOUR (10) },
715 { "L", tZONE, -HOUR (11) },
716 { "M", tZONE, -HOUR (12) },
717 { "N", tZONE, HOUR ( 1) },
718 { "O", tZONE, HOUR ( 2) },
719 { "P", tZONE, HOUR ( 3) },
720 { "Q", tZONE, HOUR ( 4) },
721 { "R", tZONE, HOUR ( 5) },
722 { "S", tZONE, HOUR ( 6) },
723 { "T", tZONE, HOUR ( 7) },
724 { "U", tZONE, HOUR ( 8) },
725 { "V", tZONE, HOUR ( 9) },
726 { "W", tZONE, HOUR (10) },
727 { "X", tZONE, HOUR (11) },
728 { "Y", tZONE, HOUR (12) },
729 { "Z", tZONE, HOUR ( 0) },
735 /* Convert a time zone expressed as HH:MM into an integer count of
736 minutes. If MM is negative, then S is of the form HHMM and needs
737 to be picked apart; otherwise, S is of the form HH. */
740 time_zone_hhmm (textint s, long int mm)
743 return (s.value / 100) * 60 + s.value % 100;
745 return s.value * 60 + (s.negative ? -mm : mm);
749 to_hour (long int hours, int meridian)
753 default: /* Pacify GCC. */
755 return 0 <= hours && hours < 24 ? hours : -1;
757 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
759 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
764 to_year (textint textyear)
766 long int year = textyear.value;
771 /* XPG4 suggests that years 00-68 map to 2000-2068, and
772 years 69-99 map to 1969-1999. */
773 else if (textyear.digits == 2)
774 year += year < 69 ? 2000 : 1900;
780 lookup_zone (parser_control const *pc, char const *name)
784 /* Try local zone abbreviations first; they're more likely to be right. */
785 for (tp = pc->local_time_zone_table; tp->name; tp++)
786 if (strcmp (name, tp->name) == 0)
789 for (tp = time_zone_table; tp->name; tp++)
790 if (strcmp (name, tp->name) == 0)
797 /* Yield the difference between *A and *B,
798 measured in seconds, ignoring leap seconds.
799 The body of this function is taken directly from the GNU C Library;
800 see src/strftime.c. */
802 tm_diff (struct tm const *a, struct tm const *b)
804 /* Compute intervening leap days correctly even if year is negative.
805 Take care to avoid int overflow in leap day calculations. */
806 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
807 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
808 int a100 = a4 / 25 - (a4 % 25 < 0);
809 int b100 = b4 / 25 - (b4 % 25 < 0);
810 int a400 = SHR (a100, 2);
811 int b400 = SHR (b100, 2);
812 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
813 long int ayear = a->tm_year;
814 long int years = ayear - b->tm_year;
815 long int days = (365 * years + intervening_leap_days
816 + (a->tm_yday - b->tm_yday));
817 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
818 + (a->tm_min - b->tm_min))
819 + (a->tm_sec - b->tm_sec));
821 #endif /* ! HAVE_TM_GMTOFF */
824 lookup_word (parser_control const *pc, char *word)
833 /* Make it uppercase. */
834 for (p = word; *p; p++)
836 unsigned char ch = *p;
841 for (tp = meridian_table; tp->name; tp++)
842 if (strcmp (word, tp->name) == 0)
845 /* See if we have an abbreviation for a month. */
846 wordlen = strlen (word);
847 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
849 for (tp = month_and_day_table; tp->name; tp++)
850 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
853 if ((tp = lookup_zone (pc, word)))
856 if (strcmp (word, dst_table[0].name) == 0)
859 for (tp = time_units_table; tp->name; tp++)
860 if (strcmp (word, tp->name) == 0)
863 /* Strip off any plural and try the units table again. */
864 if (word[wordlen - 1] == 'S')
866 word[wordlen - 1] = '\0';
867 for (tp = time_units_table; tp->name; tp++)
868 if (strcmp (word, tp->name) == 0)
870 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
873 for (tp = relative_time_table; tp->name; tp++)
874 if (strcmp (word, tp->name) == 0)
877 /* Military time zones. */
879 for (tp = military_table; tp->name; tp++)
880 if (word[0] == tp->name[0])
883 /* Drop out any periods and try the time zone table again. */
884 for (period_found = false, p = q = word; (*p = *q); q++)
889 if (period_found && (tp = lookup_zone (pc, word)))
896 yylex (YYSTYPE *lvalp, parser_control *pc)
903 while (c = *pc->input, ISSPACE (c))
906 if (ISDIGIT (c) || c == '-' || c == '+')
910 unsigned long int value;
911 if (c == '-' || c == '+')
913 sign = c == '-' ? -1 : 1;
914 while (c = *++pc->input, ISSPACE (c))
917 /* skip the '-' sign */
923 for (value = 0; ; value *= 10)
925 unsigned long int value1 = value + (c - '0');
932 if (ULONG_MAX / 10 < value)
935 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
940 unsigned long int value1;
942 /* Check for overflow when converting value to time_t. */
960 /* Accumulate fraction, to ns precision. */
963 for (digits = 2; digits <= LOG10_BILLION; digits++)
970 /* Skip excess digits, truncating toward -Infinity. */
972 for (; ISDIGIT (*p); p++)
981 /* Adjust to the timespec convention, which is that
982 tv_nsec is always a positive offset even if tv_sec is
992 lvalp->timespec.tv_sec = s;
993 lvalp->timespec.tv_nsec = ns;
995 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
999 lvalp->textintval.negative = sign < 0;
1002 lvalp->textintval.value = - value;
1003 if (0 < lvalp->textintval.value)
1008 lvalp->textintval.value = value;
1009 if (lvalp->textintval.value < 0)
1012 lvalp->textintval.digits = p - pc->input;
1014 return sign ? tSNUMBER : tUNUMBER;
1026 if (p < buff + sizeof buff - 1)
1030 while (ISALPHA (c) || c == '.');
1033 tp = lookup_word (pc, buff);
1036 lvalp->intval = tp->value;
1041 return *pc->input++;
1057 /* Do nothing if the parser reports an error. */
1059 yyerror (parser_control *pc ATTRIBUTE_UNUSED, char *s ATTRIBUTE_UNUSED)
1064 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1065 passing it to mktime, return true if it's OK that mktime returned T.
1066 It's not OK if *TM0 has out-of-range members. */
1069 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1071 if (t == (time_t) -1)
1073 /* Guard against falsely reporting an error when parsing a time
1074 stamp that happens to equal (time_t) -1, on a host that
1075 supports such a time stamp. */
1076 tm1 = localtime (&t);
1081 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1082 | (tm0->tm_min ^ tm1->tm_min)
1083 | (tm0->tm_hour ^ tm1->tm_hour)
1084 | (tm0->tm_mday ^ tm1->tm_mday)
1085 | (tm0->tm_mon ^ tm1->tm_mon)
1086 | (tm0->tm_year ^ tm1->tm_year));
1089 /* A reasonable upper bound for the size of ordinary TZ strings.
1090 Use heap allocation if TZ's length exceeds this. */
1091 enum { TZBUFSIZE = 100 };
1093 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1096 get_tz (char tzbuf[TZBUFSIZE])
1098 char *tz = getenv ("TZ");
1101 size_t tzsize = strlen (tz) + 1;
1102 tz = (tzsize <= TZBUFSIZE
1103 ? memcpy (tzbuf, tz, tzsize)
1104 : xmemdup (tz, tzsize));
1109 /* Parse a date/time string, storing the resulting time value into *RESULT.
1110 The string itself is pointed to by P. Return true if successful.
1111 P can be an incomplete or relative time specification; if so, use
1112 *NOW as the basis for the returned time. */
1114 get_date (struct timespec *result, char const *p, struct timespec const *now)
1118 struct tm const *tmp;
1122 struct timespec gettime_buffer;
1124 bool tz_was_altered = false;
1126 char tz0buf[TZBUFSIZE];
1131 if (gettime (&gettime_buffer) != 0)
1133 now = &gettime_buffer;
1136 Start = now->tv_sec;
1137 Start_ns = now->tv_nsec;
1139 tmp = localtime (&now->tv_sec);
1143 while (c = *p, ISSPACE (c))
1146 if (strncmp (p, "TZ=\"", 4) == 0)
1148 char const *tzbase = p + 4;
1152 for (s = tzbase; *s; s++, tzsize++)
1156 if (! (*s == '\\' || *s == '"'))
1163 char tz1buf[TZBUFSIZE];
1164 bool large_tz = TZBUFSIZE < tzsize;
1166 tz0 = get_tz (tz0buf);
1167 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1168 for (s = tzbase; *s != '"'; s++)
1169 *z++ = *(s += *s == '\\');
1171 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1176 tz_was_altered = true;
1182 pc.year.value = tmp->tm_year;
1183 pc.year.value += TM_YEAR_BASE;
1185 pc.month = tmp->tm_mon + 1;
1186 pc.day = tmp->tm_mday;
1187 pc.hour = tmp->tm_hour;
1188 pc.minutes = tmp->tm_min;
1189 pc.seconds.tv_sec = tmp->tm_sec;
1190 pc.seconds.tv_nsec = Start_ns;
1191 tm.tm_isdst = tmp->tm_isdst;
1193 pc.meridian = MER24;
1201 pc.timespec_seen = false;
1206 pc.local_zones_seen = 0;
1209 #if HAVE_STRUCT_TM_TM_ZONE
1210 pc.local_time_zone_table[0].name = tmp->tm_zone;
1211 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1212 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1213 pc.local_time_zone_table[1].name = NULL;
1215 /* Probe the names used in the next three calendar quarters, looking
1216 for a tm_isdst different from the one we already have. */
1219 for (quarter = 1; quarter <= 3; quarter++)
1221 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1222 struct tm const *probe_tm = localtime (&probe);
1223 if (probe_tm && probe_tm->tm_zone
1224 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1227 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1228 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1229 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1230 pc.local_time_zone_table[2].name = NULL;
1240 extern char *tzname[];
1243 for (i = 0; i < 2; i++)
1245 pc.local_time_zone_table[i].name = tzname[i];
1246 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1247 pc.local_time_zone_table[i].value = i;
1249 pc.local_time_zone_table[i].name = NULL;
1252 pc.local_time_zone_table[0].name = NULL;
1256 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1257 && ! strcmp (pc.local_time_zone_table[0].name,
1258 pc.local_time_zone_table[1].name))
1260 /* This locale uses the same abbrevation for standard and
1261 daylight times. So if we see that abbreviation, we don't
1262 know whether it's daylight time. */
1263 pc.local_time_zone_table[0].value = -1;
1264 pc.local_time_zone_table[1].name = NULL;
1267 if (yyparse (&pc) != 0)
1270 if (pc.timespec_seen)
1271 *result = pc.seconds;
1274 if (1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
1275 || 1 < (pc.local_zones_seen + pc.zones_seen)
1276 || (pc.local_zones_seen && 1 < pc.local_isdst))
1279 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1280 tm.tm_mon = pc.month - 1;
1281 tm.tm_mday = pc.day;
1282 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1284 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1287 tm.tm_min = pc.minutes;
1288 tm.tm_sec = pc.seconds.tv_sec;
1292 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1293 pc.seconds.tv_nsec = 0;
1296 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1297 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1300 /* But if the input explicitly specifies local time with or without
1301 DST, give mktime that information. */
1302 if (pc.local_zones_seen)
1303 tm.tm_isdst = pc.local_isdst;
1307 Start = mktime (&tm);
1309 if (! mktime_ok (&tm0, &tm, Start))
1311 if (! pc.zones_seen)
1315 /* Guard against falsely reporting errors near the time_t
1316 boundaries when parsing times in other time zones. For
1317 example, suppose the input string "1969-12-31 23:00:00 -0100",
1318 the current time zone is 8 hours ahead of UTC, and the min
1319 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1320 localtime value is 1970-01-01 08:00:00, and mktime will
1321 therefore fail on 1969-12-31 23:00:00. To work around the
1322 problem, set the time zone to 1 hour behind UTC temporarily
1323 by setting TZ="XXX1:00" and try mktime again. */
1325 long int time_zone = pc.time_zone;
1326 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1327 long int abs_time_zone_hour = abs_time_zone / 60;
1328 int abs_time_zone_min = abs_time_zone % 60;
1329 char tz1buf[sizeof "XXX+0:00"
1330 + sizeof pc.time_zone * CHAR_BIT / 3];
1331 if (!tz_was_altered)
1332 tz0 = get_tz (tz0buf);
1333 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1334 abs_time_zone_hour, abs_time_zone_min);
1335 if (setenv ("TZ", tz1buf, 1) != 0)
1337 tz_was_altered = true;
1339 Start = mktime (&tm);
1340 if (! mktime_ok (&tm0, &tm, Start))
1345 if (pc.days_seen && ! pc.dates_seen)
1347 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1348 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1350 Start = mktime (&tm);
1351 if (Start == (time_t) -1)
1357 long int delta = pc.time_zone * 60;
1359 #ifdef HAVE_TM_GMTOFF
1360 delta -= tm.tm_gmtoff;
1363 struct tm const *gmt = gmtime (&t);
1366 delta -= tm_diff (&tm, gmt);
1369 if ((Start < t1) != (delta < 0))
1370 goto fail; /* time_t overflow */
1374 /* Add relative date. */
1375 if (pc.rel_year | pc.rel_month | pc.rel_day)
1377 int year = tm.tm_year + pc.rel_year;
1378 int month = tm.tm_mon + pc.rel_month;
1379 int day = tm.tm_mday + pc.rel_day;
1380 if (((year < tm.tm_year) ^ (pc.rel_year < 0))
1381 | ((month < tm.tm_mon) ^ (pc.rel_month < 0))
1382 | ((day < tm.tm_mday) ^ (pc.rel_day < 0)))
1387 Start = mktime (&tm);
1388 if (Start == (time_t) -1)
1392 /* Add relative hours, minutes, and seconds. On hosts that support
1393 leap seconds, ignore the possibility of leap seconds; e.g.,
1394 "+ 10 minutes" adds 600 seconds, even if one of them is a
1395 leap second. Typically this is not what the user wants, but it's
1396 too hard to do it the other way, because the time zone indicator
1397 must be applied before relative times, and if mktime is applied
1398 again the time zone will be lost. */
1400 long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
1401 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1403 long int d1 = 60 * 60 * pc.rel_hour;
1404 time_t t1 = t0 + d1;
1405 long int d2 = 60 * pc.rel_minutes;
1406 time_t t2 = t1 + d2;
1407 long int d3 = pc.rel_seconds;
1408 time_t t3 = t2 + d3;
1409 long int d4 = (sum_ns - normalized_ns) / BILLION;
1410 time_t t4 = t3 + d4;
1412 if ((d1 / (60 * 60) ^ pc.rel_hour)
1413 | (d2 / 60 ^ pc.rel_minutes)
1414 | ((t1 < t0) ^ (d1 < 0))
1415 | ((t2 < t1) ^ (d2 < 0))
1416 | ((t3 < t2) ^ (d3 < 0))
1417 | ((t4 < t3) ^ (d4 < 0)))
1420 result->tv_sec = t4;
1421 result->tv_nsec = normalized_ns;
1431 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1440 main (int ac, char **av)
1444 printf ("Enter date, or blank line to exit.\n\t> ");
1447 buff[BUFSIZ - 1] = '\0';
1448 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1451 struct tm const *tm;
1452 if (! get_date (&d, buff, NULL))
1453 printf ("Bad format - couldn't convert.\n");
1454 else if (! (tm = localtime (&d.tv_sec)))
1456 long int sec = d.tv_sec;
1457 printf ("localtime (%ld) failed\n", sec);
1462 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1463 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1464 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);