2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999-2000, 2002-2016 Free Software Foundation, Inc.
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
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
35 #include "parse-datetime.h"
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 parse-datetime.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. */
73 #define _(str) gettext (str)
75 /* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
76 use _STDLIB_H_ as witness. Map the latter to the one bison uses. */
77 /* FIXME: this is temporary. Remove when we have a mechanism to ensure
78 that the version we're using is fixed, too. */
84 /* ISDIGIT differs from isdigit, as follows:
85 - Its arg may be any int or unsigned int; it need not be an unsigned char
87 - It's typically faster.
88 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
89 isdigit unless it's important to use the locale's definition
90 of "digit" even when the host does not conform to POSIX. */
91 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
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 #define STREQ(a, b) (strcmp (a, b) == 0)
115 /* long_time_t is a signed integer type that contains all time_t values. */
116 verify (TYPE_IS_INTEGER (time_t));
117 #if TIME_T_FITS_IN_LONG_INT
118 typedef long int long_time_t;
120 typedef time_t long_time_t;
123 /* Convert a possibly-signed character to an unsigned character. This is
124 a bit safer than casting to unsigned char, since it catches some type
125 errors that the cast doesn't. */
126 static unsigned char to_uchar (char ch) { return ch; }
129 dbg_printf (const char *msg,...)
132 /* TODO: use gnulib's 'program_name' instead? */
133 fputs ("date: ", stderr);
135 va_start (args, msg);
136 vfprintf (stderr, msg, args);
142 /* Lots of this code assumes time_t and time_t-like values fit into
144 verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t)
145 && TYPE_MAXIMUM (time_t) <= TYPE_MAXIMUM (long_time_t));
147 /* FIXME: It also assumes that signed integer overflow silently wraps around,
148 but this is not true any more with recent versions of GCC 4. */
150 /* An integer value, and the number of digits in its textual
159 /* An entry in the lexical lookup table. */
167 /* Meridian: am, pm, or 24-hour style. */
168 enum { MERam, MERpm, MER24 };
170 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
172 /* Relative times. */
175 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
185 #if HAVE_COMPOUND_LITERALS
186 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
188 static relative_time const RELATIVE_TIME_0;
191 /* Information passed to and from the parser. */
194 /* The input string remaining to be parsed. */
197 /* N, if this is the Nth Tuesday. */
198 long int day_ordinal;
200 /* Day of week; Sunday is 0. */
203 /* tm_isdst flag for the local zone. */
206 /* Time zone, in minutes east of UTC. */
209 /* Style used for time. */
212 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
218 struct timespec seconds; /* includes nanoseconds */
220 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
223 /* Presence or counts of nonterminals of various flavors parsed so far. */
228 size_t local_zones_seen;
233 /* if true, print debugging output to stderr */
234 bool parse_datetime_debug;
236 /* which of the 'seen' parts has been printed when debugging */
237 size_t debug_dates_seen;
238 size_t debug_days_seen;
239 size_t debug_local_zones_seen;
240 size_t debug_dsts_seen;
241 size_t debug_times_seen;
242 size_t debug_zones_seen;
244 /* true if the user specified explicit ordinal day value, */
245 bool debug_ordinal_day_seen;
247 /* the default input timezone, set by TZ value */
248 long int debug_default_input_timezone;
250 /* Table of local time zone abbreviations, terminated by a null entry. */
251 table local_time_zone_table[3];
255 static int yylex (union YYSTYPE *, parser_control *);
256 static int yyerror (parser_control const *, char const *);
257 static long int time_zone_hhmm (parser_control *, textint, long int);
259 /* Extract into *PC any date and time info from a string of digits
260 of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
263 digits_to_date_time (parser_control *pc, textint text_int)
265 if (pc->dates_seen && ! pc->year.digits
266 && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
270 if (4 < text_int.digits)
273 pc->day = text_int.value % 100;
274 pc->month = (text_int.value / 100) % 100;
275 pc->year.value = text_int.value / 10000;
276 pc->year.digits = text_int.digits - 4;
281 if (text_int.digits <= 2)
283 pc->hour = text_int.value;
288 pc->hour = text_int.value / 100;
289 pc->minutes = text_int.value % 100;
291 pc->seconds.tv_sec = 0;
292 pc->seconds.tv_nsec = 0;
293 pc->meridian = MER24;
298 /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). */
300 apply_relative_time (parser_control *pc, relative_time rel, int factor)
302 pc->rel.ns += factor * rel.ns;
303 pc->rel.seconds += factor * rel.seconds;
304 pc->rel.minutes += factor * rel.minutes;
305 pc->rel.hour += factor * rel.hour;
306 pc->rel.day += factor * rel.day;
307 pc->rel.month += factor * rel.month;
308 pc->rel.year += factor * rel.year;
309 pc->rels_seen = true;
312 /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */
314 set_hhmmss (parser_control *pc, long int hour, long int minutes,
315 time_t sec, long int nsec)
318 pc->minutes = minutes;
319 pc->seconds.tv_sec = sec;
320 pc->seconds.tv_nsec = nsec;
323 /* returns a textual representation of the day ordinal/number values
324 in the parser_control struct (e.g. 'last wed', 'this tues', 'thu') */
326 str_days (parser_control *pc, char* /*output*/ buffer, size_t n)
328 /* TODO: use the relative_time_table[] for reverse lookup */
329 static const char* ordinal_values[] = {
333 "(SECOND)", /* SECOND is commented out in relative_time_table[] */
345 static const char* days_values[] = {
355 /* don't add an ordinal prefix if the user didn't specify it
356 (e.g., "this wed" vs "wed") */
357 if (pc->debug_ordinal_day_seen)
359 /* use word description of possible (e.g. -1 = last, 3 = third) */
360 if (pc->day_ordinal>=-1 && pc->day_ordinal <=12)
362 strncpy (buffer, ordinal_values[ pc->day_ordinal+1 ], n);
367 snprintf (buffer,n,"%ld",pc->day_ordinal);
375 /* Add the day name */
376 if (pc->day_number>=0 && pc->day_number<=6)
378 size_t l = strlen (buffer);
381 strncat (buffer," ",n-l);
384 strncat (buffer,days_values[pc->day_number],n-l);
388 /* invalid day_number value - should never happen */
393 /* debugging: print the current time in the parser_control structure.
394 The parser will increment "*_seen" members for those which were parsed.
395 This function will print only newly seen parts. */
397 debug_print_current_time (const char* item, parser_control *pc)
400 int space = 0; /* if true, add space delimiter */
402 if (!pc->parse_datetime_debug)
405 /* no newline, more items printed below */
406 dbg_printf (_("parsed %s part: "), item);
408 if (pc->dates_seen != pc->debug_dates_seen)
410 /*TODO: use pc->year.negative? */
411 fprintf (stderr,"(Y-M-D) %04ld-%02ld-%02ld",
412 pc->year.value, pc->month, pc->day);
413 pc->debug_dates_seen = pc->dates_seen;
417 if (pc->times_seen != pc->debug_times_seen)
421 fprintf (stderr,"%02ld:%02ld:%02ld",
422 pc->hour, pc->minutes, pc->seconds.tv_sec);
423 if (pc->seconds.tv_nsec!=0)
424 fprintf (stderr,"%09ld", pc->seconds.tv_nsec);
425 if (pc->meridian==MERpm)
428 pc->debug_times_seen = pc->times_seen;
432 if (pc->days_seen != pc->debug_days_seen)
436 fprintf (stderr,_("%s (day ordinal=%ld number=%d)"),
437 str_days (pc,tmp,sizeof (tmp)),
438 pc->day_ordinal, pc->day_number);
439 pc->debug_days_seen = pc->days_seen ;
443 if (pc->dsts_seen != pc->debug_dsts_seen)
447 fprintf (stderr,_("is-dst=%d"), pc->local_isdst);
448 pc->dsts_seen = pc->debug_dsts_seen;
452 /* TODO: fix incorrect display of EST=2:08h? */
453 if (pc->zones_seen != pc->debug_zones_seen)
457 fprintf (stderr,_("TZ=%+03d:%02d"), (int)(pc->time_zone/60),
458 abs ((int)pc->time_zone%60));
459 pc->debug_zones_seen = pc->zones_seen;
463 if (pc->local_zones_seen != pc->debug_local_zones_seen)
467 fprintf (stderr,_("Local-TZ=%+03d:%02d"), (int)(pc->time_zone/60),
468 abs ((int)pc->time_zone%60));
469 pc->debug_local_zones_seen = pc->local_zones_seen;
473 if (pc->timespec_seen)
477 fprintf (stderr,_("number of seconds: %ld"), pc->seconds.tv_sec);
480 fputc ('\n', stderr);
483 /* debugging: print the current relative values. */
485 debug_print_relative_time (const char* item, const parser_control *pc)
487 int space = 0; /* if true, add space delimiter */
489 if (!pc->parse_datetime_debug)
492 /* no newline, more items printed below */
493 dbg_printf (_("parsed %s part: "), item);
495 if (pc->rel.year==0 && pc->rel.month==0 && pc->rel.day==0
496 && pc->rel.hour==0 && pc->rel.minutes==00 && pc->rel.seconds == 0
499 /* Special case: relative time of this/today/now */
500 fputs (_("today/this/now\n"),stderr);
504 #define PRINT_REL_PART(x,name) \
506 if ( (pc->rel.x) != 0 ) \
509 fputc (' ',stderr); \
510 fprintf (stderr,"%+ld %s", pc->rel.x, name); \
515 PRINT_REL_PART (year,"year(s)");
516 PRINT_REL_PART (month,"month(s)");
517 PRINT_REL_PART (day,"day(s)");
518 PRINT_REL_PART (hour,"hour(s)");
519 PRINT_REL_PART (minutes,"minutes");
520 PRINT_REL_PART (seconds,"seconds");
521 PRINT_REL_PART (ns,"nanoseconds");
530 /* We want a reentrant parser, even if the TZ manipulation and the calls to
531 localtime and gmtime are not reentrant. */
533 %parse-param { parser_control *pc }
534 %lex-param { parser_control *pc }
536 /* This grammar has 31 shift/reduce conflicts. */
543 struct timespec timespec;
550 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
551 %token <intval> tDAY_UNIT tDAY_SHIFT
553 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
554 %token <intval> tMONTH tORDINAL tZONE
556 %token <textintval> tSNUMBER tUNUMBER
557 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
559 %type <intval> o_colon_minutes
560 %type <timespec> seconds signed_seconds unsigned_seconds
562 %type <rel> relunit relunit_snumber dayshift
575 pc->timespec_seen = true;
576 debug_print_current_time (_("number of seconds"), pc);
588 pc->times_seen++; pc->dates_seen++;
589 debug_print_current_time (_("datetime"), pc);
594 debug_print_current_time (_("time"), pc);
598 pc->local_zones_seen++;
599 debug_print_current_time (_("local_zone"), pc);
604 debug_print_current_time (_("zone"), pc);
609 debug_print_current_time (_("date"), pc);
614 debug_print_current_time (_("day"), pc);
618 debug_print_relative_time (_("relative"), pc);
622 debug_print_relative_time (_("number"), pc);
626 debug_print_relative_time (_("hybrid"), pc);
635 iso_8601_date 'T' iso_8601_time
641 set_hhmmss (pc, $1.value, 0, 0, 0);
644 | tUNUMBER ':' tUNUMBER tMERIDIAN
646 set_hhmmss (pc, $1.value, $3.value, 0, 0);
649 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN
651 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
660 set_hhmmss (pc, $1.value, 0, 0, 0);
661 pc->meridian = MER24;
663 | tUNUMBER ':' tUNUMBER o_zone_offset
665 set_hhmmss (pc, $1.value, $3.value, 0, 0);
666 pc->meridian = MER24;
668 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_zone_offset
670 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
671 pc->meridian = MER24;
681 tSNUMBER o_colon_minutes
684 pc->time_zone = time_zone_hhmm (pc, $1, $2);
691 pc->local_isdst = $1;
692 pc->dsts_seen += (0 < $1);
697 pc->dsts_seen += (0 < $1) + 1;
701 /* Note 'T' is a special case, as it is used as the separator in ISO
702 8601 date and time of day representation. */
705 { pc->time_zone = $1; }
707 { pc->time_zone = HOUR(7); }
708 | tZONE relunit_snumber
709 { pc->time_zone = $1;
710 apply_relative_time (pc, $2, 1); }
711 | 'T' relunit_snumber
712 { pc->time_zone = HOUR(7);
713 apply_relative_time (pc, $2, 1); }
714 | tZONE tSNUMBER o_colon_minutes
715 { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
717 { pc->time_zone = $1 + 60; }
719 { pc->time_zone = $1 + 60; }
735 pc->day_ordinal = $1;
737 pc->debug_ordinal_day_seen = true;
741 pc->day_ordinal = $1.value;
743 pc->debug_ordinal_day_seen = true;
748 tUNUMBER '/' tUNUMBER
750 pc->month = $1.value;
753 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
755 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
756 otherwise as MM/DD/YY.
757 The goal in recognizing YYYY/MM/DD is solely to support legacy
758 machine-generated dates like those in an RCS log listing. If
759 you want portability, use the ISO 8601 format. */
762 if (pc->parse_datetime_debug)
763 dbg_printf (_("warning: value %ld has %"PRIuMAX" digits. " \
764 "Assuming YYYY/MM/DD\n"), $1.value, $1.digits);
767 pc->month = $3.value;
772 if (pc->parse_datetime_debug)
773 dbg_printf (_("warning: value %ld has less than 4 digits. " \
774 "Assuming MM/DD/YY[YY]\n"), $1.value);
776 pc->month = $1.value;
781 | tUNUMBER tMONTH tSNUMBER
783 /* e.g. 17-JUN-1992. */
786 pc->year.value = -$3.value;
787 pc->year.digits = $3.digits;
789 | tMONTH tSNUMBER tSNUMBER
791 /* e.g. JUN-17-1992. */
794 pc->year.value = -$3.value;
795 pc->year.digits = $3.digits;
802 | tMONTH tUNUMBER ',' tUNUMBER
813 | tUNUMBER tMONTH tUNUMBER
823 tUNUMBER tSNUMBER tSNUMBER
825 /* ISO 8601 format. YYYY-MM-DD. */
827 pc->month = -$2.value;
834 { apply_relative_time (pc, $1, $2); }
836 { apply_relative_time (pc, $1, 1); }
838 { apply_relative_time (pc, $1, 1); }
843 { $$ = RELATIVE_TIME_0; $$.year = $1; }
844 | tUNUMBER tYEAR_UNIT
845 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
847 { $$ = RELATIVE_TIME_0; $$.year = 1; }
848 | tORDINAL tMONTH_UNIT
849 { $$ = RELATIVE_TIME_0; $$.month = $1; }
850 | tUNUMBER tMONTH_UNIT
851 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
853 { $$ = RELATIVE_TIME_0; $$.month = 1; }
855 { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
857 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
859 { $$ = RELATIVE_TIME_0; $$.day = $1; }
860 | tORDINAL tHOUR_UNIT
861 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
862 | tUNUMBER tHOUR_UNIT
863 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
865 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
866 | tORDINAL tMINUTE_UNIT
867 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
868 | tUNUMBER tMINUTE_UNIT
869 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
871 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
873 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
875 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
876 | tSDECIMAL_NUMBER tSEC_UNIT
877 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
878 | tUDECIMAL_NUMBER tSEC_UNIT
879 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
881 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
887 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
888 | tSNUMBER tMONTH_UNIT
889 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
891 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
892 | tSNUMBER tHOUR_UNIT
893 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
894 | tSNUMBER tMINUTE_UNIT
895 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
897 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
902 { $$ = RELATIVE_TIME_0; $$.day = $1; }
905 seconds: signed_seconds | unsigned_seconds;
910 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
916 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
921 { digits_to_date_time (pc, $1); }
925 tUNUMBER relunit_snumber
927 /* Hybrid all-digit and relative offset, so that we accept e.g.,
928 "YYYYMMDD +N days" as well as "YYYYMMDD N days". */
929 digits_to_date_time (pc, $1);
930 apply_relative_time (pc, $2, 1);
943 static table const meridian_table[] =
945 { "AM", tMERIDIAN, MERam },
946 { "A.M.", tMERIDIAN, MERam },
947 { "PM", tMERIDIAN, MERpm },
948 { "P.M.", tMERIDIAN, MERpm },
952 static table const dst_table[] =
957 static table const month_and_day_table[] =
959 { "JANUARY", tMONTH, 1 },
960 { "FEBRUARY", tMONTH, 2 },
961 { "MARCH", tMONTH, 3 },
962 { "APRIL", tMONTH, 4 },
963 { "MAY", tMONTH, 5 },
964 { "JUNE", tMONTH, 6 },
965 { "JULY", tMONTH, 7 },
966 { "AUGUST", tMONTH, 8 },
967 { "SEPTEMBER",tMONTH, 9 },
968 { "SEPT", tMONTH, 9 },
969 { "OCTOBER", tMONTH, 10 },
970 { "NOVEMBER", tMONTH, 11 },
971 { "DECEMBER", tMONTH, 12 },
972 { "SUNDAY", tDAY, 0 },
973 { "MONDAY", tDAY, 1 },
974 { "TUESDAY", tDAY, 2 },
976 { "WEDNESDAY",tDAY, 3 },
977 { "WEDNES", tDAY, 3 },
978 { "THURSDAY", tDAY, 4 },
980 { "THURS", tDAY, 4 },
981 { "FRIDAY", tDAY, 5 },
982 { "SATURDAY", tDAY, 6 },
986 static table const time_units_table[] =
988 { "YEAR", tYEAR_UNIT, 1 },
989 { "MONTH", tMONTH_UNIT, 1 },
990 { "FORTNIGHT",tDAY_UNIT, 14 },
991 { "WEEK", tDAY_UNIT, 7 },
992 { "DAY", tDAY_UNIT, 1 },
993 { "HOUR", tHOUR_UNIT, 1 },
994 { "MINUTE", tMINUTE_UNIT, 1 },
995 { "MIN", tMINUTE_UNIT, 1 },
996 { "SECOND", tSEC_UNIT, 1 },
997 { "SEC", tSEC_UNIT, 1 },
1001 /* Assorted relative-time words. */
1002 static table const relative_time_table[] =
1004 { "TOMORROW", tDAY_SHIFT, 1 },
1005 { "YESTERDAY",tDAY_SHIFT, -1 },
1006 { "TODAY", tDAY_SHIFT, 0 },
1007 { "NOW", tDAY_SHIFT, 0 },
1008 { "LAST", tORDINAL, -1 },
1009 { "THIS", tORDINAL, 0 },
1010 { "NEXT", tORDINAL, 1 },
1011 { "FIRST", tORDINAL, 1 },
1012 /*{ "SECOND", tORDINAL, 2 }, */
1013 { "THIRD", tORDINAL, 3 },
1014 { "FOURTH", tORDINAL, 4 },
1015 { "FIFTH", tORDINAL, 5 },
1016 { "SIXTH", tORDINAL, 6 },
1017 { "SEVENTH", tORDINAL, 7 },
1018 { "EIGHTH", tORDINAL, 8 },
1019 { "NINTH", tORDINAL, 9 },
1020 { "TENTH", tORDINAL, 10 },
1021 { "ELEVENTH", tORDINAL, 11 },
1022 { "TWELFTH", tORDINAL, 12 },
1023 { "AGO", tAGO, -1 },
1024 { "HENCE", tAGO, 1 },
1028 /* The universal time zone table. These labels can be used even for
1029 time stamps that would not otherwise be valid, e.g., GMT time
1030 stamps in London during summer. */
1031 static table const universal_time_zone_table[] =
1033 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
1034 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
1035 { "UTC", tZONE, HOUR ( 0) },
1039 /* The time zone table. This table is necessarily incomplete, as time
1040 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
1041 as Eastern time in Australia, not as US Eastern Standard Time.
1042 You cannot rely on parse_datetime to handle arbitrary time zone
1043 abbreviations; use numeric abbreviations like "-0500" instead. */
1044 static table const time_zone_table[] =
1046 { "WET", tZONE, HOUR ( 0) }, /* Western European */
1047 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
1048 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
1049 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
1050 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
1051 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
1052 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
1053 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
1054 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
1055 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
1056 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
1057 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
1058 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
1059 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
1060 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
1061 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
1062 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
1063 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
1064 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
1065 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
1066 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
1067 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
1068 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
1069 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
1070 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
1071 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
1072 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
1073 { "CET", tZONE, HOUR ( 1) }, /* Central European */
1074 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
1075 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
1076 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
1077 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
1078 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
1079 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
1080 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
1081 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
1082 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
1083 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
1084 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
1085 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
1086 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
1087 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
1088 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
1089 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
1090 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
1091 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
1092 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
1096 /* Military time zone table.
1098 Note 'T' is a special case, as it is used as the separator in ISO
1099 8601 date and time of day representation. */
1100 static table const military_table[] =
1102 { "A", tZONE, -HOUR ( 1) },
1103 { "B", tZONE, -HOUR ( 2) },
1104 { "C", tZONE, -HOUR ( 3) },
1105 { "D", tZONE, -HOUR ( 4) },
1106 { "E", tZONE, -HOUR ( 5) },
1107 { "F", tZONE, -HOUR ( 6) },
1108 { "G", tZONE, -HOUR ( 7) },
1109 { "H", tZONE, -HOUR ( 8) },
1110 { "I", tZONE, -HOUR ( 9) },
1111 { "K", tZONE, -HOUR (10) },
1112 { "L", tZONE, -HOUR (11) },
1113 { "M", tZONE, -HOUR (12) },
1114 { "N", tZONE, HOUR ( 1) },
1115 { "O", tZONE, HOUR ( 2) },
1116 { "P", tZONE, HOUR ( 3) },
1117 { "Q", tZONE, HOUR ( 4) },
1118 { "R", tZONE, HOUR ( 5) },
1119 { "S", tZONE, HOUR ( 6) },
1121 { "U", tZONE, HOUR ( 8) },
1122 { "V", tZONE, HOUR ( 9) },
1123 { "W", tZONE, HOUR (10) },
1124 { "X", tZONE, HOUR (11) },
1125 { "Y", tZONE, HOUR (12) },
1126 { "Z", tZONE, HOUR ( 0) },
1132 /* Convert a time zone expressed as HH:MM into an integer count of
1133 minutes. If MM is negative, then S is of the form HHMM and needs
1134 to be picked apart; otherwise, S is of the form HH. As specified in
1135 http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
1136 only valid TZ range, and consider first two digits as hours, if no
1137 minutes specified. */
1140 time_zone_hhmm (parser_control *pc, textint s, long int mm)
1144 /* If the length of S is 1 or 2 and no minutes are specified,
1145 interpret it as a number of hours. */
1146 if (s.digits <= 2 && mm < 0)
1150 n_minutes = (s.value / 100) * 60 + s.value % 100;
1152 n_minutes = s.value * 60 + (s.negative ? -mm : mm);
1154 /* If the absolute number of minutes is larger than 24 hours,
1155 arrange to reject it by incrementing pc->zones_seen. Thus,
1156 we allow only values in the range UTC-24:00 to UTC+24:00. */
1157 if (24 * 60 < abs (n_minutes))
1164 to_hour (long int hours, int meridian)
1168 default: /* Pacify GCC. */
1170 return 0 <= hours && hours < 24 ? hours : -1;
1172 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
1174 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
1179 to_year (textint textyear, bool debug)
1181 long int year = textyear.value;
1186 /* XPG4 suggests that years 00-68 map to 2000-2068, and
1187 years 69-99 map to 1969-1999. */
1188 else if (textyear.digits == 2)
1190 year += year < 69 ? 2000 : 1900;
1192 dbg_printf (_("warning: adjusting year value %ld to %ld\n"),
1193 textyear.value, year);
1199 static table const * _GL_ATTRIBUTE_PURE
1200 lookup_zone (parser_control const *pc, char const *name)
1204 for (tp = universal_time_zone_table; tp->name; tp++)
1205 if (strcmp (name, tp->name) == 0)
1208 /* Try local zone abbreviations before those in time_zone_table, as
1209 the local ones are more likely to be right. */
1210 for (tp = pc->local_time_zone_table; tp->name; tp++)
1211 if (strcmp (name, tp->name) == 0)
1214 for (tp = time_zone_table; tp->name; tp++)
1215 if (strcmp (name, tp->name) == 0)
1221 #if ! HAVE_TM_GMTOFF
1222 /* Yield the difference between *A and *B,
1223 measured in seconds, ignoring leap seconds.
1224 The body of this function is taken directly from the GNU C Library;
1225 see src/strftime.c. */
1227 tm_diff (struct tm const *a, struct tm const *b)
1229 /* Compute intervening leap days correctly even if year is negative.
1230 Take care to avoid int overflow in leap day calculations. */
1231 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
1232 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
1233 int a100 = a4 / 25 - (a4 % 25 < 0);
1234 int b100 = b4 / 25 - (b4 % 25 < 0);
1235 int a400 = SHR (a100, 2);
1236 int b400 = SHR (b100, 2);
1237 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
1238 long int ayear = a->tm_year;
1239 long int years = ayear - b->tm_year;
1240 long int days = (365 * years + intervening_leap_days
1241 + (a->tm_yday - b->tm_yday));
1242 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
1243 + (a->tm_min - b->tm_min))
1244 + (a->tm_sec - b->tm_sec));
1246 #endif /* ! HAVE_TM_GMTOFF */
1248 static table const *
1249 lookup_word (parser_control const *pc, char *word)
1258 /* Make it uppercase. */
1259 for (p = word; *p; p++)
1261 unsigned char ch = *p;
1262 *p = c_toupper (ch);
1265 for (tp = meridian_table; tp->name; tp++)
1266 if (strcmp (word, tp->name) == 0)
1269 /* See if we have an abbreviation for a month. */
1270 wordlen = strlen (word);
1271 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
1273 for (tp = month_and_day_table; tp->name; tp++)
1274 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
1277 if ((tp = lookup_zone (pc, word)))
1280 if (strcmp (word, dst_table[0].name) == 0)
1283 for (tp = time_units_table; tp->name; tp++)
1284 if (strcmp (word, tp->name) == 0)
1287 /* Strip off any plural and try the units table again. */
1288 if (word[wordlen - 1] == 'S')
1290 word[wordlen - 1] = '\0';
1291 for (tp = time_units_table; tp->name; tp++)
1292 if (strcmp (word, tp->name) == 0)
1294 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
1297 for (tp = relative_time_table; tp->name; tp++)
1298 if (strcmp (word, tp->name) == 0)
1301 /* Military time zones. */
1303 for (tp = military_table; tp->name; tp++)
1304 if (word[0] == tp->name[0])
1307 /* Drop out any periods and try the time zone table again. */
1308 for (period_found = false, p = q = word; (*p = *q); q++)
1310 period_found = true;
1313 if (period_found && (tp = lookup_zone (pc, word)))
1320 yylex (union YYSTYPE *lvalp, parser_control *pc)
1327 while (c = *pc->input, c_isspace (c))
1330 if (ISDIGIT (c) || c == '-' || c == '+')
1334 unsigned long int value;
1335 if (c == '-' || c == '+')
1337 sign = c == '-' ? -1 : 1;
1338 while (c = *++pc->input, c_isspace (c))
1341 /* skip the '-' sign */
1347 for (value = 0; ; value *= 10)
1349 unsigned long int value1 = value + (c - '0');
1356 if (ULONG_MAX / 10 < value)
1359 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1364 unsigned long int value1;
1366 /* Check for overflow when converting value to time_t. */
1381 if (value != value1)
1384 /* Accumulate fraction, to ns precision. */
1387 for (digits = 2; digits <= LOG10_BILLION; digits++)
1394 /* Skip excess digits, truncating toward -Infinity. */
1396 for (; ISDIGIT (*p); p++)
1402 while (ISDIGIT (*p))
1405 /* Adjust to the timespec convention, which is that
1406 tv_nsec is always a positive offset even if tv_sec is
1416 lvalp->timespec.tv_sec = s;
1417 lvalp->timespec.tv_nsec = ns;
1419 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1423 lvalp->textintval.negative = sign < 0;
1426 lvalp->textintval.value = - value;
1427 if (0 < lvalp->textintval.value)
1432 lvalp->textintval.value = value;
1433 if (lvalp->textintval.value < 0)
1436 lvalp->textintval.digits = p - pc->input;
1438 return sign ? tSNUMBER : tUNUMBER;
1450 if (p < buff + sizeof buff - 1)
1454 while (c_isalpha (c) || c == '.');
1457 tp = lookup_word (pc, buff);
1460 if (pc->parse_datetime_debug)
1461 dbg_printf (_("error: unknown word '%s'\n"), buff);
1464 lvalp->intval = tp->value;
1469 return to_uchar (*pc->input++);
1486 /* Do nothing if the parser reports an error. */
1488 yyerror (parser_control const *pc _GL_UNUSED,
1489 char const *s _GL_UNUSED)
1494 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1495 passing it to mktime, return true if it's OK that mktime returned T.
1496 It's not OK if *TM0 has out-of-range members. */
1499 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1501 if (t == (time_t) -1)
1503 /* Guard against falsely reporting an error when parsing a time
1504 stamp that happens to equal (time_t) -1, on a host that
1505 supports such a time stamp. */
1506 tm1 = localtime (&t);
1511 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1512 | (tm0->tm_min ^ tm1->tm_min)
1513 | (tm0->tm_hour ^ tm1->tm_hour)
1514 | (tm0->tm_mday ^ tm1->tm_mday)
1515 | (tm0->tm_mon ^ tm1->tm_mon)
1516 | (tm0->tm_year ^ tm1->tm_year));
1519 /* A reasonable upper bound for the size of ordinary TZ strings.
1520 Use heap allocation if TZ's length exceeds this. */
1521 enum { TZBUFSIZE = 100 };
1523 /* A reasonable upper bound for the buffer used in debug print outs.
1524 see days_to_name(), debug_strftime() and debug_mktime_not_ok() */
1525 enum { DBGBUFSIZE = 100 };
1527 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1530 get_tz (char tzbuf[TZBUFSIZE])
1532 char *tz = getenv ("TZ");
1535 size_t tzsize = strlen (tz) + 1;
1536 tz = (tzsize <= TZBUFSIZE
1537 ? memcpy (tzbuf, tz, tzsize)
1538 : xmemdup (tz, tzsize));
1543 /* debugging: format a 'struct tm' into a buffer, taking the parser's
1544 timezone information into account (if pc!=NULL). */
1546 debug_strfdatetime (const struct tm *tm, const parser_control *pc,
1547 char* /*output*/ buf, size_t n)
1550 1. find an optimal way to print date string in a clear and unambiguous
1551 format. Currently, always add '(Y-M-D)' prefix.
1552 Consider '2016y01m10d' or 'year(2016) month(01) day(10)'.
1554 If the user needs debug printing, it means he/she already having
1555 issues with the parsing - better to avoid formats that could
1556 be mis-interpreted (e.g. just YYYY-MM-DD).
1558 2. Can strftime be used instead?
1559 depends if it is portable and can print invalid dates on all systems.
1561 3. Print timezone information ?
1563 4. Print DST information ?
1565 5. Print nanosecond information ?
1568 Printed date/time values might not be valid, e.g. '2016-02-31'
1569 or '2016-19-2016' . These are the values as parsed from the user
1570 string, before validation.
1572 int m = nstrftime (buf, n, "(Y-M-D) %Y-%m-%d %H:%M:%S", tm, 0, 0);
1574 /* if parser_control information was provided (for timezone),
1575 and there's enough space in the buffer - add timezone info */
1576 if (pc != NULL && ((n-m)>0))
1578 const long int tz = (pc->zones_seen || pc->local_zones_seen)
1580 : pc->debug_default_input_timezone;
1581 snprintf (&buf[m],n-m," TZ=%+03d:%02d", (int)(tz/60), abs ((int)tz)%60);
1587 debug_strfdate (const struct tm *tm, char* /*output*/ buf, size_t n)
1589 snprintf (buf,n,"(Y-M-D) %04d-%02d-%02d",
1590 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
1595 debug_strftime (const struct tm *tm, char* /*output*/ buf, size_t n)
1597 snprintf (buf,n,"%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
1601 /* If 'mktime_ok()' failed, display the failed time values,
1602 and provide possible hints. Example output:
1604 date: error: invalid date/time value:
1605 date: user provided time: '(Y-M-D) 2006-04-02 02:45:00'
1606 date: normalized time: '(Y-M-D) 2006-04-02 03:45:00'
1608 date: possible reasons:
1609 date: non-existing due to daylight-saving time;
1610 date: numeric values overflow;
1611 date: missing timezone;
1614 debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1,
1615 const parser_control *pc, bool time_zone_seen)
1617 /* TODO: handle t==-1 (as in 'mktime_ok') */
1618 char tmp[DBGBUFSIZE];
1620 const bool eq_sec = (tm0->tm_sec == tm1->tm_sec);
1621 const bool eq_min = (tm0->tm_min == tm1->tm_min);
1622 const bool eq_hour = (tm0->tm_hour == tm1->tm_hour);
1623 const bool eq_mday = (tm0->tm_mday == tm1->tm_mday);
1624 const bool eq_month = (tm0->tm_mon == tm1->tm_mon);
1625 const bool eq_year = (tm0->tm_year == tm1->tm_year);
1627 const bool dst_shift = eq_sec && eq_min && !eq_hour
1628 && eq_mday && eq_month && eq_year;
1630 if (!pc->parse_datetime_debug)
1633 dbg_printf (_("error: invalid date/time value:\n"));
1634 dbg_printf (_(" user provided time: '%s'\n"),
1635 debug_strfdatetime (tm0, pc, tmp, sizeof (tmp)));
1636 dbg_printf (_(" normalized time: '%s'\n"),
1637 debug_strfdatetime (tm1, pc, tmp, sizeof (tmp)));
1638 /* NOTEs: the format must be aligned with debug_strfdatetime() and the two
1639 DEBUG statements above. this string is not translated. */
1640 i = snprintf (tmp, sizeof(tmp),
1641 " %4s %2s %2s %2s %2s %2s",
1642 eq_year?"":"----", eq_month?"":"--", eq_mday?"":"--",
1643 eq_hour?"":"--", eq_min?"":"--", eq_sec?"":"--");
1644 /* Trim trailing whitespace */
1645 if ((i>0) && (i<sizeof(tmp)))
1647 while ((i>0) && (tmp[i-1]==' '))
1651 dbg_printf ("%s\n", tmp);
1653 dbg_printf (_(" possible reasons:\n"));
1655 dbg_printf (_(" non-existing due to daylight-saving time;\n"));
1656 if (!eq_mday && !eq_month)
1657 dbg_printf (_(" invalid day/month combination;\n"));
1658 dbg_printf (_(" numeric values overflow;\n"));
1659 dbg_printf (" %s\n",time_zone_seen?_("incorrect timezone")
1660 :_("missing timezone"));
1664 /* Returns the effective local timezone, in minutes. */
1666 get_effective_timezone (void)
1668 /* TODO: check for failures */
1672 ltm = localtime (&z);
1673 lz = timegm (ltm)/60;
1674 return (long int)lz;
1677 /* The original interface: run with debug=false */
1679 parse_datetime (struct timespec *result, char const *p,
1680 struct timespec const *now)
1682 return parse_datetime2 (result, p, now, 0);
1685 /* Parse a date/time string, storing the resulting time value into *RESULT.
1686 The string itself is pointed to by P. Return true if successful.
1687 P can be an incomplete or relative time specification; if so, use
1688 *NOW as the basis for the returned time. */
1690 parse_datetime2 (struct timespec *result, char const *p,
1691 struct timespec const *now, unsigned int flags)
1695 struct tm const *tmp;
1699 struct timespec gettime_buffer;
1701 bool tz_was_altered = false;
1703 char tz0buf[TZBUFSIZE];
1705 char dbg_ord[DBGBUFSIZE];
1706 char dbg_tm[DBGBUFSIZE];
1707 char const *input_sentinel = p + strlen (p);
1711 gettime (&gettime_buffer);
1712 now = &gettime_buffer;
1715 Start = now->tv_sec;
1716 Start_ns = now->tv_nsec;
1718 tmp = localtime (&now->tv_sec);
1722 while (c = *p, c_isspace (c))
1725 if (strncmp (p, "TZ=\"", 4) == 0)
1727 char const *tzbase = p + 4;
1731 for (s = tzbase; *s; s++, tzsize++)
1735 if (! (*s == '\\' || *s == '"'))
1742 char tz1buf[TZBUFSIZE];
1743 bool large_tz = TZBUFSIZE < tzsize;
1745 tz0 = get_tz (tz0buf);
1746 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1747 for (s = tzbase; *s != '"'; s++)
1748 *z++ = *(s += *s == '\\');
1750 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1755 tz_was_altered = true;
1758 while (c = *p, c_isspace (c))
1765 /* As documented, be careful to treat the empty string just like
1766 a date string of "0". Without this, an empty string would be
1767 declared invalid when parsed during a DST transition. */
1772 pc.year.value = tmp->tm_year;
1773 pc.year.value += TM_YEAR_BASE;
1775 pc.month = tmp->tm_mon + 1;
1776 pc.day = tmp->tm_mday;
1777 pc.hour = tmp->tm_hour;
1778 pc.minutes = tmp->tm_min;
1779 pc.seconds.tv_sec = tmp->tm_sec;
1780 pc.seconds.tv_nsec = Start_ns;
1781 tm.tm_isdst = tmp->tm_isdst;
1783 pc.meridian = MER24;
1784 pc.rel = RELATIVE_TIME_0;
1785 pc.timespec_seen = false;
1786 pc.rels_seen = false;
1790 pc.local_zones_seen = 0;
1793 pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG)!=0;
1794 pc.debug_dates_seen = 0;
1795 pc.debug_days_seen = 0;
1796 pc.debug_times_seen = 0;
1797 pc.debug_local_zones_seen = 0;
1798 pc.debug_dsts_seen = 0;
1799 pc.debug_zones_seen = 0;
1800 pc.debug_ordinal_day_seen = false;
1801 pc.debug_default_input_timezone = 0;
1803 #if HAVE_STRUCT_TM_TM_ZONE
1804 pc.local_time_zone_table[0].name = tmp->tm_zone;
1805 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1806 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1807 pc.local_time_zone_table[1].name = NULL;
1809 /* Probe the names used in the next three calendar quarters, looking
1810 for a tm_isdst different from the one we already have. */
1813 for (quarter = 1; quarter <= 3; quarter++)
1815 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1816 struct tm const *probe_tm = localtime (&probe);
1817 if (probe_tm && probe_tm->tm_zone
1818 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1821 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1822 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1823 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1824 pc.local_time_zone_table[2].name = NULL;
1833 # if !HAVE_DECL_TZNAME
1834 extern char *tzname[];
1837 for (i = 0; i < 2; i++)
1839 pc.local_time_zone_table[i].name = tzname[i];
1840 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1841 pc.local_time_zone_table[i].value = i;
1843 pc.local_time_zone_table[i].name = NULL;
1846 pc.local_time_zone_table[0].name = NULL;
1850 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1851 && ! strcmp (pc.local_time_zone_table[0].name,
1852 pc.local_time_zone_table[1].name))
1854 /* This locale uses the same abbreviation for standard and
1855 daylight times. So if we see that abbreviation, we don't
1856 know whether it's daylight time. */
1857 pc.local_time_zone_table[0].value = -1;
1858 pc.local_time_zone_table[1].name = NULL;
1861 pc.debug_default_input_timezone = get_effective_timezone ();
1863 if (yyparse (&pc) != 0)
1865 if (pc.parse_datetime_debug)
1867 if (input_sentinel <= pc.input)
1868 dbg_printf (_("error: parsing failed\n"), pc.input);
1871 dbg_printf (_("error: parsing failed, stopped at '%s'\n"),
1878 /* determine effective timezone source */
1879 if (pc.parse_datetime_debug)
1881 long int tz = pc.debug_default_input_timezone;
1885 if (pc.timespec_seen)
1888 tz_src = _("'@timespec' - always UTC0");
1890 else if (pc.local_zones_seen || pc.zones_seen)
1893 tz_src = _("parsed date/time string");
1895 else if ((tz_env = getenv("TZ")))
1899 snprintf (dbg_tm, sizeof(dbg_tm), _("TZ=\"%s\" in date string"),
1903 else if (STREQ(tz_env,"UTC0"))
1905 /* Special case: using 'date -u' simply set TZ=UTC0 */
1906 tz_src = _("TZ=UTC0 environment value or -u");
1910 snprintf (dbg_tm, sizeof(dbg_tm),
1911 _("TZ=\"%s\" environment value"), tz_env);
1917 tz_src = _("system default");
1920 if (pc.parse_datetime_debug)
1921 dbg_printf (_("input timezone: %+03d:%02d (set from %s)\n"),
1922 (int)(tz/60), abs ((int)tz)%60, tz_src);
1926 if (pc.timespec_seen)
1927 *result = pc.seconds;
1930 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1931 | (pc.local_zones_seen + pc.zones_seen)))
1933 if (pc.parse_datetime_debug)
1935 if (pc.times_seen > 1)
1936 dbg_printf ("error: seen multiple time parts\n");
1937 if (pc.dates_seen > 1)
1938 dbg_printf ("error: seen multiple date parts\n");
1939 if (pc.days_seen > 1)
1940 dbg_printf ("error: seen multiple days parts\n");
1941 if (pc.dsts_seen > 1)
1942 dbg_printf ("error: seen multiple daylight-saving parts\n");
1943 if ( (pc.local_zones_seen + pc.zones_seen) > 1)
1944 dbg_printf ("error: seen multiple time-zone parts\n");
1949 tm.tm_year = to_year (pc.year, pc.parse_datetime_debug) - TM_YEAR_BASE;
1950 tm.tm_mon = pc.month - 1;
1951 tm.tm_mday = pc.day;
1952 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1954 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1957 const char* mrd = (pc.meridian==MERam)?"am":
1958 (pc.meridian==MERpm)?"pm":"";
1959 if (pc.parse_datetime_debug)
1960 dbg_printf (_("error: invalid hour %ld%s\n"), pc.hour, mrd);
1964 tm.tm_min = pc.minutes;
1965 tm.tm_sec = pc.seconds.tv_sec;
1966 if (pc.parse_datetime_debug)
1967 dbg_printf (_("using %s time as starting value: '%s'\n"),
1968 (pc.times_seen)?_("specified"):_("current"),
1969 debug_strftime (&tm,dbg_tm,sizeof (dbg_tm)));
1973 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1974 pc.seconds.tv_nsec = 0;
1975 if (pc.parse_datetime_debug)
1976 dbg_printf ("warning: using midnight as starting time: 00:00:00\n");
1979 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1980 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1983 /* But if the input explicitly specifies local time with or without
1984 DST, give mktime that information. */
1985 if (pc.local_zones_seen)
1986 tm.tm_isdst = pc.local_isdst;
1990 Start = mktime (&tm);
1992 if (! mktime_ok (&tm0, &tm, Start))
1994 if (! pc.zones_seen)
1996 debug_mktime_not_ok (&tm0, &tm, &pc, pc.zones_seen);
2002 /* Guard against falsely reporting errors near the time_t
2003 boundaries when parsing times in other time zones. For
2004 example, suppose the input string "1969-12-31 23:00:00 -0100",
2005 the current time zone is 8 hours ahead of UTC, and the min
2006 time_t value is 1970-01-01 00:00:00 UTC. Then the min
2007 localtime value is 1970-01-01 08:00:00, and mktime will
2008 therefore fail on 1969-12-31 23:00:00. To work around the
2009 problem, set the time zone to 1 hour behind UTC temporarily
2010 by setting TZ="XXX1:00" and try mktime again. */
2012 long int time_zone = pc.time_zone;
2013 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
2014 long int abs_time_zone_hour = abs_time_zone / 60;
2015 int abs_time_zone_min = abs_time_zone % 60;
2016 char tz1buf[sizeof "XXX+0:00" + TYPE_WIDTH (pc.time_zone) / 3];
2017 if (!tz_was_altered)
2018 tz0 = get_tz (tz0buf);
2019 sprintf (tz1buf, "XXX%s%ld:%02d", &"-"[time_zone < 0],
2020 abs_time_zone_hour, abs_time_zone_min);
2021 if (setenv ("TZ", tz1buf, 1) != 0)
2023 /* TODO: was warn () + print errno? */
2024 if (pc.parse_datetime_debug)
2025 dbg_printf (_("error: setenv('TZ','%s') failed\n"), tz1buf);
2029 tz_was_altered = true;
2031 Start = mktime (&tm);
2032 if (! mktime_ok (&tm0, &tm, Start))
2034 debug_mktime_not_ok (&tm0, &tm, &pc, pc.zones_seen);
2041 if (pc.days_seen && ! pc.dates_seen)
2043 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
2044 + 7 * (pc.day_ordinal
2045 - (0 < pc.day_ordinal
2046 && tm.tm_wday != pc.day_number)));
2048 Start = mktime (&tm);
2049 if (Start == (time_t) -1)
2051 if (pc.parse_datetime_debug)
2052 dbg_printf (_("error: day '%s' (day ordinal=%ld number=%d) " \
2053 "resulted in an invalid date: '%s'\n"),
2054 str_days (&pc,dbg_ord,sizeof (dbg_ord)),
2055 pc.day_ordinal,pc.day_number,
2056 debug_strfdatetime (&tm, &pc, dbg_tm,
2062 if (pc.parse_datetime_debug)
2063 dbg_printf (_("new start date: '%s' is '%s'\n"),
2064 str_days (&pc,dbg_ord,sizeof (dbg_ord)),
2065 debug_strfdatetime (&tm, &pc, dbg_tm,sizeof (dbg_tm)));
2069 if (pc.parse_datetime_debug)
2071 if (!pc.dates_seen && !pc.days_seen)
2072 dbg_printf (_("using current date as starting value: '%s'\n"),
2073 debug_strfdate (&tm,dbg_tm,sizeof (dbg_tm)));
2075 if (pc.days_seen && pc.dates_seen)
2076 dbg_printf (_("warning: day (%s) ignored when explicit dates " \
2078 str_days (&pc,dbg_ord,sizeof (dbg_ord)));
2080 dbg_printf (_("starting date/time: '%s'\n"),
2081 debug_strfdatetime (&tm, &pc, dbg_tm,sizeof (dbg_tm)));
2084 /* Add relative date. */
2085 if (pc.rel.year | pc.rel.month | pc.rel.day)
2087 if (pc.parse_datetime_debug)
2089 if ((pc.rel.year != 0 || pc.rel.month !=0) && tm.tm_mday==1)
2090 dbg_printf (_("warning: when adding relative months/years, " \
2091 "it is recommended to specify the 15th of the " \
2094 if (pc.rel.day != 0 && tm.tm_hour==0)
2095 dbg_printf (_("warning: when adding relative days, " \
2096 "it is recommended to specify 12:00pm\n"));
2099 int year = tm.tm_year + pc.rel.year;
2100 int month = tm.tm_mon + pc.rel.month;
2101 int day = tm.tm_mday + pc.rel.day;
2102 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
2103 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
2104 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
2106 /* TODO: what is the actual error? int-value wrap-around? */
2107 if (pc.parse_datetime_debug)
2108 dbg_printf (_("error: %s:%d\n"), __FILE__,__LINE__);
2115 tm.tm_hour = tm0.tm_hour;
2116 tm.tm_min = tm0.tm_min;
2117 tm.tm_sec = tm0.tm_sec;
2118 tm.tm_isdst = tm0.tm_isdst;
2119 Start = mktime (&tm);
2120 if (Start == (time_t) -1)
2122 if (pc.parse_datetime_debug)
2123 dbg_printf(_("error: adding relative date resulted " \
2124 "in an invalid date: '%s'\n"),
2125 debug_strfdatetime (&tm, &pc, dbg_tm,
2131 if (pc.parse_datetime_debug)
2133 dbg_printf (_("after date adjustment " \
2134 "(%+ld years, %+ld months, %+ld days),\n"),
2135 pc.rel.year, pc.rel.month, pc.rel.day);
2136 dbg_printf (_(" new date/time = '%s'\n"),
2137 debug_strfdatetime (&tm, &pc, dbg_tm,
2143 /* The only "output" of this if-block is an updated Start value,
2144 so this block must follow others that clobber Start. */
2147 long int delta = pc.time_zone * 60;
2149 #ifdef HAVE_TM_GMTOFF
2150 delta -= tm.tm_gmtoff;
2153 struct tm const *gmt = gmtime (&t);
2156 /* TODO: use 'warn(3)' + print errno ? */
2157 if (pc.parse_datetime_debug)
2158 dbg_printf (_("error: gmtime failed for t=%ld\n"),t);
2162 delta -= tm_diff (&tm, gmt);
2165 if ((Start < t1) != (delta < 0))
2167 if (pc.parse_datetime_debug)
2168 dbg_printf (_("error: timezone %ld caused time_t overflow\n"),
2171 goto fail; /* time_t overflow */
2176 if (pc.parse_datetime_debug)
2177 dbg_printf (_("'%s' = %ld epoch-seconds\n"),
2178 debug_strfdatetime (&tm, &pc, dbg_tm, sizeof (dbg_tm)),
2181 /* Add relative hours, minutes, and seconds. On hosts that support
2182 leap seconds, ignore the possibility of leap seconds; e.g.,
2183 "+ 10 minutes" adds 600 seconds, even if one of them is a
2184 leap second. Typically this is not what the user wants, but it's
2185 too hard to do it the other way, because the time zone indicator
2186 must be applied before relative times, and if mktime is applied
2187 again the time zone will be lost. */
2189 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
2190 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
2192 long int d1 = 60 * 60 * pc.rel.hour;
2193 time_t t1 = t0 + d1;
2194 long int d2 = 60 * pc.rel.minutes;
2195 time_t t2 = t1 + d2;
2196 long_time_t d3 = pc.rel.seconds;
2197 long_time_t t3 = t2 + d3;
2198 long int d4 = (sum_ns - normalized_ns) / BILLION;
2199 long_time_t t4 = t3 + d4;
2202 if ((d1 / (60 * 60) ^ pc.rel.hour)
2203 | (d2 / 60 ^ pc.rel.minutes)
2204 | ((t1 < t0) ^ (d1 < 0))
2205 | ((t2 < t1) ^ (d2 < 0))
2206 | ((t3 < t2) ^ (d3 < 0))
2207 | ((t4 < t3) ^ (d4 < 0))
2210 if (pc.parse_datetime_debug)
2211 dbg_printf (_("error: adding relative time caused an " \
2217 if (pc.parse_datetime_debug
2218 && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns))
2220 dbg_printf (_("after time adjustment (%+ld hours, " \
2221 "%+ld minutes, %+ld seconds, %+ld ns),\n"),
2222 pc.rel.hour,pc.rel.minutes,pc.rel.seconds,pc.rel.ns);
2223 dbg_printf (_(" new time = %ld epoch-seconds\n"),t5);
2226 result->tv_sec = t5;
2227 result->tv_nsec = normalized_ns;
2237 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
2241 if (ok && pc.parse_datetime_debug)
2243 /* print local timezone AFTER restoring TZ (if tz_was_altered)*/
2244 const long int otz = get_effective_timezone ();
2248 if ((tz_env = getenv("TZ")))
2250 /* Special case: using 'date -u' simply set TZ=UTC0 */
2251 if (STREQ(tz_env,"UTC0"))
2253 tz_src = _("TZ=UTC0 environment value or -u");
2257 snprintf (dbg_tm, sizeof(dbg_tm),
2258 _("TZ=\"%s\" environment value"), tz_env);
2264 tz_src = _("system default");
2267 if (pc.parse_datetime_debug)
2269 dbg_printf (_("output timezone: %+03d:%02d (set from %s)\n"),
2270 (int)(otz/60), abs ((int)otz)%60, tz_src);
2273 dbg_printf (_("final: %ld.%09ld (epoch-seconds)\n"),
2274 result->tv_sec,result->tv_nsec);
2276 struct tm const *gmt = gmtime (&result->tv_sec);
2277 dbg_printf (_("final: %s (UTC0)\n"),
2278 debug_strfdatetime (gmt, NULL, dbg_tm, sizeof (dbg_tm)));
2279 struct tm const *lmt = localtime (&result->tv_sec);
2280 dbg_printf (_("final: %s (output timezone TZ=%+03d:%02d)\n"),
2281 debug_strfdatetime (lmt, NULL, dbg_tm, sizeof (dbg_tm)),
2282 (int)(otz/60), abs ((int)otz)%60);
2292 main (int ac, char **av)
2296 printf ("Enter date, or blank line to exit.\n\t> ");
2299 buff[BUFSIZ - 1] = '\0';
2300 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
2303 struct tm const *tm;
2304 if (! parse_datetime (&d, buff, NULL))
2305 printf ("Bad format - couldn't convert.\n");
2306 else if (! (tm = localtime (&d.tv_sec)))
2308 long int sec = d.tv_sec;
2309 printf ("localtime (%ld) failed\n", sec);
2314 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
2315 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
2316 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);