1 %{
2 /* Parse a string into an internal timestamp.
3 
4    Copyright (C) 1999-2000, 2002-2018 Free Software Foundation, Inc.
5 
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.
10 
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.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
18 
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.
23 
24    Modified by Assaf Gordon <assafgordon@gmail.com> in 2016 to add
25    debug output.
26 
27    Modified by Paul Eggert <eggert@twinsun.com> in 1999 to do the
28    right thing about local DST.  Also modified by Paul Eggert
29    <eggert@cs.ucla.edu> in 2004 to support nanosecond-resolution
30    timestamps, in 2004 to support TZ strings in dates, and in 2017 to
31    check for integer overflow and to support longer-than-'long'
32    'time_t' and 'tv_nsec'.  */
33 
34 #include <config.h>
35 
36 #include "parse-datetime.h"
37 
38 #include "intprops.h"
39 #include "timespec.h"
40 #include "verify.h"
41 #include "strftime.h"
42 
43 /* There's no need to extend the stack, so there's no need to involve
44    alloca.  */
45 #define YYSTACK_USE_ALLOCA 0
46 
47 /* Tell Bison how much stack space is needed.  20 should be plenty for
48    this grammar, which is not right recursive.  Beware setting it too
49    high, since that might cause problems on machines whose
50    implementations have lame stack-overflow checking.  */
51 #define YYMAXDEPTH 20
52 #define YYINITDEPTH YYMAXDEPTH
53 
54 /* Since the code of parse-datetime.y is not included in the Emacs executable
55    itself, there is no need to #define static in this file.  Even if
56    the code were included in the Emacs executable, it probably
57    wouldn't do any harm to #undef it here; this will only cause
58    problems if we try to write to a static variable, which I don't
59    think this code needs to do.  */
60 #ifdef emacs
61 # undef static
62 #endif
63 
64 #include <inttypes.h>
65 #include <c-ctype.h>
66 #include <limits.h>
67 #include <stdarg.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 
72 #include "gettext.h"
73 
74 #define _(str) gettext (str)
75 
76 /* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
77    use _STDLIB_H_ as witness.  Map the latter to the one bison uses.  */
78 /* FIXME: this is temporary.  Remove when we have a mechanism to ensure
79    that the version we're using is fixed, too.  */
80 #ifdef _STDLIB_H_
81 # undef _STDLIB_H
82 # define _STDLIB_H 1
83 #endif
84 
85 /* The __attribute__ feature is available in gcc versions 2.5 and later.
86    The __-protected variants of the attributes 'format' and 'printf' are
87    accepted by gcc versions 2.6.4 (effectively 2.7) and later.
88    Enable _GL_ATTRIBUTE_FORMAT only if these are supported too, because
89    gnulib and libintl do '#define printf __printf__' when they override
90    the 'printf' function.  */
91 #if 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
92 # define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec))
93 #else
94 # define _GL_ATTRIBUTE_FORMAT(spec) /* empty */
95 #endif
96 
97 /* Shift A right by B bits portably, by dividing A by 2**B and
98    truncating towards minus infinity.  A and B should be free of side
99    effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
100    INT_BITS is the number of useful bits in an int.  GNU code can
101    assume that INT_BITS is at least 32.
102 
103    ISO C99 says that A >> B is implementation-defined if A < 0.  Some
104    implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
105    right in the usual way when A < 0, so SHR falls back on division if
106    ordinary A >> B doesn't seem to be the usual signed shift.  */
107 #define SHR(a, b)       \
108   (-1 >> 1 == -1        \
109    ? (a) >> (b)         \
110    : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
111 
112 #define HOUR(x) (60 * 60 * (x))
113 
114 #define STREQ(a, b) (strcmp (a, b) == 0)
115 
116 /* Verify that time_t is an integer as POSIX requires, and that every
117    time_t value fits in intmax_t.  Please file a bug report if these
118    assumptions are false on your platform.  */
119 verify (TYPE_IS_INTEGER (time_t));
120 verify (!TYPE_SIGNED (time_t) || INTMAX_MIN <= TYPE_MINIMUM (time_t));
121 verify (TYPE_MAXIMUM (time_t) <= INTMAX_MAX);
122 
123 /* True if N is out of range for time_t.  */
124 static bool
time_overflow(intmax_t n)125 time_overflow (intmax_t n)
126 {
127   return ! ((TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= n : 0 <= n)
128             && n <= TYPE_MAXIMUM (time_t));
129 }
130 
131 /* Convert a possibly-signed character to an unsigned character.  This is
132    a bit safer than casting to unsigned char, since it catches some type
133    errors that the cast doesn't.  */
to_uchar(char ch)134 static unsigned char to_uchar (char ch) { return ch; }
135 
136 static void _GL_ATTRIBUTE_FORMAT ((__printf__, 1, 2))
dbg_printf(char const * msg,...)137 dbg_printf (char const *msg, ...)
138 {
139   va_list args;
140   /* TODO: use gnulib's 'program_name' instead?  */
141   fputs ("date: ", stderr);
142 
143   va_start (args, msg);
144   vfprintf (stderr, msg, args);
145   va_end (args);
146 }
147 
148 
149 /* An integer value, and the number of digits in its textual
150    representation.  */
151 typedef struct
152 {
153   bool negative;
154   intmax_t value;
155   ptrdiff_t digits;
156 } textint;
157 
158 /* An entry in the lexical lookup table.  */
159 typedef struct
160 {
161   char const *name;
162   int type;
163   int value;
164 } table;
165 
166 /* Meridian: am, pm, or 24-hour style.  */
167 enum { MERam, MERpm, MER24 };
168 
169 /* A reasonable upper bound for the buffer used in debug output.  */
170 enum { DBGBUFSIZE = 100 };
171 
172 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
173 
174 /* Relative times.  */
175 typedef struct
176 {
177   /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
178   intmax_t year;
179   intmax_t month;
180   intmax_t day;
181   intmax_t hour;
182   intmax_t minutes;
183   intmax_t seconds;
184   int ns;
185 } relative_time;
186 
187 #if HAVE_COMPOUND_LITERALS
188 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
189 #else
190 static relative_time const RELATIVE_TIME_0;
191 #endif
192 
193 /* Information passed to and from the parser.  */
194 typedef struct
195 {
196   /* The input string remaining to be parsed.  */
197   const char *input;
198 
199   /* N, if this is the Nth Tuesday.  */
200   intmax_t day_ordinal;
201 
202   /* Day of week; Sunday is 0.  */
203   int day_number;
204 
205   /* tm_isdst flag for the local zone.  */
206   int local_isdst;
207 
208   /* Time zone, in seconds east of UT.  */
209   int time_zone;
210 
211   /* Style used for time.  */
212   int meridian;
213 
214   /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds.  */
215   textint year;
216   intmax_t month;
217   intmax_t day;
218   intmax_t hour;
219   intmax_t minutes;
220   struct timespec seconds; /* includes nanoseconds */
221 
222   /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
223   relative_time rel;
224 
225   /* Presence or counts of nonterminals of various flavors parsed so far.  */
226   bool timespec_seen;
227   bool rels_seen;
228   ptrdiff_t dates_seen;
229   ptrdiff_t days_seen;
230   ptrdiff_t local_zones_seen;
231   ptrdiff_t dsts_seen;
232   ptrdiff_t times_seen;
233   ptrdiff_t zones_seen;
234   bool year_seen;
235 
236   /* Print debugging output to stderr.  */
237   bool parse_datetime_debug;
238 
239   /* Which of the 'seen' parts have been printed when debugging.  */
240   bool debug_dates_seen;
241   bool debug_days_seen;
242   bool debug_local_zones_seen;
243   bool debug_times_seen;
244   bool debug_zones_seen;
245   bool debug_year_seen;
246 
247   /* The user specified explicit ordinal day value.  */
248   bool debug_ordinal_day_seen;
249 
250   /* Table of local time zone abbreviations, terminated by a null entry.  */
251   table local_time_zone_table[3];
252 } parser_control;
253 
254 union YYSTYPE;
255 static int yylex (union YYSTYPE *, parser_control *);
256 static int yyerror (parser_control const *, char const *);
257 static bool time_zone_hhmm (parser_control *, textint, intmax_t);
258 
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,
261    YYYY, ...).  */
262 static void
digits_to_date_time(parser_control * pc,textint text_int)263 digits_to_date_time (parser_control *pc, textint text_int)
264 {
265   if (pc->dates_seen && ! pc->year.digits
266       && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
267     {
268       pc->year_seen = true;
269       pc->year = text_int;
270     }
271   else
272     {
273       if (4 < text_int.digits)
274         {
275           pc->dates_seen++;
276           pc->day = text_int.value % 100;
277           pc->month = (text_int.value / 100) % 100;
278           pc->year.value = text_int.value / 10000;
279           pc->year.digits = text_int.digits - 4;
280         }
281       else
282         {
283           pc->times_seen++;
284           if (text_int.digits <= 2)
285             {
286               pc->hour = text_int.value;
287               pc->minutes = 0;
288             }
289           else
290             {
291               pc->hour = text_int.value / 100;
292               pc->minutes = text_int.value % 100;
293             }
294           pc->seconds.tv_sec = 0;
295           pc->seconds.tv_nsec = 0;
296           pc->meridian = MER24;
297         }
298     }
299 }
300 
301 /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1).  Return true
302    if successful, false if an overflow occurred.  */
303 static bool
apply_relative_time(parser_control * pc,relative_time rel,int factor)304 apply_relative_time (parser_control *pc, relative_time rel, int factor)
305 {
306   if (factor < 0
307       ? (INT_SUBTRACT_WRAPV (pc->rel.ns, rel.ns, &pc->rel.ns)
308          | INT_SUBTRACT_WRAPV (pc->rel.seconds, rel.seconds, &pc->rel.seconds)
309          | INT_SUBTRACT_WRAPV (pc->rel.minutes, rel.minutes, &pc->rel.minutes)
310          | INT_SUBTRACT_WRAPV (pc->rel.hour, rel.hour, &pc->rel.hour)
311          | INT_SUBTRACT_WRAPV (pc->rel.day, rel.day, &pc->rel.day)
312          | INT_SUBTRACT_WRAPV (pc->rel.month, rel.month, &pc->rel.month)
313          | INT_SUBTRACT_WRAPV (pc->rel.year, rel.year, &pc->rel.year))
314       : (INT_ADD_WRAPV (pc->rel.ns, rel.ns, &pc->rel.ns)
315          | INT_ADD_WRAPV (pc->rel.seconds, rel.seconds, &pc->rel.seconds)
316          | INT_ADD_WRAPV (pc->rel.minutes, rel.minutes, &pc->rel.minutes)
317          | INT_ADD_WRAPV (pc->rel.hour, rel.hour, &pc->rel.hour)
318          | INT_ADD_WRAPV (pc->rel.day, rel.day, &pc->rel.day)
319          | INT_ADD_WRAPV (pc->rel.month, rel.month, &pc->rel.month)
320          | INT_ADD_WRAPV (pc->rel.year, rel.year, &pc->rel.year)))
321     return false;
322   pc->rels_seen = true;
323   return true;
324 }
325 
326 /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments.  */
327 static void
set_hhmmss(parser_control * pc,intmax_t hour,intmax_t minutes,time_t sec,int nsec)328 set_hhmmss (parser_control *pc, intmax_t hour, intmax_t minutes,
329             time_t sec, int nsec)
330 {
331   pc->hour = hour;
332   pc->minutes = minutes;
333   pc->seconds.tv_sec = sec;
334   pc->seconds.tv_nsec = nsec;
335 }
336 
337 /* Return a textual representation of the day ordinal/number values
338    in the parser_control struct (e.g., "last wed", "this tues", "thu").  */
339 static const char *
str_days(parser_control * pc,char * buffer,int n)340 str_days (parser_control *pc, char *buffer, int n)
341 {
342   /* TODO: use relative_time_table for reverse lookup.  */
343   static char const ordinal_values[][11] = {
344      "last",
345      "this",
346      "next/first",
347      "(SECOND)", /* SECOND is commented out in relative_time_table.  */
348      "third",
349      "fourth",
350      "fifth",
351      "sixth",
352      "seventh",
353      "eight",
354      "ninth",
355      "tenth",
356      "eleventh",
357      "twelfth"
358   };
359 
360   static char const days_values[][4] = {
361      "Sun",
362      "Mon",
363      "Tue",
364      "Wed",
365      "Thu",
366      "Fri",
367      "Sat"
368   };
369 
370   int len;
371 
372   /* Don't add an ordinal prefix if the user didn't specify it
373      (e.g., "this wed" vs "wed").  */
374   if (pc->debug_ordinal_day_seen)
375     {
376       /* Use word description if possible (e.g., -1 = last, 3 = third).  */
377       len = (-1 <= pc->day_ordinal && pc->day_ordinal <= 12
378              ? snprintf (buffer, n, "%s", ordinal_values[pc->day_ordinal + 1])
379              : snprintf (buffer, n, "%"PRIdMAX, pc->day_ordinal));
380     }
381   else
382     {
383       buffer[0] = '\0';
384       len = 0;
385     }
386 
387   /* Add the day name */
388   if (0 <= pc->day_number && pc->day_number <= 6 && 0 <= len && len < n)
389     snprintf (buffer + len, n - len, &" %s"[len == 0],
390               days_values[pc->day_number]);
391   else
392     {
393       /* invalid day_number value - should never happen */
394     }
395   return buffer;
396 }
397 
398 /* Convert a time zone to its string representation.  */
399 
400 enum { TIME_ZONE_BUFSIZE = INT_STRLEN_BOUND (intmax_t) + sizeof ":MM:SS" } ;
401 
402 static char const *
time_zone_str(int time_zone,char time_zone_buf[TIME_ZONE_BUFSIZE])403 time_zone_str (int time_zone, char time_zone_buf[TIME_ZONE_BUFSIZE])
404 {
405   char *p = time_zone_buf;
406   char sign = time_zone < 0 ? '-' : '+';
407   int hour = abs (time_zone / (60 * 60));
408   p += sprintf (time_zone_buf, "%c%02d", sign, hour);
409   int offset_from_hour = abs (time_zone % (60 * 60));
410   if (offset_from_hour != 0)
411     {
412       int mm = offset_from_hour / 60;
413       int ss = offset_from_hour % 60;
414       *p++ = ':';
415       *p++ = '0' + mm / 10;
416       *p++ = '0' + mm % 10;
417       if (ss)
418         {
419           *p++ = ':';
420           *p++ = '0' + ss / 10;
421           *p++ = '0' + ss % 10;
422         }
423       *p = '\0';
424     }
425   return time_zone_buf;
426 }
427 
428 /* debugging: print the current time in the parser_control structure.
429    The parser will increment "*_seen" members for those which were parsed.
430    This function will print only newly seen parts.  */
431 static void
debug_print_current_time(char const * item,parser_control * pc)432 debug_print_current_time (char const *item, parser_control *pc)
433 {
434   bool space = false;
435 
436   if (!pc->parse_datetime_debug)
437     return;
438 
439   /* no newline, more items printed below */
440   dbg_printf (_("parsed %s part: "), item);
441 
442   if (pc->dates_seen && !pc->debug_dates_seen)
443     {
444       /*TODO: use pc->year.negative?  */
445       fprintf (stderr, "(Y-M-D) %04"PRIdMAX"-%02"PRIdMAX"-%02"PRIdMAX,
446               pc->year.value, pc->month, pc->day);
447       pc->debug_dates_seen = true;
448       space = true;
449     }
450 
451   if (pc->year_seen != pc->debug_year_seen)
452     {
453       if (space)
454         fputc (' ', stderr);
455       fprintf (stderr, _("year: %04"PRIdMAX), pc->year.value);
456 
457       pc->debug_year_seen = pc->year_seen;
458       space = true;
459     }
460 
461   if (pc->times_seen && !pc->debug_times_seen)
462     {
463       intmax_t sec = pc->seconds.tv_sec;
464       fprintf (stderr, &" %02"PRIdMAX":%02"PRIdMAX":%02"PRIdMAX[!space],
465                pc->hour, pc->minutes, sec);
466       if (pc->seconds.tv_nsec != 0)
467         {
468           int nsec = pc->seconds.tv_nsec;
469           fprintf (stderr, ".%09d", nsec);
470         }
471       if (pc->meridian == MERpm)
472         fputs ("pm", stderr);
473 
474       pc->debug_times_seen = true;
475       space = true;
476     }
477 
478   if (pc->days_seen && !pc->debug_days_seen)
479     {
480       if (space)
481         fputc (' ', stderr);
482       char tmp[DBGBUFSIZE];
483       fprintf (stderr, _("%s (day ordinal=%"PRIdMAX" number=%d)"),
484                str_days (pc, tmp, sizeof tmp),
485                pc->day_ordinal, pc->day_number);
486       pc->debug_days_seen = true;
487       space = true;
488     }
489 
490   /* local zone strings only change the DST settings,
491      not the timezone value.  If seen, inform about the DST.  */
492   if (pc->local_zones_seen && !pc->debug_local_zones_seen)
493     {
494       fprintf (stderr, &" isdst=%d%s"[!space],
495                pc->local_isdst, pc->dsts_seen ? " DST" : "");
496       pc->debug_local_zones_seen = true;
497       space = true;
498     }
499 
500   if (pc->zones_seen && !pc->debug_zones_seen)
501     {
502       char time_zone_buf[TIME_ZONE_BUFSIZE];
503       fprintf (stderr, &" UTC%s"[!space],
504                time_zone_str (pc->time_zone, time_zone_buf));
505       pc->debug_zones_seen = true;
506       space = true;
507     }
508 
509   if (pc->timespec_seen)
510     {
511       intmax_t sec = pc->seconds.tv_sec;
512       if (space)
513         fputc (' ', stderr);
514       fprintf (stderr, _("number of seconds: %"PRIdMAX), sec);
515     }
516 
517   fputc ('\n', stderr);
518 }
519 
520 /* Debugging: print the current relative values.  */
521 
522 static bool
print_rel_part(bool space,intmax_t val,char const * name)523 print_rel_part (bool space, intmax_t val, char const *name)
524 {
525   if (val == 0)
526     return space;
527   fprintf (stderr, &" %+"PRIdMAX" %s"[!space], val, name);
528   return true;
529 }
530 
531 static void
debug_print_relative_time(char const * item,parser_control const * pc)532 debug_print_relative_time (char const *item, parser_control const *pc)
533 {
534   bool space = false;
535 
536   if (!pc->parse_datetime_debug)
537     return;
538 
539   /* no newline, more items printed below */
540   dbg_printf (_("parsed %s part: "), item);
541 
542   if (pc->rel.year == 0 && pc->rel.month == 0 && pc->rel.day == 0
543       && pc->rel.hour == 0 && pc->rel.minutes == 0 && pc->rel.seconds == 0
544       && pc->rel.ns == 0)
545     {
546       /* Special case: relative time of this/today/now */
547       fputs (_("today/this/now\n"), stderr);
548       return;
549     }
550 
551   space = print_rel_part (space, pc->rel.year, "year(s)");
552   space = print_rel_part (space, pc->rel.month, "month(s)");
553   space = print_rel_part (space, pc->rel.day, "day(s)");
554   space = print_rel_part (space, pc->rel.hour, "hour(s)");
555   space = print_rel_part (space, pc->rel.minutes, "minutes");
556   space = print_rel_part (space, pc->rel.seconds, "seconds");
557   print_rel_part (space, pc->rel.ns, "nanoseconds");
558 
559   fputc ('\n', stderr);
560 }
561 
562 
563 
564 %}
565 
566 /* We want a reentrant parser, even if the TZ manipulation and the calls to
567    localtime and gmtime are not reentrant.  */
568 %pure-parser
569 %parse-param { parser_control *pc }
570 %lex-param { parser_control *pc }
571 
572 /* This grammar has 31 shift/reduce conflicts.  */
573 %expect 31
574 
575 %union
576 {
577   intmax_t intval;
578   textint textintval;
579   struct timespec timespec;
580   relative_time rel;
581 }
582 
583 %token <intval> tAGO
584 %token tDST
585 
586 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
587 %token <intval> tDAY_UNIT tDAY_SHIFT
588 
589 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
590 %token <intval> tMONTH tORDINAL tZONE
591 
592 %token <textintval> tSNUMBER tUNUMBER
593 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
594 
595 %type <intval> o_colon_minutes
596 %type <timespec> seconds signed_seconds unsigned_seconds
597 
598 %type <rel> relunit relunit_snumber dayshift
599 
600 %%
601 
602 spec:
603     timespec
604   | items
605   ;
606 
607 timespec:
608     '@' seconds
609       {
610         pc->seconds = $2;
611         pc->timespec_seen = true;
612         debug_print_current_time (_("number of seconds"), pc);
613       }
614   ;
615 
616 items:
617     /* empty */
618   | items item
619   ;
620 
621 item:
622     datetime
623       {
624         pc->times_seen++; pc->dates_seen++;
625         debug_print_current_time (_("datetime"), pc);
626       }
627   | time
628       {
629         pc->times_seen++;
630         debug_print_current_time (_("time"), pc);
631       }
632   | local_zone
633       {
634         pc->local_zones_seen++;
635         debug_print_current_time (_("local_zone"), pc);
636       }
637   | zone
638       {
639         pc->zones_seen++;
640         debug_print_current_time (_("zone"), pc);
641       }
642   | date
643       {
644         pc->dates_seen++;
645         debug_print_current_time (_("date"), pc);
646       }
647   | day
648       {
649         pc->days_seen++;
650         debug_print_current_time (_("day"), pc);
651       }
652   | rel
653       {
654         debug_print_relative_time (_("relative"), pc);
655       }
656   | number
657       {
658         debug_print_current_time (_("number"), pc);
659       }
660   | hybrid
661       {
662         debug_print_relative_time (_("hybrid"), pc);
663       }
664   ;
665 
666 datetime:
667     iso_8601_datetime
668   ;
669 
670 iso_8601_datetime:
671     iso_8601_date 'T' iso_8601_time
672   ;
673 
674 time:
675     tUNUMBER tMERIDIAN
676       {
677         set_hhmmss (pc, $1.value, 0, 0, 0);
678         pc->meridian = $2;
679       }
680   | tUNUMBER ':' tUNUMBER tMERIDIAN
681       {
682         set_hhmmss (pc, $1.value, $3.value, 0, 0);
683         pc->meridian = $4;
684       }
685   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN
686       {
687         set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
688         pc->meridian = $6;
689       }
690   | iso_8601_time
691   ;
692 
693 iso_8601_time:
694     tUNUMBER zone_offset
695       {
696         set_hhmmss (pc, $1.value, 0, 0, 0);
697         pc->meridian = MER24;
698       }
699   | tUNUMBER ':' tUNUMBER o_zone_offset
700       {
701         set_hhmmss (pc, $1.value, $3.value, 0, 0);
702         pc->meridian = MER24;
703       }
704   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_zone_offset
705       {
706         set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
707         pc->meridian = MER24;
708       }
709   ;
710 
711 o_zone_offset:
712   /* empty */
713   | zone_offset
714   ;
715 
716 zone_offset:
717     tSNUMBER o_colon_minutes
718       {
719         pc->zones_seen++;
720         if (! time_zone_hhmm (pc, $1, $2)) YYABORT;
721       }
722   ;
723 
724 /* Local zone strings affect only the DST setting, and take effect
725    only if the current TZ setting is relevant.
726 
727    Example 1:
728    'EEST' is parsed as tLOCAL_ZONE, as it relates to the effective TZ:
729         TZ='Europe/Helsinki' date -d '2016-06-30 EEST'
730 
731    Example 2:
732    'EEST' is parsed as tDAYZONE:
733         TZ='Asia/Tokyo' date -d '2016-06-30 EEST'
734 
735    This is implemented by probing the next three calendar quarters
736    of the effective timezone and looking for DST changes -
737    if found, the timezone name (EEST) is inserted into
738    the lexical lookup table with type tLOCAL_ZONE.
739    (Search for 'quarter' comment in  'parse_datetime2'.)
740 */
741 local_zone:
742     tLOCAL_ZONE
743       { pc->local_isdst = $1; }
744   | tLOCAL_ZONE tDST
745       {
746         pc->local_isdst = 1;
747         pc->dsts_seen++;
748       }
749   ;
750 
751 /* Note 'T' is a special case, as it is used as the separator in ISO
752    8601 date and time of day representation.  */
753 zone:
754     tZONE
755       { pc->time_zone = $1; }
756   | 'T'
757       { pc->time_zone = HOUR (7); }
758   | tZONE relunit_snumber
759       { pc->time_zone = $1;
760         if (! apply_relative_time (pc, $2, 1)) YYABORT;
761         debug_print_relative_time (_("relative"), pc);
762       }
763   | 'T' relunit_snumber
764       { pc->time_zone = HOUR (7);
765         if (! apply_relative_time (pc, $2, 1)) YYABORT;
766         debug_print_relative_time (_("relative"), pc);
767       }
768   | tZONE tSNUMBER o_colon_minutes
769       { if (! time_zone_hhmm (pc, $2, $3)) YYABORT;
770         if (INT_ADD_WRAPV (pc->time_zone, $1, &pc->time_zone)) YYABORT; }
771   | tDAYZONE
772       { pc->time_zone = $1 + 60 * 60; }
773   | tZONE tDST
774       { pc->time_zone = $1 + 60 * 60; }
775   ;
776 
777 day:
778     tDAY
779       {
780         pc->day_ordinal = 0;
781         pc->day_number = $1;
782       }
783   | tDAY ','
784       {
785         pc->day_ordinal = 0;
786         pc->day_number = $1;
787       }
788   | tORDINAL tDAY
789       {
790         pc->day_ordinal = $1;
791         pc->day_number = $2;
792         pc->debug_ordinal_day_seen = true;
793       }
794   | tUNUMBER tDAY
795       {
796         pc->day_ordinal = $1.value;
797         pc->day_number = $2;
798         pc->debug_ordinal_day_seen = true;
799       }
800   ;
801 
802 date:
803     tUNUMBER '/' tUNUMBER
804       {
805         pc->month = $1.value;
806         pc->day = $3.value;
807       }
808   | tUNUMBER '/' tUNUMBER '/' tUNUMBER
809       {
810         /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
811            otherwise as MM/DD/YY.
812            The goal in recognizing YYYY/MM/DD is solely to support legacy
813            machine-generated dates like those in an RCS log listing.  If
814            you want portability, use the ISO 8601 format.  */
815         if (4 <= $1.digits)
816           {
817             if (pc->parse_datetime_debug)
818               {
819                 intmax_t digits = $1.digits;
820                 dbg_printf (_("warning: value %"PRIdMAX" has %"PRIdMAX" digits. "
821                               "Assuming YYYY/MM/DD\n"),
822                             $1.value, digits);
823               }
824 
825             pc->year = $1;
826             pc->month = $3.value;
827             pc->day = $5.value;
828           }
829         else
830           {
831             if (pc->parse_datetime_debug)
832               dbg_printf (_("warning: value %"PRIdMAX" has less than 4 digits. "
833                             "Assuming MM/DD/YY[YY]\n"),
834                           $1.value);
835 
836             pc->month = $1.value;
837             pc->day = $3.value;
838             pc->year = $5;
839           }
840       }
841   | tUNUMBER tMONTH tSNUMBER
842       {
843         /* E.g., 17-JUN-1992.  */
844         pc->day = $1.value;
845         pc->month = $2;
846         if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->year.value)) YYABORT;
847         pc->year.digits = $3.digits;
848       }
849   | tMONTH tSNUMBER tSNUMBER
850       {
851         /* E.g., JUN-17-1992.  */
852         pc->month = $1;
853         if (INT_SUBTRACT_WRAPV (0, $2.value, &pc->day)) YYABORT;
854         if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->year.value)) YYABORT;
855         pc->year.digits = $3.digits;
856       }
857   | tMONTH tUNUMBER
858       {
859         pc->month = $1;
860         pc->day = $2.value;
861       }
862   | tMONTH tUNUMBER ',' tUNUMBER
863       {
864         pc->month = $1;
865         pc->day = $2.value;
866         pc->year = $4;
867       }
868   | tUNUMBER tMONTH
869       {
870         pc->day = $1.value;
871         pc->month = $2;
872       }
873   | tUNUMBER tMONTH tUNUMBER
874       {
875         pc->day = $1.value;
876         pc->month = $2;
877         pc->year = $3;
878       }
879   | iso_8601_date
880   ;
881 
882 iso_8601_date:
883     tUNUMBER tSNUMBER tSNUMBER
884       {
885         /* ISO 8601 format.  YYYY-MM-DD.  */
886         pc->year = $1;
887         if (INT_SUBTRACT_WRAPV (0, $2.value, &pc->month)) YYABORT;
888         if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->day)) YYABORT;
889       }
890   ;
891 
892 rel:
893     relunit tAGO
894       { if (! apply_relative_time (pc, $1, $2)) YYABORT; }
895   | relunit
896       { if (! apply_relative_time (pc, $1, 1)) YYABORT; }
897   | dayshift
898       { if (! apply_relative_time (pc, $1, 1)) YYABORT; }
899   ;
900 
901 relunit:
902     tORDINAL tYEAR_UNIT
903       { $$ = RELATIVE_TIME_0; $$.year = $1; }
904   | tUNUMBER tYEAR_UNIT
905       { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
906   | tYEAR_UNIT
907       { $$ = RELATIVE_TIME_0; $$.year = 1; }
908   | tORDINAL tMONTH_UNIT
909       { $$ = RELATIVE_TIME_0; $$.month = $1; }
910   | tUNUMBER tMONTH_UNIT
911       { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
912   | tMONTH_UNIT
913       { $$ = RELATIVE_TIME_0; $$.month = 1; }
914   | tORDINAL tDAY_UNIT
915       { $$ = RELATIVE_TIME_0;
916         if (INT_MULTIPLY_WRAPV ($1, $2, &$$.day)) YYABORT; }
917   | tUNUMBER tDAY_UNIT
918       { $$ = RELATIVE_TIME_0;
919         if (INT_MULTIPLY_WRAPV ($1.value, $2, &$$.day)) YYABORT; }
920   | tDAY_UNIT
921       { $$ = RELATIVE_TIME_0; $$.day = $1; }
922   | tORDINAL tHOUR_UNIT
923       { $$ = RELATIVE_TIME_0; $$.hour = $1; }
924   | tUNUMBER tHOUR_UNIT
925       { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
926   | tHOUR_UNIT
927       { $$ = RELATIVE_TIME_0; $$.hour = 1; }
928   | tORDINAL tMINUTE_UNIT
929       { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
930   | tUNUMBER tMINUTE_UNIT
931       { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
932   | tMINUTE_UNIT
933       { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
934   | tORDINAL tSEC_UNIT
935       { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
936   | tUNUMBER tSEC_UNIT
937       { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
938   | tSDECIMAL_NUMBER tSEC_UNIT
939       { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
940   | tUDECIMAL_NUMBER tSEC_UNIT
941       { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
942   | tSEC_UNIT
943       { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
944   | relunit_snumber
945   ;
946 
947 relunit_snumber:
948     tSNUMBER tYEAR_UNIT
949       { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
950   | tSNUMBER tMONTH_UNIT
951       { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
952   | tSNUMBER tDAY_UNIT
953       { $$ = RELATIVE_TIME_0;
954         if (INT_MULTIPLY_WRAPV ($1.value, $2, &$$.day)) YYABORT; }
955   | tSNUMBER tHOUR_UNIT
956       { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
957   | tSNUMBER tMINUTE_UNIT
958       { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
959   | tSNUMBER tSEC_UNIT
960       { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
961   ;
962 
963 dayshift:
964     tDAY_SHIFT
965       { $$ = RELATIVE_TIME_0; $$.day = $1; }
966   ;
967 
968 seconds: signed_seconds | unsigned_seconds;
969 
970 signed_seconds:
971     tSDECIMAL_NUMBER
972   | tSNUMBER
973       { if (time_overflow ($1.value)) YYABORT;
974         $$.tv_sec = $1.value; $$.tv_nsec = 0; }
975   ;
976 
977 unsigned_seconds:
978     tUDECIMAL_NUMBER
979   | tUNUMBER
980       { if (time_overflow ($1.value)) YYABORT;
981         $$.tv_sec = $1.value; $$.tv_nsec = 0; }
982   ;
983 
984 number:
985     tUNUMBER
986       { digits_to_date_time (pc, $1); }
987   ;
988 
989 hybrid:
990     tUNUMBER relunit_snumber
991       {
992         /* Hybrid all-digit and relative offset, so that we accept e.g.,
993            "YYYYMMDD +N days" as well as "YYYYMMDD N days".  */
994         digits_to_date_time (pc, $1);
995         if (! apply_relative_time (pc, $2, 1)) YYABORT;
996       }
997   ;
998 
999 o_colon_minutes:
1000     /* empty */
1001       { $$ = -1; }
1002   | ':' tUNUMBER
1003       { $$ = $2.value; }
1004   ;
1005 
1006 %%
1007 
1008 static table const meridian_table[] =
1009 {
1010   { "AM",   tMERIDIAN, MERam },
1011   { "A.M.", tMERIDIAN, MERam },
1012   { "PM",   tMERIDIAN, MERpm },
1013   { "P.M.", tMERIDIAN, MERpm },
1014   { NULL, 0, 0 }
1015 };
1016 
1017 static table const dst_table[] =
1018 {
1019   { "DST", tDST, 0 }
1020 };
1021 
1022 static table const month_and_day_table[] =
1023 {
1024   { "JANUARY",  tMONTH,  1 },
1025   { "FEBRUARY", tMONTH,  2 },
1026   { "MARCH",    tMONTH,  3 },
1027   { "APRIL",    tMONTH,  4 },
1028   { "MAY",      tMONTH,  5 },
1029   { "JUNE",     tMONTH,  6 },
1030   { "JULY",     tMONTH,  7 },
1031   { "AUGUST",   tMONTH,  8 },
1032   { "SEPTEMBER",tMONTH,  9 },
1033   { "SEPT",     tMONTH,  9 },
1034   { "OCTOBER",  tMONTH, 10 },
1035   { "NOVEMBER", tMONTH, 11 },
1036   { "DECEMBER", tMONTH, 12 },
1037   { "SUNDAY",   tDAY,    0 },
1038   { "MONDAY",   tDAY,    1 },
1039   { "TUESDAY",  tDAY,    2 },
1040   { "TUES",     tDAY,    2 },
1041   { "WEDNESDAY",tDAY,    3 },
1042   { "WEDNES",   tDAY,    3 },
1043   { "THURSDAY", tDAY,    4 },
1044   { "THUR",     tDAY,    4 },
1045   { "THURS",    tDAY,    4 },
1046   { "FRIDAY",   tDAY,    5 },
1047   { "SATURDAY", tDAY,    6 },
1048   { NULL, 0, 0 }
1049 };
1050 
1051 static table const time_units_table[] =
1052 {
1053   { "YEAR",     tYEAR_UNIT,      1 },
1054   { "MONTH",    tMONTH_UNIT,     1 },
1055   { "FORTNIGHT",tDAY_UNIT,      14 },
1056   { "WEEK",     tDAY_UNIT,       7 },
1057   { "DAY",      tDAY_UNIT,       1 },
1058   { "HOUR",     tHOUR_UNIT,      1 },
1059   { "MINUTE",   tMINUTE_UNIT,    1 },
1060   { "MIN",      tMINUTE_UNIT,    1 },
1061   { "SECOND",   tSEC_UNIT,       1 },
1062   { "SEC",      tSEC_UNIT,       1 },
1063   { NULL, 0, 0 }
1064 };
1065 
1066 /* Assorted relative-time words.  */
1067 static table const relative_time_table[] =
1068 {
1069   { "TOMORROW", tDAY_SHIFT,      1 },
1070   { "YESTERDAY",tDAY_SHIFT,     -1 },
1071   { "TODAY",    tDAY_SHIFT,      0 },
1072   { "NOW",      tDAY_SHIFT,      0 },
1073   { "LAST",     tORDINAL,       -1 },
1074   { "THIS",     tORDINAL,        0 },
1075   { "NEXT",     tORDINAL,        1 },
1076   { "FIRST",    tORDINAL,        1 },
1077 /*{ "SECOND",   tORDINAL,        2 }, */
1078   { "THIRD",    tORDINAL,        3 },
1079   { "FOURTH",   tORDINAL,        4 },
1080   { "FIFTH",    tORDINAL,        5 },
1081   { "SIXTH",    tORDINAL,        6 },
1082   { "SEVENTH",  tORDINAL,        7 },
1083   { "EIGHTH",   tORDINAL,        8 },
1084   { "NINTH",    tORDINAL,        9 },
1085   { "TENTH",    tORDINAL,       10 },
1086   { "ELEVENTH", tORDINAL,       11 },
1087   { "TWELFTH",  tORDINAL,       12 },
1088   { "AGO",      tAGO,           -1 },
1089   { "HENCE",    tAGO,            1 },
1090   { NULL, 0, 0 }
1091 };
1092 
1093 /* The universal time zone table.  These labels can be used even for
1094    timestamps that would not otherwise be valid, e.g., GMT timestamps
1095    oin London during summer.  */
1096 static table const universal_time_zone_table[] =
1097 {
1098   { "GMT",      tZONE,     HOUR ( 0) }, /* Greenwich Mean */
1099   { "UT",       tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
1100   { "UTC",      tZONE,     HOUR ( 0) },
1101   { NULL, 0, 0 }
1102 };
1103 
1104 /* The time zone table.  This table is necessarily incomplete, as time
1105    zone abbreviations are ambiguous; e.g., Australians interpret "EST"
1106    as Eastern time in Australia, not as US Eastern Standard Time.
1107    You cannot rely on parse_datetime to handle arbitrary time zone
1108    abbreviations; use numeric abbreviations like "-0500" instead.  */
1109 static table const time_zone_table[] =
1110 {
1111   { "WET",      tZONE,     HOUR ( 0) }, /* Western European */
1112   { "WEST",     tDAYZONE,  HOUR ( 0) }, /* Western European Summer */
1113   { "BST",      tDAYZONE,  HOUR ( 0) }, /* British Summer */
1114   { "ART",      tZONE,    -HOUR ( 3) }, /* Argentina */
1115   { "BRT",      tZONE,    -HOUR ( 3) }, /* Brazil */
1116   { "BRST",     tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
1117   { "NST",      tZONE,   -(HOUR ( 3) + 30 * 60) }, /* Newfoundland Standard */
1118   { "NDT",      tDAYZONE,-(HOUR ( 3) + 30 * 60) }, /* Newfoundland Daylight */
1119   { "AST",      tZONE,    -HOUR ( 4) }, /* Atlantic Standard */
1120   { "ADT",      tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
1121   { "CLT",      tZONE,    -HOUR ( 4) }, /* Chile */
1122   { "CLST",     tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
1123   { "EST",      tZONE,    -HOUR ( 5) }, /* Eastern Standard */
1124   { "EDT",      tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
1125   { "CST",      tZONE,    -HOUR ( 6) }, /* Central Standard */
1126   { "CDT",      tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
1127   { "MST",      tZONE,    -HOUR ( 7) }, /* Mountain Standard */
1128   { "MDT",      tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
1129   { "PST",      tZONE,    -HOUR ( 8) }, /* Pacific Standard */
1130   { "PDT",      tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
1131   { "AKST",     tZONE,    -HOUR ( 9) }, /* Alaska Standard */
1132   { "AKDT",     tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
1133   { "HST",      tZONE,    -HOUR (10) }, /* Hawaii Standard */
1134   { "HAST",     tZONE,    -HOUR (10) }, /* Hawaii-Aleutian Standard */
1135   { "HADT",     tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
1136   { "SST",      tZONE,    -HOUR (12) }, /* Samoa Standard */
1137   { "WAT",      tZONE,     HOUR ( 1) }, /* West Africa */
1138   { "CET",      tZONE,     HOUR ( 1) }, /* Central European */
1139   { "CEST",     tDAYZONE,  HOUR ( 1) }, /* Central European Summer */
1140   { "MET",      tZONE,     HOUR ( 1) }, /* Middle European */
1141   { "MEZ",      tZONE,     HOUR ( 1) }, /* Middle European */
1142   { "MEST",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
1143   { "MESZ",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
1144   { "EET",      tZONE,     HOUR ( 2) }, /* Eastern European */
1145   { "EEST",     tDAYZONE,  HOUR ( 2) }, /* Eastern European Summer */
1146   { "CAT",      tZONE,     HOUR ( 2) }, /* Central Africa */
1147   { "SAST",     tZONE,     HOUR ( 2) }, /* South Africa Standard */
1148   { "EAT",      tZONE,     HOUR ( 3) }, /* East Africa */
1149   { "MSK",      tZONE,     HOUR ( 3) }, /* Moscow */
1150   { "MSD",      tDAYZONE,  HOUR ( 3) }, /* Moscow Daylight */
1151   { "IST",      tZONE,    (HOUR ( 5) + 30 * 60) }, /* India Standard */
1152   { "SGT",      tZONE,     HOUR ( 8) }, /* Singapore */
1153   { "KST",      tZONE,     HOUR ( 9) }, /* Korea Standard */
1154   { "JST",      tZONE,     HOUR ( 9) }, /* Japan Standard */
1155   { "GST",      tZONE,     HOUR (10) }, /* Guam Standard */
1156   { "NZST",     tZONE,     HOUR (12) }, /* New Zealand Standard */
1157   { "NZDT",     tDAYZONE,  HOUR (12) }, /* New Zealand Daylight */
1158   { NULL, 0, 0 }
1159 };
1160 
1161 /* Military time zone table.
1162 
1163    Note 'T' is a special case, as it is used as the separator in ISO
1164    8601 date and time of day representation.  */
1165 static table const military_table[] =
1166 {
1167   { "A", tZONE, -HOUR ( 1) },
1168   { "B", tZONE, -HOUR ( 2) },
1169   { "C", tZONE, -HOUR ( 3) },
1170   { "D", tZONE, -HOUR ( 4) },
1171   { "E", tZONE, -HOUR ( 5) },
1172   { "F", tZONE, -HOUR ( 6) },
1173   { "G", tZONE, -HOUR ( 7) },
1174   { "H", tZONE, -HOUR ( 8) },
1175   { "I", tZONE, -HOUR ( 9) },
1176   { "K", tZONE, -HOUR (10) },
1177   { "L", tZONE, -HOUR (11) },
1178   { "M", tZONE, -HOUR (12) },
1179   { "N", tZONE,  HOUR ( 1) },
1180   { "O", tZONE,  HOUR ( 2) },
1181   { "P", tZONE,  HOUR ( 3) },
1182   { "Q", tZONE,  HOUR ( 4) },
1183   { "R", tZONE,  HOUR ( 5) },
1184   { "S", tZONE,  HOUR ( 6) },
1185   { "T", 'T',    0 },
1186   { "U", tZONE,  HOUR ( 8) },
1187   { "V", tZONE,  HOUR ( 9) },
1188   { "W", tZONE,  HOUR (10) },
1189   { "X", tZONE,  HOUR (11) },
1190   { "Y", tZONE,  HOUR (12) },
1191   { "Z", tZONE,  HOUR ( 0) },
1192   { NULL, 0, 0 }
1193 };
1194 
1195 
1196 
1197 /* Convert a time zone expressed as HH:MM into an integer count of
1198    seconds.  If MM is negative, then S is of the form HHMM and needs
1199    to be picked apart; otherwise, S is of the form HH.  As specified in
1200    http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
1201    only valid TZ range, and consider first two digits as hours, if no
1202    minutes specified.  Return true if successful.  */
1203 
1204 static bool
time_zone_hhmm(parser_control * pc,textint s,intmax_t mm)1205 time_zone_hhmm (parser_control *pc, textint s, intmax_t mm)
1206 {
1207   intmax_t n_minutes;
1208   bool overflow = false;
1209 
1210   /* If the length of S is 1 or 2 and no minutes are specified,
1211      interpret it as a number of hours.  */
1212   if (s.digits <= 2 && mm < 0)
1213     s.value *= 100;
1214 
1215   if (mm < 0)
1216     n_minutes = (s.value / 100) * 60 + s.value % 100;
1217   else
1218     {
1219       overflow |= INT_MULTIPLY_WRAPV (s.value, 60, &n_minutes);
1220       overflow |= (s.negative
1221                    ? INT_SUBTRACT_WRAPV (n_minutes, mm, &n_minutes)
1222                    : INT_ADD_WRAPV (n_minutes, mm, &n_minutes));
1223     }
1224 
1225   if (overflow || ! (-24 * 60 <= n_minutes && n_minutes <= 24 * 60))
1226     return false;
1227   pc->time_zone = n_minutes * 60;
1228   return true;
1229 }
1230 
1231 static int
to_hour(intmax_t hours,int meridian)1232 to_hour (intmax_t hours, int meridian)
1233 {
1234   switch (meridian)
1235     {
1236     default: /* Pacify GCC.  */
1237     case MER24:
1238       return 0 <= hours && hours < 24 ? hours : -1;
1239     case MERam:
1240       return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
1241     case MERpm:
1242       return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
1243     }
1244 }
1245 
1246 enum { TM_YEAR_BASE = 1900 };
1247 enum { TM_YEAR_BUFSIZE = INT_BUFSIZE_BOUND (int) + 1 };
1248 
1249 /* Convert TM_YEAR, a year minus 1900, to a string that is numerically
1250    correct even if subtracting 1900 would overflow.  */
1251 
1252 static char const *
tm_year_str(int tm_year,char buf[TM_YEAR_BUFSIZE])1253 tm_year_str (int tm_year, char buf[TM_YEAR_BUFSIZE])
1254 {
1255   verify (TM_YEAR_BASE % 100 == 0);
1256   sprintf (buf, &"-%02d%02d"[-TM_YEAR_BASE <= tm_year],
1257            abs (tm_year / 100 + TM_YEAR_BASE / 100),
1258            abs (tm_year % 100));
1259   return buf;
1260 }
1261 
1262 /* Convert a text year number to a year minus 1900, working correctly
1263    even if the input is in the range INT_MAX .. INT_MAX + 1900 - 1.  */
1264 
1265 static bool
to_tm_year(textint textyear,bool debug,int * tm_year)1266 to_tm_year (textint textyear, bool debug, int *tm_year)
1267 {
1268   intmax_t year = textyear.value;
1269 
1270   /* XPG4 suggests that years 00-68 map to 2000-2068, and
1271      years 69-99 map to 1969-1999.  */
1272   if (0 <= year && textyear.digits == 2)
1273     {
1274       year += year < 69 ? 2000 : 1900;
1275       if (debug)
1276         dbg_printf (_("warning: adjusting year value %"PRIdMAX
1277                       " to %"PRIdMAX"\n"),
1278                     textyear.value, year);
1279     }
1280 
1281   if (year < 0
1282       ? INT_SUBTRACT_WRAPV (-TM_YEAR_BASE, year, tm_year)
1283       : INT_SUBTRACT_WRAPV (year, TM_YEAR_BASE, tm_year))
1284     {
1285       if (debug)
1286         dbg_printf (_("error: out-of-range year %"PRIdMAX"\n"), year);
1287       return false;
1288     }
1289 
1290   return true;
1291 }
1292 
1293 static table const * _GL_ATTRIBUTE_PURE
lookup_zone(parser_control const * pc,char const * name)1294 lookup_zone (parser_control const *pc, char const *name)
1295 {
1296   table const *tp;
1297 
1298   for (tp = universal_time_zone_table; tp->name; tp++)
1299     if (strcmp (name, tp->name) == 0)
1300       return tp;
1301 
1302   /* Try local zone abbreviations before those in time_zone_table, as
1303      the local ones are more likely to be right.  */
1304   for (tp = pc->local_time_zone_table; tp->name; tp++)
1305     if (strcmp (name, tp->name) == 0)
1306       return tp;
1307 
1308   for (tp = time_zone_table; tp->name; tp++)
1309     if (strcmp (name, tp->name) == 0)
1310       return tp;
1311 
1312   return NULL;
1313 }
1314 
1315 #if ! HAVE_TM_GMTOFF
1316 /* Yield the difference between *A and *B,
1317    measured in seconds, ignoring leap seconds.
1318    The body of this function is taken directly from the GNU C Library;
1319    see strftime.c.  */
1320 static int
tm_diff(const struct tm * a,const struct tm * b)1321 tm_diff (const struct tm *a, const struct tm *b)
1322 {
1323   /* Compute intervening leap days correctly even if year is negative.
1324      Take care to avoid int overflow in leap day calculations,
1325      but it's OK to assume that A and B are close to each other.  */
1326   int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
1327   int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
1328   int a100 = a4 / 25 - (a4 % 25 < 0);
1329   int b100 = b4 / 25 - (b4 % 25 < 0);
1330   int a400 = SHR (a100, 2);
1331   int b400 = SHR (b100, 2);
1332   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
1333   int years = a->tm_year - b->tm_year;
1334   int days = (365 * years + intervening_leap_days
1335               + (a->tm_yday - b->tm_yday));
1336   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
1337                 + (a->tm_min - b->tm_min))
1338           + (a->tm_sec - b->tm_sec));
1339 }
1340 #endif /* ! HAVE_TM_GMTOFF */
1341 
1342 static table const *
lookup_word(parser_control const * pc,char * word)1343 lookup_word (parser_control const *pc, char *word)
1344 {
1345   char *p;
1346   char *q;
1347   ptrdiff_t wordlen;
1348   table const *tp;
1349   bool period_found;
1350   bool abbrev;
1351 
1352   /* Make it uppercase.  */
1353   for (p = word; *p; p++)
1354     *p = c_toupper (to_uchar (*p));
1355 
1356   for (tp = meridian_table; tp->name; tp++)
1357     if (strcmp (word, tp->name) == 0)
1358       return tp;
1359 
1360   /* See if we have an abbreviation for a month.  */
1361   wordlen = strlen (word);
1362   abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
1363 
1364   for (tp = month_and_day_table; tp->name; tp++)
1365     if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
1366       return tp;
1367 
1368   if ((tp = lookup_zone (pc, word)))
1369     return tp;
1370 
1371   if (strcmp (word, dst_table[0].name) == 0)
1372     return dst_table;
1373 
1374   for (tp = time_units_table; tp->name; tp++)
1375     if (strcmp (word, tp->name) == 0)
1376       return tp;
1377 
1378   /* Strip off any plural and try the units table again.  */
1379   if (word[wordlen - 1] == 'S')
1380     {
1381       word[wordlen - 1] = '\0';
1382       for (tp = time_units_table; tp->name; tp++)
1383         if (strcmp (word, tp->name) == 0)
1384           return tp;
1385       word[wordlen - 1] = 'S';  /* For "this" in relative_time_table.  */
1386     }
1387 
1388   for (tp = relative_time_table; tp->name; tp++)
1389     if (strcmp (word, tp->name) == 0)
1390       return tp;
1391 
1392   /* Military time zones.  */
1393   if (wordlen == 1)
1394     for (tp = military_table; tp->name; tp++)
1395       if (word[0] == tp->name[0])
1396         return tp;
1397 
1398   /* Drop out any periods and try the time zone table again.  */
1399   for (period_found = false, p = q = word; (*p = *q); q++)
1400     if (*q == '.')
1401       period_found = true;
1402     else
1403       p++;
1404   if (period_found && (tp = lookup_zone (pc, word)))
1405     return tp;
1406 
1407   return NULL;
1408 }
1409 
1410 static int
yylex(union YYSTYPE * lvalp,parser_control * pc)1411 yylex (union YYSTYPE *lvalp, parser_control *pc)
1412 {
1413   unsigned char c;
1414 
1415   for (;;)
1416     {
1417       while (c = *pc->input, c_isspace (c))
1418         pc->input++;
1419 
1420       if (c_isdigit (c) || c == '-' || c == '+')
1421         {
1422           char const *p;
1423           int sign;
1424           intmax_t value = 0;
1425           if (c == '-' || c == '+')
1426             {
1427               sign = c == '-' ? -1 : 1;
1428               while (c = *++pc->input, c_isspace (c))
1429                 continue;
1430               if (! c_isdigit (c))
1431                 /* skip the '-' sign */
1432                 continue;
1433             }
1434           else
1435             sign = 0;
1436           p = pc->input;
1437 
1438           do
1439             {
1440               if (INT_MULTIPLY_WRAPV (value, 10, &value))
1441                 return '?';
1442               if (INT_ADD_WRAPV (value, sign < 0 ? '0' - c : c - '0', &value))
1443                 return '?';
1444               c = *++p;
1445             }
1446           while (c_isdigit (c));
1447 
1448           if ((c == '.' || c == ',') && c_isdigit (p[1]))
1449             {
1450               time_t s;
1451               int ns;
1452               int digits;
1453 
1454               if (time_overflow (value))
1455                 return '?';
1456               s = value;
1457 
1458               /* Accumulate fraction, to ns precision.  */
1459               p++;
1460               ns = *p++ - '0';
1461               for (digits = 2; digits <= LOG10_BILLION; digits++)
1462                 {
1463                   ns *= 10;
1464                   if (c_isdigit (*p))
1465                     ns += *p++ - '0';
1466                 }
1467 
1468               /* Skip excess digits, truncating toward -Infinity.  */
1469               if (sign < 0)
1470                 for (; c_isdigit (*p); p++)
1471                   if (*p != '0')
1472                     {
1473                       ns++;
1474                       break;
1475                     }
1476               while (c_isdigit (*p))
1477                 p++;
1478 
1479               /* Adjust to the timespec convention, which is that
1480                  tv_nsec is always a positive offset even if tv_sec is
1481                  negative.  */
1482               if (sign < 0 && ns)
1483                 {
1484                   if (s == TYPE_MINIMUM (time_t))
1485                     return '?';
1486                   s--;
1487                   ns = BILLION - ns;
1488                 }
1489 
1490               lvalp->timespec.tv_sec = s;
1491               lvalp->timespec.tv_nsec = ns;
1492               pc->input = p;
1493               return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1494             }
1495           else
1496             {
1497               lvalp->textintval.negative = sign < 0;
1498               lvalp->textintval.value = value;
1499               lvalp->textintval.digits = p - pc->input;
1500               pc->input = p;
1501               return sign ? tSNUMBER : tUNUMBER;
1502             }
1503         }
1504 
1505       if (c_isalpha (c))
1506         {
1507           char buff[20];
1508           char *p = buff;
1509           table const *tp;
1510 
1511           do
1512             {
1513               if (p < buff + sizeof buff - 1)
1514                 *p++ = c;
1515               c = *++pc->input;
1516             }
1517           while (c_isalpha (c) || c == '.');
1518 
1519           *p = '\0';
1520           tp = lookup_word (pc, buff);
1521           if (! tp)
1522             {
1523               if (pc->parse_datetime_debug)
1524                 dbg_printf (_("error: unknown word '%s'\n"), buff);
1525               return '?';
1526             }
1527           lvalp->intval = tp->value;
1528           return tp->type;
1529         }
1530 
1531       if (c != '(')
1532         return to_uchar (*pc->input++);
1533 
1534       ptrdiff_t count = 0;
1535       do
1536         {
1537           c = *pc->input++;
1538           if (c == '\0')
1539             return c;
1540           if (c == '(')
1541             count++;
1542           else if (c == ')')
1543             count--;
1544         }
1545       while (count != 0);
1546     }
1547 }
1548 
1549 /* Do nothing if the parser reports an error.  */
1550 static int
yyerror(parser_control const * pc _GL_UNUSED,char const * s _GL_UNUSED)1551 yyerror (parser_control const *pc _GL_UNUSED,
1552          char const *s _GL_UNUSED)
1553 {
1554   return 0;
1555 }
1556 
1557 /* In timezone TZ, if *TM0 is the old and *TM1 is the new value of a
1558    struct tm after passing it to mktime_z, return true if it's OK that
1559    mktime_z returned T.  It's not OK if *TM0 has out-of-range
1560    members.  */
1561 
1562 static bool
mktime_ok(timezone_t tz,struct tm const * tm0,struct tm const * tm1,time_t t)1563 mktime_ok (timezone_t tz, struct tm const *tm0, struct tm const *tm1, time_t t)
1564 {
1565   struct tm ltm;
1566   if (t == (time_t) -1)
1567     {
1568       /* Guard against falsely reporting an error when parsing a
1569          timestamp that happens to equal (time_t) -1, on a host that
1570          supports such a timestamp.  */
1571       tm1 = localtime_rz (tz, &t, &ltm);
1572       if (!tm1)
1573         return false;
1574     }
1575 
1576   return ! ((tm0->tm_sec ^ tm1->tm_sec)
1577             | (tm0->tm_min ^ tm1->tm_min)
1578             | (tm0->tm_hour ^ tm1->tm_hour)
1579             | (tm0->tm_mday ^ tm1->tm_mday)
1580             | (tm0->tm_mon ^ tm1->tm_mon)
1581             | (tm0->tm_year ^ tm1->tm_year));
1582 }
1583 
1584 /* Debugging: format a 'struct tm' into a buffer, taking the parser's
1585    timezone information into account (if pc != NULL).  */
1586 static char const *
debug_strfdatetime(struct tm const * tm,parser_control const * pc,char * buf,int n)1587 debug_strfdatetime (struct tm const *tm, parser_control const *pc,
1588                     char *buf, int n)
1589 {
1590   /* TODO:
1591      1. find an optimal way to print date string in a clear and unambiguous
1592         format.  Currently, always add '(Y-M-D)' prefix.
1593         Consider '2016y01m10d'  or 'year(2016) month(01) day(10)'.
1594 
1595         If the user needs debug printing, it means he/she already having
1596         issues with the parsing - better to avoid formats that could
1597         be mis-interpreted (e.g., just YYYY-MM-DD).
1598 
1599      2. Can strftime be used instead?
1600         depends if it is portable and can print invalid dates on all systems.
1601 
1602      3. Print timezone information ?
1603 
1604      4. Print DST information ?
1605 
1606      5. Print nanosecond information ?
1607 
1608      NOTE:
1609      Printed date/time values might not be valid, e.g., '2016-02-31'
1610      or '2016-19-2016' .  These are the values as parsed from the user
1611      string, before validation.
1612   */
1613   int m = nstrftime (buf, n, "(Y-M-D) %Y-%m-%d %H:%M:%S", tm, 0, 0);
1614 
1615   /* If parser_control information was provided (for timezone),
1616      and there's enough space in the buffer, add timezone info.  */
1617   if (pc && m < n && pc->zones_seen)
1618     {
1619       int tz = pc->time_zone;
1620 
1621       /* Account for DST if tLOCAL_ZONE was seen.  */
1622       if (pc->local_zones_seen && !pc->zones_seen && 0 < pc->local_isdst)
1623         tz += 60 * 60;
1624 
1625       char time_zone_buf[TIME_ZONE_BUFSIZE];
1626       snprintf (&buf[m], n - m, " TZ=%s", time_zone_str (tz, time_zone_buf));
1627     }
1628   return buf;
1629 }
1630 
1631 static char const *
debug_strfdate(struct tm const * tm,char * buf,int n)1632 debug_strfdate (struct tm const *tm, char *buf, int n)
1633 {
1634   char tm_year_buf[TM_YEAR_BUFSIZE];
1635   snprintf (buf, n, "(Y-M-D) %s-%02d-%02d",
1636             tm_year_str (tm->tm_year, tm_year_buf),
1637             tm->tm_mon + 1, tm->tm_mday);
1638   return buf;
1639 }
1640 
1641 static char const *
debug_strftime(struct tm const * tm,char * buf,int n)1642 debug_strftime (struct tm const *tm, char *buf, int n)
1643 {
1644   snprintf (buf, n, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
1645   return buf;
1646 }
1647 
1648 /* If mktime_ok failed, display the failed time values,
1649    and provide possible hints.  Example output:
1650 
1651     date: error: invalid date/time value:
1652     date:     user provided time: '(Y-M-D) 2006-04-02 02:45:00'
1653     date:        normalized time: '(Y-M-D) 2006-04-02 03:45:00'
1654     date:                                             __
1655     date:      possible reasons:
1656     date:        non-existing due to daylight-saving time;
1657     date:        numeric values overflow;
1658     date:        missing timezone;
1659  */
1660 static void
debug_mktime_not_ok(struct tm const * tm0,struct tm const * tm1,parser_control const * pc,bool time_zone_seen)1661 debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1,
1662                      parser_control const *pc, bool time_zone_seen)
1663 {
1664   /* TODO: handle t == -1 (as in 'mktime_ok').  */
1665   char tmp[DBGBUFSIZE];
1666   int i;
1667   const bool eq_sec   = (tm0->tm_sec  == tm1->tm_sec);
1668   const bool eq_min   = (tm0->tm_min  == tm1->tm_min);
1669   const bool eq_hour  = (tm0->tm_hour == tm1->tm_hour);
1670   const bool eq_mday  = (tm0->tm_mday == tm1->tm_mday);
1671   const bool eq_month = (tm0->tm_mon  == tm1->tm_mon);
1672   const bool eq_year  = (tm0->tm_year == tm1->tm_year);
1673 
1674   const bool dst_shift = eq_sec && eq_min && !eq_hour
1675                          && eq_mday && eq_month && eq_year;
1676 
1677   if (!pc->parse_datetime_debug)
1678     return;
1679 
1680   dbg_printf (_("error: invalid date/time value:\n"));
1681   dbg_printf (_("    user provided time: '%s'\n"),
1682               debug_strfdatetime (tm0, pc, tmp, sizeof tmp));
1683   dbg_printf (_("       normalized time: '%s'\n"),
1684               debug_strfdatetime (tm1, pc, tmp, sizeof tmp));
1685   /* The format must be aligned with debug_strfdatetime and the two
1686      DEBUG statements above.  This string is not translated.  */
1687   i = snprintf (tmp, sizeof tmp,
1688                 "                                 %4s %2s %2s %2s %2s %2s",
1689                 eq_year ? "" : "----",
1690                 eq_month ? "" : "--",
1691                 eq_mday ? "" : "--",
1692                 eq_hour ? "" : "--",
1693                 eq_min ? "" : "--",
1694                 eq_sec ? "" : "--");
1695   /* Trim trailing whitespace.  */
1696   if (0 <= i)
1697     {
1698       if (sizeof tmp - 1 < i)
1699         i = sizeof tmp - 1;
1700       while (0 < i && tmp[i - 1] == ' ')
1701         --i;
1702       tmp[i] = '\0';
1703     }
1704   dbg_printf ("%s\n", tmp);
1705 
1706   dbg_printf (_("     possible reasons:\n"));
1707   if (dst_shift)
1708     dbg_printf (_("       non-existing due to daylight-saving time;\n"));
1709   if (!eq_mday && !eq_month)
1710     dbg_printf (_("       invalid day/month combination;\n"));
1711   dbg_printf (_("       numeric values overflow;\n"));
1712   dbg_printf ("       %s\n", (time_zone_seen ? _("incorrect timezone")
1713                               : _("missing timezone")));
1714 }
1715 
1716 /* The original interface: run with debug=false and the default timezone.   */
1717 bool
parse_datetime(struct timespec * result,char const * p,struct timespec const * now)1718 parse_datetime (struct timespec *result, char const *p,
1719                 struct timespec const *now)
1720 {
1721   char const *tzstring = getenv ("TZ");
1722   timezone_t tz = tzalloc (tzstring);
1723   if (!tz)
1724     return false;
1725   bool ok = parse_datetime2 (result, p, now, 0, tz, tzstring);
1726   tzfree (tz);
1727   return ok;
1728 }
1729 
1730 /* Parse a date/time string, storing the resulting time value into *RESULT.
1731    The string itself is pointed to by P.  Return true if successful.
1732    P can be an incomplete or relative time specification; if so, use
1733    *NOW as the basis for the returned time.  Default to timezone
1734    TZDEFAULT, which corresponds to tzalloc (TZSTRING).  */
1735 bool
parse_datetime2(struct timespec * result,char const * p,struct timespec const * now,unsigned int flags,timezone_t tzdefault,char const * tzstring)1736 parse_datetime2 (struct timespec *result, char const *p,
1737                  struct timespec const *now, unsigned int flags,
1738                  timezone_t tzdefault, char const *tzstring)
1739 {
1740   struct tm tm;
1741   struct tm tm0;
1742   char time_zone_buf[TIME_ZONE_BUFSIZE];
1743   char dbg_tm[DBGBUFSIZE];
1744   bool ok = false;
1745   char const *input_sentinel = p + strlen (p);
1746   char *tz1alloc = NULL;
1747 
1748   /* A reasonable upper bound for the size of ordinary TZ strings.
1749      Use heap allocation if TZ's length exceeds this.  */
1750   enum { TZBUFSIZE = 100 };
1751   char tz1buf[TZBUFSIZE];
1752 
1753   struct timespec gettime_buffer;
1754   if (! now)
1755     {
1756       gettime (&gettime_buffer);
1757       now = &gettime_buffer;
1758     }
1759 
1760   time_t Start = now->tv_sec;
1761   int Start_ns = now->tv_nsec;
1762 
1763   unsigned char c;
1764   while (c = *p, c_isspace (c))
1765     p++;
1766 
1767   timezone_t tz = tzdefault;
1768 
1769   /* Store a local copy prior to first "goto".  Without this, a prior use
1770      below of RELATIVE_TIME_0 on the RHS might translate to an assignment-
1771      to-temporary, which would trigger a -Wjump-misses-init warning.  */
1772   const relative_time rel_time_0 = RELATIVE_TIME_0;
1773 
1774   if (strncmp (p, "TZ=\"", 4) == 0)
1775     {
1776       char const *tzbase = p + 4;
1777       ptrdiff_t tzsize = 1;
1778       char const *s;
1779 
1780       for (s = tzbase; *s; s++, tzsize++)
1781         if (*s == '\\')
1782           {
1783             s++;
1784             if (! (*s == '\\' || *s == '"'))
1785               break;
1786           }
1787         else if (*s == '"')
1788           {
1789             timezone_t tz1;
1790             char *tz1string = tz1buf;
1791             char *z;
1792             if (TZBUFSIZE < tzsize)
1793               {
1794                 tz1alloc = malloc (tzsize);
1795                 if (!tz1alloc)
1796                   goto fail;
1797                 tz1string = tz1alloc;
1798               }
1799             z = tz1string;
1800             for (s = tzbase; *s != '"'; s++)
1801               *z++ = *(s += *s == '\\');
1802             *z = '\0';
1803             tz1 = tzalloc (tz1string);
1804             if (!tz1)
1805               goto fail;
1806             tz = tz1;
1807             tzstring = tz1string;
1808 
1809             p = s + 1;
1810             while (c = *p, c_isspace (c))
1811               p++;
1812 
1813             break;
1814           }
1815     }
1816 
1817   struct tm tmp;
1818   if (! localtime_rz (tz, &now->tv_sec, &tmp))
1819     goto fail;
1820 
1821   /* As documented, be careful to treat the empty string just like
1822      a date string of "0".  Without this, an empty string would be
1823      declared invalid when parsed during a DST transition.  */
1824   if (*p == '\0')
1825     p = "0";
1826 
1827   parser_control pc;
1828   pc.input = p;
1829   pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG) != 0;
1830   if (INT_ADD_WRAPV (tmp.tm_year, TM_YEAR_BASE, &pc.year.value))
1831     {
1832       if (pc.parse_datetime_debug)
1833         dbg_printf (_("error: initial year out of range\n"));
1834       goto fail;
1835     }
1836   pc.year.digits = 0;
1837   pc.month = tmp.tm_mon + 1;
1838   pc.day = tmp.tm_mday;
1839   pc.hour = tmp.tm_hour;
1840   pc.minutes = tmp.tm_min;
1841   pc.seconds.tv_sec = tmp.tm_sec;
1842   pc.seconds.tv_nsec = Start_ns;
1843   tm.tm_isdst = tmp.tm_isdst;
1844 
1845   pc.meridian = MER24;
1846   pc.rel = rel_time_0;
1847   pc.timespec_seen = false;
1848   pc.rels_seen = false;
1849   pc.dates_seen = 0;
1850   pc.days_seen = 0;
1851   pc.times_seen = 0;
1852   pc.local_zones_seen = 0;
1853   pc.dsts_seen = 0;
1854   pc.zones_seen = 0;
1855   pc.year_seen = false;
1856   pc.debug_dates_seen = false;
1857   pc.debug_days_seen = false;
1858   pc.debug_times_seen = false;
1859   pc.debug_local_zones_seen = false;
1860   pc.debug_zones_seen = false;
1861   pc.debug_year_seen = false;
1862   pc.debug_ordinal_day_seen = false;
1863 
1864 #if HAVE_STRUCT_TM_TM_ZONE
1865   pc.local_time_zone_table[0].name = tmp.tm_zone;
1866   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1867   pc.local_time_zone_table[0].value = tmp.tm_isdst;
1868   pc.local_time_zone_table[1].name = NULL;
1869 
1870   /* Probe the names used in the next three calendar quarters, looking
1871      for a tm_isdst different from the one we already have.  */
1872   {
1873     int quarter;
1874     for (quarter = 1; quarter <= 3; quarter++)
1875       {
1876         intmax_t iprobe;
1877         if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &iprobe)
1878             || time_overflow (iprobe))
1879           break;
1880         time_t probe = iprobe;
1881         struct tm probe_tm;
1882         if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone
1883             && probe_tm.tm_isdst != pc.local_time_zone_table[0].value)
1884           {
1885               {
1886                 pc.local_time_zone_table[1].name = probe_tm.tm_zone;
1887                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1888                 pc.local_time_zone_table[1].value = probe_tm.tm_isdst;
1889                 pc.local_time_zone_table[2].name = NULL;
1890               }
1891             break;
1892           }
1893       }
1894   }
1895 #else
1896 #if HAVE_TZNAME
1897   {
1898 # if !HAVE_DECL_TZNAME
1899     extern char *tzname[];
1900 # endif
1901     int i;
1902     for (i = 0; i < 2; i++)
1903       {
1904         pc.local_time_zone_table[i].name = tzname[i];
1905         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1906         pc.local_time_zone_table[i].value = i;
1907       }
1908     pc.local_time_zone_table[i].name = NULL;
1909   }
1910 #else
1911   pc.local_time_zone_table[0].name = NULL;
1912 #endif
1913 #endif
1914 
1915   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1916       && ! strcmp (pc.local_time_zone_table[0].name,
1917                    pc.local_time_zone_table[1].name))
1918     {
1919       /* This locale uses the same abbreviation for standard and
1920          daylight times.  So if we see that abbreviation, we don't
1921          know whether it's daylight time.  */
1922       pc.local_time_zone_table[0].value = -1;
1923       pc.local_time_zone_table[1].name = NULL;
1924     }
1925 
1926   if (yyparse (&pc) != 0)
1927     {
1928       if (pc.parse_datetime_debug)
1929         dbg_printf ((input_sentinel <= pc.input
1930                      ? _("error: parsing failed\n")
1931                      : _("error: parsing failed, stopped at '%s'\n")),
1932                     pc.input);
1933       goto fail;
1934     }
1935 
1936 
1937   /* Determine effective timezone source.  */
1938 
1939   if (pc.parse_datetime_debug)
1940     {
1941       dbg_printf (_("input timezone: "));
1942 
1943       if (pc.timespec_seen)
1944         fprintf (stderr, _("'@timespec' - always UTC"));
1945       else if (pc.zones_seen)
1946         fprintf (stderr, _("parsed date/time string"));
1947       else if (tzstring)
1948         {
1949           if (tz != tzdefault)
1950             fprintf (stderr, _("TZ=\"%s\" in date string"), tzstring);
1951           else if (STREQ (tzstring, "UTC0"))
1952             {
1953               /* Special case: 'date -u' sets TZ="UTC0".  */
1954               fprintf (stderr, _("TZ=\"UTC0\" environment value or -u"));
1955             }
1956           else
1957             fprintf (stderr, _("TZ=\"%s\" environment value"), tzstring);
1958         }
1959       else
1960         fprintf (stderr, _("system default"));
1961 
1962       /* Account for DST changes if tLOCAL_ZONE was seen.
1963          local timezone only changes DST and is relative to the
1964          default timezone.*/
1965       if (pc.local_zones_seen && !pc.zones_seen && 0 < pc.local_isdst)
1966         fprintf (stderr, ", dst");
1967 
1968       if (pc.zones_seen)
1969         fprintf (stderr, " (%s)", time_zone_str (pc.time_zone, time_zone_buf));
1970 
1971       fputc ('\n', stderr);
1972     }
1973 
1974   if (pc.timespec_seen)
1975     *result = pc.seconds;
1976   else
1977     {
1978       if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1979                | (pc.local_zones_seen + pc.zones_seen)))
1980         {
1981           if (pc.parse_datetime_debug)
1982             {
1983               if (pc.times_seen > 1)
1984                 dbg_printf ("error: seen multiple time parts\n");
1985               if (pc.dates_seen > 1)
1986                 dbg_printf ("error: seen multiple date parts\n");
1987               if (pc.days_seen > 1)
1988                 dbg_printf ("error: seen multiple days parts\n");
1989               if (pc.dsts_seen > 1)
1990                 dbg_printf ("error: seen multiple daylight-saving parts\n");
1991               if ((pc.local_zones_seen + pc.zones_seen) > 1)
1992                 dbg_printf ("error: seen multiple time-zone parts\n");
1993             }
1994           goto fail;
1995         }
1996 
1997       if (! to_tm_year (pc.year, pc.parse_datetime_debug, &tm.tm_year)
1998           || INT_ADD_WRAPV (pc.month, -1, &tm.tm_mon)
1999           || INT_ADD_WRAPV (pc.day, 0, &tm.tm_mday))
2000         {
2001           if (pc.parse_datetime_debug)
2002             dbg_printf (_("error: year, month, or day overflow\n"));
2003           goto fail;
2004         }
2005       if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
2006         {
2007           tm.tm_hour = to_hour (pc.hour, pc.meridian);
2008           if (tm.tm_hour < 0)
2009             {
2010               char const *mrd = (pc.meridian == MERam ? "am"
2011                                  : pc.meridian == MERpm ?"pm" : "");
2012               if (pc.parse_datetime_debug)
2013                 dbg_printf (_("error: invalid hour %"PRIdMAX"%s\n"),
2014                             pc.hour, mrd);
2015               goto fail;
2016             }
2017           tm.tm_min = pc.minutes;
2018           tm.tm_sec = pc.seconds.tv_sec;
2019           if (pc.parse_datetime_debug)
2020             dbg_printf ((pc.times_seen
2021                          ? _("using specified time as starting value: '%s'\n")
2022                          : _("using current time as starting value: '%s'\n")),
2023                         debug_strftime (&tm, dbg_tm, sizeof dbg_tm));
2024         }
2025       else
2026         {
2027           tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
2028           pc.seconds.tv_nsec = 0;
2029           if (pc.parse_datetime_debug)
2030             dbg_printf ("warning: using midnight as starting time: 00:00:00\n");
2031         }
2032 
2033       /* Let mktime deduce tm_isdst if we have an absolute timestamp.  */
2034       if (pc.dates_seen | pc.days_seen | pc.times_seen)
2035         tm.tm_isdst = -1;
2036 
2037       /* But if the input explicitly specifies local time with or without
2038          DST, give mktime that information.  */
2039       if (pc.local_zones_seen)
2040         tm.tm_isdst = pc.local_isdst;
2041 
2042       tm0.tm_sec = tm.tm_sec;
2043       tm0.tm_min = tm.tm_min;
2044       tm0.tm_hour = tm.tm_hour;
2045       tm0.tm_mday = tm.tm_mday;
2046       tm0.tm_mon = tm.tm_mon;
2047       tm0.tm_year = tm.tm_year;
2048       tm0.tm_isdst = tm.tm_isdst;
2049 
2050       Start = mktime_z (tz, &tm);
2051 
2052       if (! mktime_ok (tz, &tm0, &tm, Start))
2053         {
2054           bool repaired = false;
2055           bool time_zone_seen = pc.zones_seen != 0;
2056           if (time_zone_seen)
2057             {
2058               /* Guard against falsely reporting errors near the time_t
2059                  boundaries when parsing times in other time zones.  For
2060                  example, suppose the input string "1969-12-31 23:00:00 -0100",
2061                  the current time zone is 8 hours ahead of UTC, and the min
2062                  time_t value is 1970-01-01 00:00:00 UTC.  Then the min
2063                  localtime value is 1970-01-01 08:00:00, and mktime will
2064                  therefore fail on 1969-12-31 23:00:00.  To work around the
2065                  problem, set the time zone to 1 hour behind UTC temporarily
2066                  by setting TZ="XXX1:00" and try mktime again.  */
2067 
2068               char tz2buf[sizeof "XXX" - 1 + TIME_ZONE_BUFSIZE];
2069               tz2buf[0] = tz2buf[1] = tz2buf[2] = 'X';
2070               time_zone_str (pc.time_zone, &tz2buf[3]);
2071               timezone_t tz2 = tzalloc (tz2buf);
2072               if (!tz2)
2073                 {
2074                   if (pc.parse_datetime_debug)
2075                     dbg_printf (_("error: tzalloc (\"%s\") failed\n"), tz2buf);
2076                   goto fail;
2077                 }
2078               tm.tm_sec = tm0.tm_sec;
2079               tm.tm_min = tm0.tm_min;
2080               tm.tm_hour = tm0.tm_hour;
2081               tm.tm_mday = tm0.tm_mday;
2082               tm.tm_mon = tm0.tm_mon;
2083               tm.tm_year = tm0.tm_year;
2084               tm.tm_isdst = tm0.tm_isdst;
2085               Start = mktime_z (tz2, &tm);
2086               repaired = mktime_ok (tz2, &tm0, &tm, Start);
2087               tzfree (tz2);
2088             }
2089 
2090           if (! repaired)
2091             {
2092               debug_mktime_not_ok (&tm0, &tm, &pc, time_zone_seen);
2093               goto fail;
2094             }
2095         }
2096 
2097       char dbg_ord[DBGBUFSIZE];
2098 
2099       if (pc.days_seen && ! pc.dates_seen)
2100         {
2101           intmax_t dayincr;
2102           if (INT_MULTIPLY_WRAPV ((pc.day_ordinal
2103                                    - (0 < pc.day_ordinal
2104                                       && tm.tm_wday != pc.day_number)),
2105                                   7, &dayincr)
2106               || INT_ADD_WRAPV ((pc.day_number - tm.tm_wday + 7) % 7,
2107                                 dayincr, &dayincr)
2108               || INT_ADD_WRAPV (dayincr, tm.tm_mday, &tm.tm_mday))
2109             Start = -1;
2110           else
2111             {
2112               tm.tm_isdst = -1;
2113               Start = mktime_z (tz, &tm);
2114             }
2115 
2116           if (Start == (time_t) -1)
2117             {
2118               if (pc.parse_datetime_debug)
2119                 dbg_printf (_("error: day '%s' "
2120                               "(day ordinal=%"PRIdMAX" number=%d) "
2121                               "resulted in an invalid date: '%s'\n"),
2122                             str_days (&pc, dbg_ord, sizeof dbg_ord),
2123                             pc.day_ordinal, pc.day_number,
2124                             debug_strfdatetime (&tm, &pc, dbg_tm,
2125                                                 sizeof dbg_tm));
2126               goto fail;
2127             }
2128 
2129           if (pc.parse_datetime_debug)
2130             dbg_printf (_("new start date: '%s' is '%s'\n"),
2131                         str_days (&pc, dbg_ord, sizeof dbg_ord),
2132                         debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2133 
2134         }
2135 
2136       if (pc.parse_datetime_debug)
2137         {
2138           if (!pc.dates_seen && !pc.days_seen)
2139             dbg_printf (_("using current date as starting value: '%s'\n"),
2140                         debug_strfdate (&tm, dbg_tm, sizeof dbg_tm));
2141 
2142           if (pc.days_seen && pc.dates_seen)
2143             dbg_printf (_("warning: day (%s) ignored when explicit dates "
2144                           "are given\n"),
2145                         str_days (&pc, dbg_ord, sizeof dbg_ord));
2146 
2147           dbg_printf (_("starting date/time: '%s'\n"),
2148                       debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2149         }
2150 
2151       /* Add relative date.  */
2152       if (pc.rel.year | pc.rel.month | pc.rel.day)
2153         {
2154           if (pc.parse_datetime_debug)
2155             {
2156               if ((pc.rel.year != 0 || pc.rel.month != 0) && tm.tm_mday != 15)
2157                 dbg_printf (_("warning: when adding relative months/years, "
2158                               "it is recommended to specify the 15th of the "
2159                               "months\n"));
2160 
2161               if (pc.rel.day != 0 && tm.tm_hour != 12)
2162                 dbg_printf (_("warning: when adding relative days, "
2163                               "it is recommended to specify noon\n"));
2164             }
2165 
2166           int year, month, day;
2167           if (INT_ADD_WRAPV (tm.tm_year, pc.rel.year, &year)
2168               || INT_ADD_WRAPV (tm.tm_mon, pc.rel.month, &month)
2169               || INT_ADD_WRAPV (tm.tm_mday, pc.rel.day, &day))
2170             {
2171               if (pc.parse_datetime_debug)
2172                 dbg_printf (_("error: %s:%d\n"), __FILE__, __LINE__);
2173               goto fail;
2174             }
2175           tm.tm_year = year;
2176           tm.tm_mon = month;
2177           tm.tm_mday = day;
2178           tm.tm_hour = tm0.tm_hour;
2179           tm.tm_min = tm0.tm_min;
2180           tm.tm_sec = tm0.tm_sec;
2181           tm.tm_isdst = tm0.tm_isdst;
2182           Start = mktime_z (tz, &tm);
2183           if (Start == (time_t) -1)
2184             {
2185               if (pc.parse_datetime_debug)
2186                 dbg_printf (_("error: adding relative date resulted "
2187                               "in an invalid date: '%s'\n"),
2188                             debug_strfdatetime (&tm, &pc, dbg_tm,
2189                                                 sizeof dbg_tm));
2190               goto fail;
2191             }
2192 
2193           if (pc.parse_datetime_debug)
2194             {
2195               dbg_printf (_("after date adjustment "
2196                             "(%+"PRIdMAX" years, %+"PRIdMAX" months, "
2197                             "%+"PRIdMAX" days),\n"),
2198                           pc.rel.year, pc.rel.month, pc.rel.day);
2199               dbg_printf (_("    new date/time = '%s'\n"),
2200                           debug_strfdatetime (&tm, &pc, dbg_tm,
2201                                               sizeof dbg_tm));
2202 
2203               /* Warn about crossing DST due to time adjustment.
2204                  Example: https://bugs.gnu.org/8357
2205                  env TZ=Europe/Helsinki \
2206                    date --debug \
2207                         -d 'Mon Mar 28 00:36:07 2011 EEST 1 day ago'
2208 
2209                  This case is different than DST changes due to time adjustment,
2210                  i.e., "1 day ago" vs "24 hours ago" are calculated in different
2211                  places.
2212 
2213                  'tm0.tm_isdst' contains the DST of the input date,
2214                  'tm.tm_isdst' is the normalized result after calling
2215                  mktime (&tm).
2216               */
2217               if (tm0.tm_isdst != -1 && tm.tm_isdst != tm0.tm_isdst)
2218                 dbg_printf (_("warning: daylight saving time changed after "
2219                               "date adjustment\n"));
2220 
2221               /* Warn if the user did not ask to adjust days but mday changed,
2222                  or
2223                  user did not ask to adjust months/days but the month changed.
2224 
2225                  Example for first case:
2226                  2016-05-31 + 1 month => 2016-06-31 => 2016-07-01.
2227                  User asked to adjust month, but the day changed from 31 to 01.
2228 
2229                  Example for second case:
2230                  2016-02-29 + 1 year => 2017-02-29 => 2017-03-01.
2231                  User asked to adjust year, but the month changed from 02 to 03.
2232               */
2233               if (pc.rel.day == 0
2234                   && (tm.tm_mday != day
2235                       || (pc.rel.month == 0 && tm.tm_mon != month)))
2236                 {
2237                   dbg_printf (_("warning: month/year adjustment resulted in "
2238                                 "shifted dates:\n"));
2239                   char tm_year_buf[TM_YEAR_BUFSIZE];
2240                   dbg_printf (_("     adjusted Y M D: %s %02d %02d\n"),
2241                               tm_year_str (year, tm_year_buf), month + 1, day);
2242                   dbg_printf (_("   normalized Y M D: %s %02d %02d\n"),
2243                               tm_year_str (tm.tm_year, tm_year_buf),
2244                               tm.tm_mon + 1, tm.tm_mday);
2245                 }
2246             }
2247 
2248         }
2249 
2250       /* The only "output" of this if-block is an updated Start value,
2251          so this block must follow others that clobber Start.  */
2252       if (pc.zones_seen)
2253         {
2254           intmax_t delta = pc.time_zone, t1;
2255           bool overflow = false;
2256 #ifdef HAVE_TM_GMTOFF
2257           long int utcoff = tm.tm_gmtoff;
2258 #else
2259           time_t t = Start;
2260           struct tm gmt;
2261           int utcoff = (gmtime_r (&t, &gmt)
2262                         ? tm_diff (&tm, &gmt)
2263                         : (overflow = true, 0));
2264 #endif
2265           overflow |= INT_SUBTRACT_WRAPV (delta, utcoff, &delta);
2266           overflow |= INT_SUBTRACT_WRAPV (Start, delta, &t1);
2267           if (overflow || time_overflow (t1))
2268             {
2269               if (pc.parse_datetime_debug)
2270                 dbg_printf (_("error: timezone %d caused time_t overflow\n"),
2271                             pc.time_zone);
2272               goto fail;
2273             }
2274           Start = t1;
2275         }
2276 
2277       if (pc.parse_datetime_debug)
2278         {
2279           intmax_t Starti = Start;
2280           dbg_printf (_("'%s' = %"PRIdMAX" epoch-seconds\n"),
2281                       debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm),
2282                       Starti);
2283         }
2284 
2285 
2286       /* Add relative hours, minutes, and seconds.  On hosts that support
2287          leap seconds, ignore the possibility of leap seconds; e.g.,
2288          "+ 10 minutes" adds 600 seconds, even if one of them is a
2289          leap second.  Typically this is not what the user wants, but it's
2290          too hard to do it the other way, because the time zone indicator
2291          must be applied before relative times, and if mktime is applied
2292          again the time zone will be lost.  */
2293       {
2294         intmax_t orig_ns = pc.seconds.tv_nsec;
2295         intmax_t sum_ns = orig_ns + pc.rel.ns;
2296         int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
2297         int d4 = (sum_ns - normalized_ns) / BILLION;
2298         intmax_t d1, t1, d2, t2, t3, t4;
2299         if (INT_MULTIPLY_WRAPV (pc.rel.hour, 60 * 60, &d1)
2300             || INT_ADD_WRAPV (Start, d1, &t1)
2301             || INT_MULTIPLY_WRAPV (pc.rel.minutes, 60, &d2)
2302             || INT_ADD_WRAPV (t1, d2, &t2)
2303             || INT_ADD_WRAPV (t2, pc.rel.seconds, &t3)
2304             || INT_ADD_WRAPV (t3, d4, &t4)
2305             || time_overflow (t4))
2306           {
2307             if (pc.parse_datetime_debug)
2308               dbg_printf (_("error: adding relative time caused an "
2309                             "overflow\n"));
2310             goto fail;
2311           }
2312 
2313         result->tv_sec = t4;
2314         result->tv_nsec = normalized_ns;
2315 
2316         if (pc.parse_datetime_debug
2317             && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns))
2318           {
2319             dbg_printf (_("after time adjustment (%+"PRIdMAX" hours, "
2320                           "%+"PRIdMAX" minutes, "
2321                           "%+"PRIdMAX" seconds, %+d ns),\n"),
2322                         pc.rel.hour, pc.rel.minutes, pc.rel.seconds,
2323                         pc.rel.ns);
2324             dbg_printf (_("    new time = %"PRIdMAX" epoch-seconds\n"), t4);
2325 
2326             /* Warn about crossing DST due to time adjustment.
2327                Example: https://bugs.gnu.org/8357
2328                env TZ=Europe/Helsinki           \
2329                date --debug                                             \
2330                -d 'Mon Mar 28 00:36:07 2011 EEST 24 hours ago'
2331 
2332                This case is different than DST changes due to days adjustment,
2333                i.e., "1 day ago" vs "24 hours ago" are calculated in different
2334                places.
2335 
2336                'tm.tm_isdst' contains the date after date adjustment.  */
2337             struct tm lmt;
2338             if (tm.tm_isdst != -1 && localtime_rz (tz, &result->tv_sec, &lmt)
2339                 && tm.tm_isdst != lmt.tm_isdst)
2340               dbg_printf (_("warning: daylight saving time changed after "
2341                             "time adjustment\n"));
2342           }
2343       }
2344     }
2345 
2346   if (pc.parse_datetime_debug)
2347     {
2348       /* Special case: using 'date -u' simply set TZ=UTC0 */
2349       if (! tzstring)
2350         dbg_printf (_("timezone: system default\n"));
2351       else if (STREQ (tzstring, "UTC0"))
2352         dbg_printf (_("timezone: Universal Time\n"));
2353       else
2354         dbg_printf (_("timezone: TZ=\"%s\" environment value\n"), tzstring);
2355 
2356       intmax_t sec = result->tv_sec;
2357       int nsec = result->tv_nsec;
2358       dbg_printf (_("final: %"PRIdMAX".%09d (epoch-seconds)\n"),
2359                   sec, nsec);
2360 
2361       struct tm gmt, lmt;
2362       bool got_utc = !!gmtime_r (&result->tv_sec, &gmt);
2363       if (got_utc)
2364         dbg_printf (_("final: %s (UTC)\n"),
2365                     debug_strfdatetime (&gmt, NULL,
2366                                         dbg_tm, sizeof dbg_tm));
2367       if (localtime_rz (tz, &result->tv_sec, &lmt))
2368         {
2369 #ifdef HAVE_TM_GMTOFF
2370           bool got_utcoff = true;
2371           long int utcoff = lmt.tm_gmtoff;
2372 #else
2373           bool got_utcoff = got_utc;
2374           int utcoff;
2375           if (got_utcoff)
2376             utcoff = tm_diff (&lmt, &gmt);
2377 #endif
2378           if (got_utcoff)
2379             dbg_printf (_("final: %s (UTC%s)\n"),
2380                         debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm),
2381                         time_zone_str (utcoff, time_zone_buf));
2382           else
2383             dbg_printf (_("final: %s (unknown time zone offset)\n"),
2384                         debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm));
2385         }
2386     }
2387 
2388   ok = true;
2389 
2390  fail:
2391   if (tz != tzdefault)
2392     tzfree (tz);
2393   free (tz1alloc);
2394   return ok;
2395 }
2396 
2397 #if TEST
2398 
2399 int
main(int ac,char ** av)2400 main (int ac, char **av)
2401 {
2402   char buff[BUFSIZ];
2403 
2404   printf ("Enter date, or blank line to exit.\n\t> ");
2405   fflush (stdout);
2406 
2407   buff[BUFSIZ - 1] = '\0';
2408   while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
2409     {
2410       struct timespec d;
2411       struct tm const *tm;
2412       if (! parse_datetime (&d, buff, NULL))
2413         printf ("Bad format - couldn't convert.\n");
2414       else if (! (tm = localtime (&d.tv_sec)))
2415         {
2416           intmax_t sec = d.tv_sec;
2417           printf ("localtime (%"PRIdMAX") failed\n", sec);
2418         }
2419       else
2420         {
2421           int ns = d.tv_nsec;
2422           char tm_year_buf[TM_YEAR_BUFSIZE];
2423           printf ("%s-%02d-%02d %02d:%02d:%02d.%09d\n",
2424                   tm_year_str (tm->tm_year, tm_year_buf),
2425                   tm->tm_mon + 1, tm->tm_mday,
2426                   tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
2427         }
2428       printf ("\t> ");
2429       fflush (stdout);
2430     }
2431   return 0;
2432 }
2433 #endif /* TEST */
2434