2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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
41 /* There's no need to extend the stack, so there's no need to involve
43 #define YYSTACK_USE_ALLOCA 0
45 /* Tell Bison how much stack space is needed. 20 should be plenty for
46 this grammar, which is not right recursive. Beware setting it too
47 high, since that might cause problems on machines whose
48 implementations have lame stack-overflow checking. */
50 #define YYINITDEPTH YYMAXDEPTH
52 /* Since the code of getdate.y is not included in the Emacs executable
53 itself, there is no need to #define static in this file. Even if
54 the code were included in the Emacs executable, it probably
55 wouldn't do any harm to #undef it here; this will only cause
56 problems if we try to write to a static variable, which I don't
57 think this code needs to do. */
71 #if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
72 # define IN_CTYPE_DOMAIN(c) 1
74 # define IN_CTYPE_DOMAIN(c) isascii (c)
77 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
78 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
79 #define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
81 /* ISDIGIT differs from isdigit, as follows:
82 - Its arg may be any int or unsigned int; it need not be an unsigned char.
83 - It's guaranteed to evaluate its argument exactly once.
84 - It's typically faster.
85 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
86 isdigit unless it's important to use the locale's definition
87 of `digit' even when the host does not conform to POSIX. */
88 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
90 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
91 # define __attribute__(x)
94 #ifndef ATTRIBUTE_UNUSED
95 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
98 /* Shift A right by B bits portably, by dividing A by 2**B and
99 truncating towards minus infinity. A and B should be free of side
100 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
101 INT_BITS is the number of useful bits in an int. GNU code can
102 assume that INT_BITS is at least 32.
104 ISO C99 says that A >> B is implementation-defined if A < 0. Some
105 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
106 right in the usual way when A < 0, so SHR falls back on division if
107 ordinary A >> B doesn't seem to be the usual signed shift. */
111 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
113 #define EPOCH_YEAR 1970
114 #define TM_YEAR_BASE 1900
116 #define HOUR(x) ((x) * 60)
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 /* Information passed to and from the parser. */
143 /* The input string remaining to be parsed. */
146 /* N, if this is the Nth Tuesday. */
147 long int day_ordinal;
149 /* Day of week; Sunday is 0. */
152 /* tm_isdst flag for the local zone. */
155 /* Time zone, in minutes east of UTC. */
158 /* Style used for time. */
161 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
167 struct timespec seconds; /* includes nanoseconds */
169 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
174 long int rel_minutes;
175 long int rel_seconds;
178 /* Presence or 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++; }
259 { pc->rels_seen = true; }
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);
311 pc->local_isdst = $1;
312 pc->dsts_seen += (0 < $1);
317 pc->dsts_seen += (0 < $1) + 1;
323 { pc->time_zone = $1; }
324 | tZONE tSNUMBER o_colon_minutes
325 { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
327 { pc->time_zone = $1 + 60; }
329 { pc->time_zone = $1 + 60; }
345 pc->day_ordinal = $1;
350 pc->day_ordinal = $1.value;
356 tUNUMBER '/' tUNUMBER
358 pc->month = $1.value;
361 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
363 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
364 otherwise as MM/DD/YY.
365 The goal in recognizing YYYY/MM/DD is solely to support legacy
366 machine-generated dates like those in an RCS log listing. If
367 you want portability, use the ISO 8601 format. */
371 pc->month = $3.value;
376 pc->month = $1.value;
381 | tUNUMBER tSNUMBER tSNUMBER
383 /* ISO 8601 format. YYYY-MM-DD. */
385 pc->month = -$2.value;
388 | tUNUMBER tMONTH tSNUMBER
390 /* e.g. 17-JUN-1992. */
393 pc->year.value = -$3.value;
394 pc->year.digits = $3.digits;
396 | tMONTH tSNUMBER tSNUMBER
398 /* e.g. JUN-17-1992. */
401 pc->year.value = -$3.value;
402 pc->year.digits = $3.digits;
409 | tMONTH tUNUMBER ',' tUNUMBER
420 | tUNUMBER tMONTH tUNUMBER
431 pc->rel_ns = -pc->rel_ns;
432 pc->rel_seconds = -pc->rel_seconds;
433 pc->rel_minutes = -pc->rel_minutes;
434 pc->rel_hour = -pc->rel_hour;
435 pc->rel_day = -pc->rel_day;
436 pc->rel_month = -pc->rel_month;
437 pc->rel_year = -pc->rel_year;
444 { pc->rel_year += $1 * $2; }
445 | tUNUMBER tYEAR_UNIT
446 { pc->rel_year += $1.value * $2; }
447 | tSNUMBER tYEAR_UNIT
448 { pc->rel_year += $1.value * $2; }
450 { pc->rel_year += $1; }
451 | tORDINAL tMONTH_UNIT
452 { pc->rel_month += $1 * $2; }
453 | tUNUMBER tMONTH_UNIT
454 { pc->rel_month += $1.value * $2; }
455 | tSNUMBER tMONTH_UNIT
456 { pc->rel_month += $1.value * $2; }
458 { pc->rel_month += $1; }
460 { pc->rel_day += $1 * $2; }
462 { pc->rel_day += $1.value * $2; }
464 { pc->rel_day += $1.value * $2; }
466 { pc->rel_day += $1; }
467 | tORDINAL tHOUR_UNIT
468 { pc->rel_hour += $1 * $2; }
469 | tUNUMBER tHOUR_UNIT
470 { pc->rel_hour += $1.value * $2; }
471 | tSNUMBER tHOUR_UNIT
472 { pc->rel_hour += $1.value * $2; }
474 { pc->rel_hour += $1; }
475 | tORDINAL tMINUTE_UNIT
476 { pc->rel_minutes += $1 * $2; }
477 | tUNUMBER tMINUTE_UNIT
478 { pc->rel_minutes += $1.value * $2; }
479 | tSNUMBER tMINUTE_UNIT
480 { pc->rel_minutes += $1.value * $2; }
482 { pc->rel_minutes += $1; }
484 { pc->rel_seconds += $1 * $2; }
486 { pc->rel_seconds += $1.value * $2; }
488 { pc->rel_seconds += $1.value * $2; }
489 | tSDECIMAL_NUMBER tSEC_UNIT
490 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
491 | tUDECIMAL_NUMBER tSEC_UNIT
492 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
494 { pc->rel_seconds += $1; }
497 seconds: signed_seconds | unsigned_seconds;
502 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
508 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
514 if (pc->dates_seen && ! pc->year.digits
515 && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
522 pc->day = $1.value % 100;
523 pc->month = ($1.value / 100) % 100;
524 pc->year.value = $1.value / 10000;
525 pc->year.digits = $1.digits - 4;
537 pc->hour = $1.value / 100;
538 pc->minutes = $1.value % 100;
540 pc->seconds.tv_sec = 0;
541 pc->seconds.tv_nsec = 0;
542 pc->meridian = MER24;
564 static table const meridian_table[] =
566 { "AM", tMERIDIAN, MERam },
567 { "A.M.", tMERIDIAN, MERam },
568 { "PM", tMERIDIAN, MERpm },
569 { "P.M.", tMERIDIAN, MERpm },
573 static table const dst_table[] =
578 static table const month_and_day_table[] =
580 { "JANUARY", tMONTH, 1 },
581 { "FEBRUARY", tMONTH, 2 },
582 { "MARCH", tMONTH, 3 },
583 { "APRIL", tMONTH, 4 },
584 { "MAY", tMONTH, 5 },
585 { "JUNE", tMONTH, 6 },
586 { "JULY", tMONTH, 7 },
587 { "AUGUST", tMONTH, 8 },
588 { "SEPTEMBER",tMONTH, 9 },
589 { "SEPT", tMONTH, 9 },
590 { "OCTOBER", tMONTH, 10 },
591 { "NOVEMBER", tMONTH, 11 },
592 { "DECEMBER", tMONTH, 12 },
593 { "SUNDAY", tDAY, 0 },
594 { "MONDAY", tDAY, 1 },
595 { "TUESDAY", tDAY, 2 },
597 { "WEDNESDAY",tDAY, 3 },
598 { "WEDNES", tDAY, 3 },
599 { "THURSDAY", tDAY, 4 },
601 { "THURS", tDAY, 4 },
602 { "FRIDAY", tDAY, 5 },
603 { "SATURDAY", tDAY, 6 },
607 static table const time_units_table[] =
609 { "YEAR", tYEAR_UNIT, 1 },
610 { "MONTH", tMONTH_UNIT, 1 },
611 { "FORTNIGHT",tDAY_UNIT, 14 },
612 { "WEEK", tDAY_UNIT, 7 },
613 { "DAY", tDAY_UNIT, 1 },
614 { "HOUR", tHOUR_UNIT, 1 },
615 { "MINUTE", tMINUTE_UNIT, 1 },
616 { "MIN", tMINUTE_UNIT, 1 },
617 { "SECOND", tSEC_UNIT, 1 },
618 { "SEC", tSEC_UNIT, 1 },
622 /* Assorted relative-time words. */
623 static table const relative_time_table[] =
625 { "TOMORROW", tDAY_UNIT, 1 },
626 { "YESTERDAY",tDAY_UNIT, -1 },
627 { "TODAY", tDAY_UNIT, 0 },
628 { "NOW", tDAY_UNIT, 0 },
629 { "LAST", tORDINAL, -1 },
630 { "THIS", tORDINAL, 0 },
631 { "NEXT", tORDINAL, 1 },
632 { "FIRST", tORDINAL, 1 },
633 /*{ "SECOND", tORDINAL, 2 }, */
634 { "THIRD", tORDINAL, 3 },
635 { "FOURTH", tORDINAL, 4 },
636 { "FIFTH", tORDINAL, 5 },
637 { "SIXTH", tORDINAL, 6 },
638 { "SEVENTH", tORDINAL, 7 },
639 { "EIGHTH", tORDINAL, 8 },
640 { "NINTH", tORDINAL, 9 },
641 { "TENTH", tORDINAL, 10 },
642 { "ELEVENTH", tORDINAL, 11 },
643 { "TWELFTH", tORDINAL, 12 },
648 /* The time zone table. This table is necessarily incomplete, as time
649 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
650 as Eastern time in Australia, not as US Eastern Standard Time.
651 You cannot rely on getdate to handle arbitrary time zone
652 abbreviations; use numeric abbreviations like `-0500' instead. */
653 static table const time_zone_table[] =
655 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
656 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
657 { "UTC", tZONE, HOUR ( 0) },
658 { "WET", tZONE, HOUR ( 0) }, /* Western European */
659 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
660 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
661 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
662 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
663 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
664 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
665 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
666 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
667 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
668 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
669 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
670 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
671 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
672 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
673 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
674 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
675 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
676 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
677 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
678 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
679 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
680 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
681 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
682 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
683 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
684 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
685 { "CET", tZONE, HOUR ( 1) }, /* Central European */
686 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
687 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
688 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
689 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
690 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
691 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
692 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
693 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
694 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
695 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
696 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
697 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
698 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
699 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
700 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
701 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
702 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
703 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
704 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
708 /* Military time zone table. */
709 static table const military_table[] =
711 { "A", tZONE, -HOUR ( 1) },
712 { "B", tZONE, -HOUR ( 2) },
713 { "C", tZONE, -HOUR ( 3) },
714 { "D", tZONE, -HOUR ( 4) },
715 { "E", tZONE, -HOUR ( 5) },
716 { "F", tZONE, -HOUR ( 6) },
717 { "G", tZONE, -HOUR ( 7) },
718 { "H", tZONE, -HOUR ( 8) },
719 { "I", tZONE, -HOUR ( 9) },
720 { "K", tZONE, -HOUR (10) },
721 { "L", tZONE, -HOUR (11) },
722 { "M", tZONE, -HOUR (12) },
723 { "N", tZONE, HOUR ( 1) },
724 { "O", tZONE, HOUR ( 2) },
725 { "P", tZONE, HOUR ( 3) },
726 { "Q", tZONE, HOUR ( 4) },
727 { "R", tZONE, HOUR ( 5) },
728 { "S", tZONE, HOUR ( 6) },
729 { "T", tZONE, HOUR ( 7) },
730 { "U", tZONE, HOUR ( 8) },
731 { "V", tZONE, HOUR ( 9) },
732 { "W", tZONE, HOUR (10) },
733 { "X", tZONE, HOUR (11) },
734 { "Y", tZONE, HOUR (12) },
735 { "Z", tZONE, HOUR ( 0) },
741 /* Convert a time zone expressed as HH:MM into an integer count of
742 minutes. If MM is negative, then S is of the form HHMM and needs
743 to be picked apart; otherwise, S is of the form HH. */
746 time_zone_hhmm (textint s, long int mm)
749 return (s.value / 100) * 60 + s.value % 100;
751 return s.value * 60 + (s.negative ? -mm : mm);
755 to_hour (long int hours, int meridian)
759 default: /* Pacify GCC. */
761 return 0 <= hours && hours < 24 ? hours : -1;
763 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
765 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
770 to_year (textint textyear)
772 long int year = textyear.value;
777 /* XPG4 suggests that years 00-68 map to 2000-2068, and
778 years 69-99 map to 1969-1999. */
779 else if (textyear.digits == 2)
780 year += year < 69 ? 2000 : 1900;
786 lookup_zone (parser_control const *pc, char const *name)
790 /* Try local zone abbreviations first; they're more likely to be right. */
791 for (tp = pc->local_time_zone_table; tp->name; tp++)
792 if (strcmp (name, tp->name) == 0)
795 for (tp = time_zone_table; tp->name; tp++)
796 if (strcmp (name, tp->name) == 0)
803 /* Yield the difference between *A and *B,
804 measured in seconds, ignoring leap seconds.
805 The body of this function is taken directly from the GNU C Library;
806 see src/strftime.c. */
808 tm_diff (struct tm const *a, struct tm const *b)
810 /* Compute intervening leap days correctly even if year is negative.
811 Take care to avoid int overflow in leap day calculations. */
812 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
813 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
814 int a100 = a4 / 25 - (a4 % 25 < 0);
815 int b100 = b4 / 25 - (b4 % 25 < 0);
816 int a400 = SHR (a100, 2);
817 int b400 = SHR (b100, 2);
818 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
819 long int ayear = a->tm_year;
820 long int years = ayear - b->tm_year;
821 long int days = (365 * years + intervening_leap_days
822 + (a->tm_yday - b->tm_yday));
823 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
824 + (a->tm_min - b->tm_min))
825 + (a->tm_sec - b->tm_sec));
827 #endif /* ! HAVE_TM_GMTOFF */
830 lookup_word (parser_control const *pc, char *word)
839 /* Make it uppercase. */
840 for (p = word; *p; p++)
842 unsigned char ch = *p;
847 for (tp = meridian_table; tp->name; tp++)
848 if (strcmp (word, tp->name) == 0)
851 /* See if we have an abbreviation for a month. */
852 wordlen = strlen (word);
853 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
855 for (tp = month_and_day_table; tp->name; tp++)
856 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
859 if ((tp = lookup_zone (pc, word)))
862 if (strcmp (word, dst_table[0].name) == 0)
865 for (tp = time_units_table; tp->name; tp++)
866 if (strcmp (word, tp->name) == 0)
869 /* Strip off any plural and try the units table again. */
870 if (word[wordlen - 1] == 'S')
872 word[wordlen - 1] = '\0';
873 for (tp = time_units_table; tp->name; tp++)
874 if (strcmp (word, tp->name) == 0)
876 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
879 for (tp = relative_time_table; tp->name; tp++)
880 if (strcmp (word, tp->name) == 0)
883 /* Military time zones. */
885 for (tp = military_table; tp->name; tp++)
886 if (word[0] == tp->name[0])
889 /* Drop out any periods and try the time zone table again. */
890 for (period_found = false, p = q = word; (*p = *q); q++)
895 if (period_found && (tp = lookup_zone (pc, word)))
902 yylex (YYSTYPE *lvalp, parser_control *pc)
909 while (c = *pc->input, ISSPACE (c))
912 if (ISDIGIT (c) || c == '-' || c == '+')
916 unsigned long int value;
917 if (c == '-' || c == '+')
919 sign = c == '-' ? -1 : 1;
920 while (c = *++pc->input, ISSPACE (c))
923 /* skip the '-' sign */
929 for (value = 0; ; value *= 10)
931 unsigned long int value1 = value + (c - '0');
938 if (ULONG_MAX / 10 < value)
941 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
946 unsigned long int value1;
948 /* Check for overflow when converting value to time_t. */
966 /* Accumulate fraction, to ns precision. */
969 for (digits = 2; digits <= LOG10_BILLION; digits++)
976 /* Skip excess digits, truncating toward -Infinity. */
978 for (; ISDIGIT (*p); p++)
987 /* Adjust to the timespec convention, which is that
988 tv_nsec is always a positive offset even if tv_sec is
998 lvalp->timespec.tv_sec = s;
999 lvalp->timespec.tv_nsec = ns;
1001 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1005 lvalp->textintval.negative = sign < 0;
1008 lvalp->textintval.value = - value;
1009 if (0 < lvalp->textintval.value)
1014 lvalp->textintval.value = value;
1015 if (lvalp->textintval.value < 0)
1018 lvalp->textintval.digits = p - pc->input;
1020 return sign ? tSNUMBER : tUNUMBER;
1032 if (p < buff + sizeof buff - 1)
1036 while (ISALPHA (c) || c == '.');
1039 tp = lookup_word (pc, buff);
1042 lvalp->intval = tp->value;
1047 return *pc->input++;
1063 /* Do nothing if the parser reports an error. */
1065 yyerror (parser_control *pc ATTRIBUTE_UNUSED, char *s ATTRIBUTE_UNUSED)
1070 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1071 passing it to mktime, return true if it's OK that mktime returned T.
1072 It's not OK if *TM0 has out-of-range members. */
1075 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1077 if (t == (time_t) -1)
1079 /* Guard against falsely reporting an error when parsing a time
1080 stamp that happens to equal (time_t) -1, on a host that
1081 supports such a time stamp. */
1082 tm1 = localtime (&t);
1087 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1088 | (tm0->tm_min ^ tm1->tm_min)
1089 | (tm0->tm_hour ^ tm1->tm_hour)
1090 | (tm0->tm_mday ^ tm1->tm_mday)
1091 | (tm0->tm_mon ^ tm1->tm_mon)
1092 | (tm0->tm_year ^ tm1->tm_year));
1095 /* A reasonable upper bound for the size of ordinary TZ strings.
1096 Use heap allocation if TZ's length exceeds this. */
1097 enum { TZBUFSIZE = 100 };
1099 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1102 get_tz (char tzbuf[TZBUFSIZE])
1104 char *tz = getenv ("TZ");
1107 size_t tzsize = strlen (tz) + 1;
1108 tz = (tzsize <= TZBUFSIZE
1109 ? memcpy (tzbuf, tz, tzsize)
1110 : xmemdup (tz, tzsize));
1115 /* Parse a date/time string, storing the resulting time value into *RESULT.
1116 The string itself is pointed to by P. Return true if successful.
1117 P can be an incomplete or relative time specification; if so, use
1118 *NOW as the basis for the returned time. */
1120 get_date (struct timespec *result, char const *p, struct timespec const *now)
1124 struct tm const *tmp;
1128 struct timespec gettime_buffer;
1130 bool tz_was_altered = false;
1132 char tz0buf[TZBUFSIZE];
1137 gettime (&gettime_buffer);
1138 now = &gettime_buffer;
1141 Start = now->tv_sec;
1142 Start_ns = now->tv_nsec;
1144 tmp = localtime (&now->tv_sec);
1148 while (c = *p, ISSPACE (c))
1151 if (strncmp (p, "TZ=\"", 4) == 0)
1153 char const *tzbase = p + 4;
1157 for (s = tzbase; *s; s++, tzsize++)
1161 if (! (*s == '\\' || *s == '"'))
1168 char tz1buf[TZBUFSIZE];
1169 bool large_tz = TZBUFSIZE < tzsize;
1171 tz0 = get_tz (tz0buf);
1172 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1173 for (s = tzbase; *s != '"'; s++)
1174 *z++ = *(s += *s == '\\');
1176 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1181 tz_was_altered = true;
1187 pc.year.value = tmp->tm_year;
1188 pc.year.value += TM_YEAR_BASE;
1190 pc.month = tmp->tm_mon + 1;
1191 pc.day = tmp->tm_mday;
1192 pc.hour = tmp->tm_hour;
1193 pc.minutes = tmp->tm_min;
1194 pc.seconds.tv_sec = tmp->tm_sec;
1195 pc.seconds.tv_nsec = Start_ns;
1196 tm.tm_isdst = tmp->tm_isdst;
1198 pc.meridian = MER24;
1206 pc.timespec_seen = false;
1207 pc.rels_seen = false;
1211 pc.local_zones_seen = 0;
1215 #if HAVE_STRUCT_TM_TM_ZONE
1216 pc.local_time_zone_table[0].name = tmp->tm_zone;
1217 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1218 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1219 pc.local_time_zone_table[1].name = NULL;
1221 /* Probe the names used in the next three calendar quarters, looking
1222 for a tm_isdst different from the one we already have. */
1225 for (quarter = 1; quarter <= 3; quarter++)
1227 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1228 struct tm const *probe_tm = localtime (&probe);
1229 if (probe_tm && probe_tm->tm_zone
1230 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1233 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1234 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1235 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1236 pc.local_time_zone_table[2].name = NULL;
1246 extern char *tzname[];
1249 for (i = 0; i < 2; i++)
1251 pc.local_time_zone_table[i].name = tzname[i];
1252 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1253 pc.local_time_zone_table[i].value = i;
1255 pc.local_time_zone_table[i].name = NULL;
1258 pc.local_time_zone_table[0].name = NULL;
1262 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1263 && ! strcmp (pc.local_time_zone_table[0].name,
1264 pc.local_time_zone_table[1].name))
1266 /* This locale uses the same abbrevation for standard and
1267 daylight times. So if we see that abbreviation, we don't
1268 know whether it's daylight time. */
1269 pc.local_time_zone_table[0].value = -1;
1270 pc.local_time_zone_table[1].name = NULL;
1273 if (yyparse (&pc) != 0)
1276 if (pc.timespec_seen)
1277 *result = pc.seconds;
1280 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1281 | (pc.local_zones_seen + pc.zones_seen)))
1284 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1285 tm.tm_mon = pc.month - 1;
1286 tm.tm_mday = pc.day;
1287 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1289 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1292 tm.tm_min = pc.minutes;
1293 tm.tm_sec = pc.seconds.tv_sec;
1297 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1298 pc.seconds.tv_nsec = 0;
1301 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1305 /* But if the input explicitly specifies local time with or without
1306 DST, give mktime that information. */
1307 if (pc.local_zones_seen)
1308 tm.tm_isdst = pc.local_isdst;
1312 Start = mktime (&tm);
1314 if (! mktime_ok (&tm0, &tm, Start))
1316 if (! pc.zones_seen)
1320 /* Guard against falsely reporting errors near the time_t
1321 boundaries when parsing times in other time zones. For
1322 example, suppose the input string "1969-12-31 23:00:00 -0100",
1323 the current time zone is 8 hours ahead of UTC, and the min
1324 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1325 localtime value is 1970-01-01 08:00:00, and mktime will
1326 therefore fail on 1969-12-31 23:00:00. To work around the
1327 problem, set the time zone to 1 hour behind UTC temporarily
1328 by setting TZ="XXX1:00" and try mktime again. */
1330 long int time_zone = pc.time_zone;
1331 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1332 long int abs_time_zone_hour = abs_time_zone / 60;
1333 int abs_time_zone_min = abs_time_zone % 60;
1334 char tz1buf[sizeof "XXX+0:00"
1335 + sizeof pc.time_zone * CHAR_BIT / 3];
1336 if (!tz_was_altered)
1337 tz0 = get_tz (tz0buf);
1338 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1339 abs_time_zone_hour, abs_time_zone_min);
1340 if (setenv ("TZ", tz1buf, 1) != 0)
1342 tz_was_altered = true;
1344 Start = mktime (&tm);
1345 if (! mktime_ok (&tm0, &tm, Start))
1350 if (pc.days_seen && ! pc.dates_seen)
1352 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1353 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1355 Start = mktime (&tm);
1356 if (Start == (time_t) -1)
1362 long int delta = pc.time_zone * 60;
1364 #ifdef HAVE_TM_GMTOFF
1365 delta -= tm.tm_gmtoff;
1368 struct tm const *gmt = gmtime (&t);
1371 delta -= tm_diff (&tm, gmt);
1374 if ((Start < t1) != (delta < 0))
1375 goto fail; /* time_t overflow */
1379 /* Add relative date. */
1380 if (pc.rel_year | pc.rel_month | pc.rel_day)
1382 int year = tm.tm_year + pc.rel_year;
1383 int month = tm.tm_mon + pc.rel_month;
1384 int day = tm.tm_mday + pc.rel_day;
1385 if (((year < tm.tm_year) ^ (pc.rel_year < 0))
1386 | ((month < tm.tm_mon) ^ (pc.rel_month < 0))
1387 | ((day < tm.tm_mday) ^ (pc.rel_day < 0)))
1392 Start = mktime (&tm);
1393 if (Start == (time_t) -1)
1397 /* Add relative hours, minutes, and seconds. On hosts that support
1398 leap seconds, ignore the possibility of leap seconds; e.g.,
1399 "+ 10 minutes" adds 600 seconds, even if one of them is a
1400 leap second. Typically this is not what the user wants, but it's
1401 too hard to do it the other way, because the time zone indicator
1402 must be applied before relative times, and if mktime is applied
1403 again the time zone will be lost. */
1405 long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
1406 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1408 long int d1 = 60 * 60 * pc.rel_hour;
1409 time_t t1 = t0 + d1;
1410 long int d2 = 60 * pc.rel_minutes;
1411 time_t t2 = t1 + d2;
1412 long int d3 = pc.rel_seconds;
1413 time_t t3 = t2 + d3;
1414 long int d4 = (sum_ns - normalized_ns) / BILLION;
1415 time_t t4 = t3 + d4;
1417 if ((d1 / (60 * 60) ^ pc.rel_hour)
1418 | (d2 / 60 ^ pc.rel_minutes)
1419 | ((t1 < t0) ^ (d1 < 0))
1420 | ((t2 < t1) ^ (d2 < 0))
1421 | ((t3 < t2) ^ (d3 < 0))
1422 | ((t4 < t3) ^ (d4 < 0)))
1425 result->tv_sec = t4;
1426 result->tv_nsec = normalized_ns;
1436 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1445 main (int ac, char **av)
1449 printf ("Enter date, or blank line to exit.\n\t> ");
1452 buff[BUFSIZ - 1] = '\0';
1453 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1456 struct tm const *tm;
1457 if (! get_date (&d, buff, NULL))
1458 printf ("Bad format - couldn't convert.\n");
1459 else if (! (tm = localtime (&d.tv_sec)))
1461 long int sec = d.tv_sec;
1462 printf ("localtime (%ld) failed\n", sec);
1467 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1468 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1469 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);