1 %{
2 /* Parse a string into an internal timestamp.
3 
4    Copyright (C) 1999-2000, 2002-2021 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 and 2020 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 "idx.h"
39 #include "intprops.h"
40 #include "timespec.h"
41 #include "verify.h"
42 #include "strftime.h"
43 
44 /* There's no need to extend the stack, so there's no need to involve
45    alloca.  */
46 #define YYSTACK_USE_ALLOCA 0
47 
48 /* Tell Bison how much stack space is needed.  20 should be plenty for
49    this grammar, which is not right recursive.  Beware setting it too
50    high, since that might cause problems on machines whose
51    implementations have lame stack-overflow checking.  */
52 #define YYMAXDEPTH 20
53 #define YYINITDEPTH YYMAXDEPTH
54 
55 /* Since the code of parse-datetime.y is not included in the Emacs executable
56    itself, there is no need to #define static in this file.  Even if
57    the code were included in the Emacs executable, it probably
58    wouldn't do any harm to #undef it here; this will only cause
59    problems if we try to write to a static variable, which I don't
60    think this code needs to do.  */
61 #ifdef emacs
62 # undef static
63 #endif
64 
65 #include <inttypes.h>
66 #include <c-ctype.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 /* Shift A right by B bits portably, by dividing A by 2**B and
86    truncating towards minus infinity.  A and B should be free of side
87    effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
88    INT_BITS is the number of useful bits in an int.  GNU code can
89    assume that INT_BITS is at least 32.
90 
91    ISO C99 says that A >> B is implementation-defined if A < 0.  Some
92    implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
93    right in the usual way when A < 0, so SHR falls back on division if
94    ordinary A >> B doesn't seem to be the usual signed shift.  */
95 #define SHR(a, b)       \
96   (-1 >> 1 == -1        \
97    ? (a) >> (b)         \
98    : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
99 
100 #define HOUR(x) (60 * 60 * (x))
101 
102 #define STREQ(a, b) (strcmp (a, b) == 0)
103 
104 /* Verify that time_t is an integer as POSIX requires, and that every
105    time_t value fits in intmax_t.  Please file a bug report if these
106    assumptions are false on your platform.  */
107 verify (TYPE_IS_INTEGER (time_t));
108 verify (!TYPE_SIGNED (time_t) || INTMAX_MIN <= TYPE_MINIMUM (time_t));
109 verify (TYPE_MAXIMUM (time_t) <= INTMAX_MAX);
110 
111 /* True if N is out of range for time_t.  */
112 static bool
time_overflow(intmax_t n)113 time_overflow (intmax_t n)
114 {
115   return ! ((TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= n : 0 <= n)
116             && n <= TYPE_MAXIMUM (time_t));
117 }
118 
119 /* Convert a possibly-signed character to an unsigned character.  This is
120    a bit safer than casting to unsigned char, since it catches some type
121    errors that the cast doesn't.  */
to_uchar(char ch)122 static unsigned char to_uchar (char ch) { return ch; }
123 
124 static void _GL_ATTRIBUTE_FORMAT ((__printf__, 1, 2))
dbg_printf(char const * msg,...)125 dbg_printf (char const *msg, ...)
126 {
127   va_list args;
128   /* TODO: use gnulib's 'program_name' instead?  */
129   fputs ("date: ", stderr);
130 
131   va_start (args, msg);
132   vfprintf (stderr, msg, args);
133   va_end (args);
134 }
135 
136 
137 /* An integer value, and the number of digits in its textual
138    representation.  */
139 typedef struct
140 {
141   bool negative;
142   intmax_t value;
143   idx_t digits;
144 } textint;
145 
146 /* An entry in the lexical lookup table.  */
147 typedef struct
148 {
149   char const *name;
150   int type;
151   int value;
152 } table;
153 
154 /* Meridian: am, pm, or 24-hour style.  */
155 enum { MERam, MERpm, MER24 };
156 
157 /* A reasonable upper bound for the buffer used in debug output.  */
158 enum { DBGBUFSIZE = 100 };
159 
160 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
161 
162 /* Relative times.  */
163 typedef struct
164 {
165   /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
166   intmax_t year;
167   intmax_t month;
168   intmax_t day;
169   intmax_t hour;
170   intmax_t minutes;
171   intmax_t seconds;
172   int ns;
173 } relative_time;
174 
175 #if HAVE_COMPOUND_LITERALS
176 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
177 #else
178 static relative_time const RELATIVE_TIME_0;
179 #endif
180 
181 /* Information passed to and from the parser.  */
182 typedef struct
183 {
184   /* The input string remaining to be parsed.  */
185   const char *input;
186 
187   /* N, if this is the Nth Tuesday.  */
188   intmax_t day_ordinal;
189 
190   /* Day of week; Sunday is 0.  */
191   int day_number;
192 
193   /* tm_isdst flag for the local zone.  */
194   int local_isdst;
195 
196   /* Time zone, in seconds east of UT.  */
197   int time_zone;
198 
199   /* Style used for time.  */
200   int meridian;
201 
202   /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds.  */
203   textint year;
204   intmax_t month;
205   intmax_t day;
206   intmax_t hour;
207   intmax_t minutes;
208   struct timespec seconds; /* includes nanoseconds */
209 
210   /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
211   relative_time rel;
212 
213   /* Presence or counts of nonterminals of various flavors parsed so far.  */
214   bool timespec_seen;
215   bool rels_seen;
216   idx_t dates_seen;
217   idx_t days_seen;
218   idx_t local_zones_seen;
219   idx_t dsts_seen;
220   idx_t times_seen;
221   idx_t zones_seen;
222   bool year_seen;
223 
224 #ifdef GNULIB_PARSE_DATETIME2
225   /* Print debugging output to stderr.  */
226   bool parse_datetime_debug;
227 #endif
228 
229   /* Which of the 'seen' parts have been printed when debugging.  */
230   bool debug_dates_seen;
231   bool debug_days_seen;
232   bool debug_local_zones_seen;
233   bool debug_times_seen;
234   bool debug_zones_seen;
235   bool debug_year_seen;
236 
237   /* The user specified explicit ordinal day value.  */
238   bool debug_ordinal_day_seen;
239 
240   /* Table of local time zone abbreviations, terminated by a null entry.  */
241   table local_time_zone_table[3];
242 } parser_control;
243 
244 static bool
debugging(parser_control const * pc)245 debugging (parser_control const *pc)
246 {
247 #ifdef GNULIB_PARSE_DATETIME2
248   return pc->parse_datetime_debug;
249 #else
250   return false;
251 #endif
252 }
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 (!debugging (pc))
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 (!debugging (pc))
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 %define api.pure
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 (debugging (pc))
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 (debugging (pc))
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    RFC 822 got these backwards, but RFC 5322 makes the incorrect
1164    treatment optional, so do them the right way here.
1165 
1166    Note 'T' is a special case, as it is used as the separator in ISO
1167    8601 date and time of day representation.  */
1168 static table const military_table[] =
1169 {
1170   { "A", tZONE,  HOUR ( 1) },
1171   { "B", tZONE,  HOUR ( 2) },
1172   { "C", tZONE,  HOUR ( 3) },
1173   { "D", tZONE,  HOUR ( 4) },
1174   { "E", tZONE,  HOUR ( 5) },
1175   { "F", tZONE,  HOUR ( 6) },
1176   { "G", tZONE,  HOUR ( 7) },
1177   { "H", tZONE,  HOUR ( 8) },
1178   { "I", tZONE,  HOUR ( 9) },
1179   { "K", tZONE,  HOUR (10) },
1180   { "L", tZONE,  HOUR (11) },
1181   { "M", tZONE,  HOUR (12) },
1182   { "N", tZONE, -HOUR ( 1) },
1183   { "O", tZONE, -HOUR ( 2) },
1184   { "P", tZONE, -HOUR ( 3) },
1185   { "Q", tZONE, -HOUR ( 4) },
1186   { "R", tZONE, -HOUR ( 5) },
1187   { "S", tZONE, -HOUR ( 6) },
1188   { "T", 'T',    0 },
1189   { "U", tZONE, -HOUR ( 8) },
1190   { "V", tZONE, -HOUR ( 9) },
1191   { "W", tZONE, -HOUR (10) },
1192   { "X", tZONE, -HOUR (11) },
1193   { "Y", tZONE, -HOUR (12) },
1194   { "Z", tZONE,  HOUR ( 0) },
1195   { NULL, 0, 0 }
1196 };
1197 
1198 
1199 
1200 /* Convert a time zone expressed as HH:MM into an integer count of
1201    seconds.  If MM is negative, then S is of the form HHMM and needs
1202    to be picked apart; otherwise, S is of the form HH.  As specified in
1203    https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03, allow
1204    only valid TZ range, and consider first two digits as hours, if no
1205    minutes specified.  Return true if successful.  */
1206 
1207 static bool
time_zone_hhmm(parser_control * pc,textint s,intmax_t mm)1208 time_zone_hhmm (parser_control *pc, textint s, intmax_t mm)
1209 {
1210   intmax_t n_minutes;
1211   bool overflow = false;
1212 
1213   /* If the length of S is 1 or 2 and no minutes are specified,
1214      interpret it as a number of hours.  */
1215   if (s.digits <= 2 && mm < 0)
1216     s.value *= 100;
1217 
1218   if (mm < 0)
1219     n_minutes = (s.value / 100) * 60 + s.value % 100;
1220   else
1221     {
1222       overflow |= INT_MULTIPLY_WRAPV (s.value, 60, &n_minutes);
1223       overflow |= (s.negative
1224                    ? INT_SUBTRACT_WRAPV (n_minutes, mm, &n_minutes)
1225                    : INT_ADD_WRAPV (n_minutes, mm, &n_minutes));
1226     }
1227 
1228   if (overflow || ! (-24 * 60 <= n_minutes && n_minutes <= 24 * 60))
1229     return false;
1230   pc->time_zone = n_minutes * 60;
1231   return true;
1232 }
1233 
1234 static int
to_hour(intmax_t hours,int meridian)1235 to_hour (intmax_t hours, int meridian)
1236 {
1237   switch (meridian)
1238     {
1239     default: /* Pacify GCC.  */
1240     case MER24:
1241       return 0 <= hours && hours < 24 ? hours : -1;
1242     case MERam:
1243       return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
1244     case MERpm:
1245       return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
1246     }
1247 }
1248 
1249 enum { TM_YEAR_BASE = 1900 };
1250 enum { TM_YEAR_BUFSIZE = INT_BUFSIZE_BOUND (int) + 1 };
1251 
1252 /* Convert TM_YEAR, a year minus 1900, to a string that is numerically
1253    correct even if subtracting 1900 would overflow.  */
1254 
1255 static char const *
tm_year_str(int tm_year,char buf[TM_YEAR_BUFSIZE])1256 tm_year_str (int tm_year, char buf[TM_YEAR_BUFSIZE])
1257 {
1258   verify (TM_YEAR_BASE % 100 == 0);
1259   sprintf (buf, &"-%02d%02d"[-TM_YEAR_BASE <= tm_year],
1260            abs (tm_year / 100 + TM_YEAR_BASE / 100),
1261            abs (tm_year % 100));
1262   return buf;
1263 }
1264 
1265 /* Convert a text year number to a year minus 1900, working correctly
1266    even if the input is in the range INT_MAX .. INT_MAX + 1900 - 1.  */
1267 
1268 static bool
to_tm_year(textint textyear,bool debug,int * tm_year)1269 to_tm_year (textint textyear, bool debug, int *tm_year)
1270 {
1271   intmax_t year = textyear.value;
1272 
1273   /* XPG4 suggests that years 00-68 map to 2000-2068, and
1274      years 69-99 map to 1969-1999.  */
1275   if (0 <= year && textyear.digits == 2)
1276     {
1277       year += year < 69 ? 2000 : 1900;
1278       if (debug)
1279         dbg_printf (_("warning: adjusting year value %"PRIdMAX
1280                       " to %"PRIdMAX"\n"),
1281                     textyear.value, year);
1282     }
1283 
1284   if (year < 0
1285       ? INT_SUBTRACT_WRAPV (-TM_YEAR_BASE, year, tm_year)
1286       : INT_SUBTRACT_WRAPV (year, TM_YEAR_BASE, tm_year))
1287     {
1288       if (debug)
1289         dbg_printf (_("error: out-of-range year %"PRIdMAX"\n"), year);
1290       return false;
1291     }
1292 
1293   return true;
1294 }
1295 
1296 static table const * _GL_ATTRIBUTE_PURE
lookup_zone(parser_control const * pc,char const * name)1297 lookup_zone (parser_control const *pc, char const *name)
1298 {
1299   table const *tp;
1300 
1301   for (tp = universal_time_zone_table; tp->name; tp++)
1302     if (strcmp (name, tp->name) == 0)
1303       return tp;
1304 
1305   /* Try local zone abbreviations before those in time_zone_table, as
1306      the local ones are more likely to be right.  */
1307   for (tp = pc->local_time_zone_table; tp->name; tp++)
1308     if (strcmp (name, tp->name) == 0)
1309       return tp;
1310 
1311   for (tp = time_zone_table; tp->name; tp++)
1312     if (strcmp (name, tp->name) == 0)
1313       return tp;
1314 
1315   return NULL;
1316 }
1317 
1318 #if ! HAVE_TM_GMTOFF
1319 /* Yield the difference between *A and *B,
1320    measured in seconds, ignoring leap seconds.
1321    The body of this function is taken directly from the GNU C Library;
1322    see strftime.c.  */
1323 static int
tm_diff(const struct tm * a,const struct tm * b)1324 tm_diff (const struct tm *a, const struct tm *b)
1325 {
1326   /* Compute intervening leap days correctly even if year is negative.
1327      Take care to avoid int overflow in leap day calculations,
1328      but it's OK to assume that A and B are close to each other.  */
1329   int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
1330   int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
1331   int a100 = a4 / 25 - (a4 % 25 < 0);
1332   int b100 = b4 / 25 - (b4 % 25 < 0);
1333   int a400 = SHR (a100, 2);
1334   int b400 = SHR (b100, 2);
1335   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
1336   int years = a->tm_year - b->tm_year;
1337   int days = (365 * years + intervening_leap_days
1338               + (a->tm_yday - b->tm_yday));
1339   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
1340                 + (a->tm_min - b->tm_min))
1341           + (a->tm_sec - b->tm_sec));
1342 }
1343 #endif /* ! HAVE_TM_GMTOFF */
1344 
1345 static table const *
lookup_word(parser_control const * pc,char * word)1346 lookup_word (parser_control const *pc, char *word)
1347 {
1348   char *p;
1349   char *q;
1350   idx_t wordlen;
1351   table const *tp;
1352   bool period_found;
1353   bool abbrev;
1354 
1355   /* Make it uppercase.  */
1356   for (p = word; *p; p++)
1357     *p = c_toupper (to_uchar (*p));
1358 
1359   for (tp = meridian_table; tp->name; tp++)
1360     if (strcmp (word, tp->name) == 0)
1361       return tp;
1362 
1363   /* See if we have an abbreviation for a month.  */
1364   wordlen = strlen (word);
1365   abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
1366 
1367   for (tp = month_and_day_table; tp->name; tp++)
1368     if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
1369       return tp;
1370 
1371   if ((tp = lookup_zone (pc, word)))
1372     return tp;
1373 
1374   if (strcmp (word, dst_table[0].name) == 0)
1375     return dst_table;
1376 
1377   for (tp = time_units_table; tp->name; tp++)
1378     if (strcmp (word, tp->name) == 0)
1379       return tp;
1380 
1381   /* Strip off any plural and try the units table again.  */
1382   if (word[wordlen - 1] == 'S')
1383     {
1384       word[wordlen - 1] = '\0';
1385       for (tp = time_units_table; tp->name; tp++)
1386         if (strcmp (word, tp->name) == 0)
1387           return tp;
1388       word[wordlen - 1] = 'S';  /* For "this" in relative_time_table.  */
1389     }
1390 
1391   for (tp = relative_time_table; tp->name; tp++)
1392     if (strcmp (word, tp->name) == 0)
1393       return tp;
1394 
1395   /* Military time zones.  */
1396   if (wordlen == 1)
1397     for (tp = military_table; tp->name; tp++)
1398       if (word[0] == tp->name[0])
1399         return tp;
1400 
1401   /* Drop out any periods and try the time zone table again.  */
1402   for (period_found = false, p = q = word; (*p = *q); q++)
1403     if (*q == '.')
1404       period_found = true;
1405     else
1406       p++;
1407   if (period_found && (tp = lookup_zone (pc, word)))
1408     return tp;
1409 
1410   return NULL;
1411 }
1412 
1413 static int
yylex(union YYSTYPE * lvalp,parser_control * pc)1414 yylex (union YYSTYPE *lvalp, parser_control *pc)
1415 {
1416   unsigned char c;
1417 
1418   for (;;)
1419     {
1420       while (c = *pc->input, c_isspace (c))
1421         pc->input++;
1422 
1423       if (c_isdigit (c) || c == '-' || c == '+')
1424         {
1425           char const *p = pc->input;
1426           int sign;
1427           if (c == '-' || c == '+')
1428             {
1429               sign = c == '-' ? -1 : 1;
1430               while (c = *(pc->input = ++p), c_isspace (c))
1431                 continue;
1432               if (! c_isdigit (c))
1433                 /* skip the '-' sign */
1434                 continue;
1435             }
1436           else
1437             sign = 0;
1438 
1439           time_t value = 0;
1440           do
1441             {
1442               if (INT_MULTIPLY_WRAPV (value, 10, &value))
1443                 return '?';
1444               if (INT_ADD_WRAPV (value, sign < 0 ? '0' - c : c - '0', &value))
1445                 return '?';
1446               c = *++p;
1447             }
1448           while (c_isdigit (c));
1449 
1450           if ((c == '.' || c == ',') && c_isdigit (p[1]))
1451             {
1452               time_t s = value;
1453               int digits;
1454 
1455               /* Accumulate fraction, to ns precision.  */
1456               p++;
1457               int ns = *p++ - '0';
1458               for (digits = 2; digits <= LOG10_BILLION; digits++)
1459                 {
1460                   ns *= 10;
1461                   if (c_isdigit (*p))
1462                     ns += *p++ - '0';
1463                 }
1464 
1465               /* Skip excess digits, truncating toward -Infinity.  */
1466               if (sign < 0)
1467                 for (; c_isdigit (*p); p++)
1468                   if (*p != '0')
1469                     {
1470                       ns++;
1471                       break;
1472                     }
1473               while (c_isdigit (*p))
1474                 p++;
1475 
1476               /* Adjust to the timespec convention, which is that
1477                  tv_nsec is always a positive offset even if tv_sec is
1478                  negative.  */
1479               if (sign < 0 && ns)
1480                 {
1481                   if (INT_SUBTRACT_WRAPV (s, 1, &s))
1482                     return '?';
1483                   ns = BILLION - ns;
1484                 }
1485 
1486               lvalp->timespec.tv_sec = s;
1487               lvalp->timespec.tv_nsec = ns;
1488               pc->input = p;
1489               return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1490             }
1491           else
1492             {
1493               lvalp->textintval.negative = sign < 0;
1494               lvalp->textintval.value = value;
1495               lvalp->textintval.digits = p - pc->input;
1496               pc->input = p;
1497               return sign ? tSNUMBER : tUNUMBER;
1498             }
1499         }
1500 
1501       if (c_isalpha (c))
1502         {
1503           char buff[20];
1504           char *p = buff;
1505           table const *tp;
1506 
1507           do
1508             {
1509               if (p < buff + sizeof buff - 1)
1510                 *p++ = c;
1511               c = *++pc->input;
1512             }
1513           while (c_isalpha (c) || c == '.');
1514 
1515           *p = '\0';
1516           tp = lookup_word (pc, buff);
1517           if (! tp)
1518             {
1519               if (debugging (pc))
1520                 dbg_printf (_("error: unknown word '%s'\n"), buff);
1521               return '?';
1522             }
1523           lvalp->intval = tp->value;
1524           return tp->type;
1525         }
1526 
1527       if (c != '(')
1528         return to_uchar (*pc->input++);
1529 
1530       idx_t count = 0;
1531       do
1532         {
1533           c = *pc->input++;
1534           if (c == '\0')
1535             return c;
1536           if (c == '(')
1537             count++;
1538           else if (c == ')')
1539             count--;
1540         }
1541       while (count != 0);
1542     }
1543 }
1544 
1545 /* Do nothing if the parser reports an error.  */
1546 static int
yyerror(_GL_UNUSED parser_control const * pc,_GL_UNUSED char const * s)1547 yyerror (_GL_UNUSED parser_control const *pc,
1548          _GL_UNUSED char const *s)
1549 {
1550   return 0;
1551 }
1552 
1553 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1554    passing it to mktime_z, return true if it's OK.  It's not OK if
1555    mktime failed or if *TM0 has out-of-range mainline members.
1556    The caller should set TM1->tm_wday to -1 before calling mktime,
1557    as a negative tm_wday is how mktime failure is inferred.  */
1558 
1559 static bool
mktime_ok(struct tm const * tm0,struct tm const * tm1)1560 mktime_ok (struct tm const *tm0, struct tm const *tm1)
1561 {
1562   if (tm1->tm_wday < 0)
1563     return false;
1564 
1565   return ! ((tm0->tm_sec ^ tm1->tm_sec)
1566             | (tm0->tm_min ^ tm1->tm_min)
1567             | (tm0->tm_hour ^ tm1->tm_hour)
1568             | (tm0->tm_mday ^ tm1->tm_mday)
1569             | (tm0->tm_mon ^ tm1->tm_mon)
1570             | (tm0->tm_year ^ tm1->tm_year));
1571 }
1572 
1573 /* Debugging: format a 'struct tm' into a buffer, taking the parser's
1574    timezone information into account (if pc != NULL).  */
1575 static char const *
debug_strfdatetime(struct tm const * tm,parser_control const * pc,char * buf,int n)1576 debug_strfdatetime (struct tm const *tm, parser_control const *pc,
1577                     char *buf, int n)
1578 {
1579   /* TODO:
1580      1. find an optimal way to print date string in a clear and unambiguous
1581         format.  Currently, always add '(Y-M-D)' prefix.
1582         Consider '2016y01m10d'  or 'year(2016) month(01) day(10)'.
1583 
1584         If the user needs debug printing, it means he/she already having
1585         issues with the parsing - better to avoid formats that could
1586         be mis-interpreted (e.g., just YYYY-MM-DD).
1587 
1588      2. Can strftime be used instead?
1589         depends if it is portable and can print invalid dates on all systems.
1590 
1591      3. Print timezone information ?
1592 
1593      4. Print DST information ?
1594 
1595      5. Print nanosecond information ?
1596 
1597      NOTE:
1598      Printed date/time values might not be valid, e.g., '2016-02-31'
1599      or '2016-19-2016' .  These are the values as parsed from the user
1600      string, before validation.
1601   */
1602   int m = nstrftime (buf, n, "(Y-M-D) %Y-%m-%d %H:%M:%S", tm, 0, 0);
1603 
1604   /* If parser_control information was provided (for timezone),
1605      and there's enough space in the buffer, add timezone info.  */
1606   if (pc && m < n && pc->zones_seen)
1607     {
1608       int tz = pc->time_zone;
1609 
1610       /* Account for DST if tLOCAL_ZONE was seen.  */
1611       if (pc->local_zones_seen && !pc->zones_seen && 0 < pc->local_isdst)
1612         tz += 60 * 60;
1613 
1614       char time_zone_buf[TIME_ZONE_BUFSIZE];
1615       snprintf (&buf[m], n - m, " TZ=%s", time_zone_str (tz, time_zone_buf));
1616     }
1617   return buf;
1618 }
1619 
1620 static char const *
debug_strfdate(struct tm const * tm,char * buf,int n)1621 debug_strfdate (struct tm const *tm, char *buf, int n)
1622 {
1623   char tm_year_buf[TM_YEAR_BUFSIZE];
1624   snprintf (buf, n, "(Y-M-D) %s-%02d-%02d",
1625             tm_year_str (tm->tm_year, tm_year_buf),
1626             tm->tm_mon + 1, tm->tm_mday);
1627   return buf;
1628 }
1629 
1630 static char const *
debug_strftime(struct tm const * tm,char * buf,int n)1631 debug_strftime (struct tm const *tm, char *buf, int n)
1632 {
1633   snprintf (buf, n, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
1634   return buf;
1635 }
1636 
1637 /* If mktime_ok failed, display the failed time values,
1638    and provide possible hints.  Example output:
1639 
1640     date: error: invalid date/time value:
1641     date:     user provided time: '(Y-M-D) 2006-04-02 02:45:00'
1642     date:        normalized time: '(Y-M-D) 2006-04-02 03:45:00'
1643     date:                                             __
1644     date:      possible reasons:
1645     date:        non-existing due to daylight-saving time;
1646     date:        numeric values overflow;
1647     date:        missing timezone;
1648  */
1649 static void
debug_mktime_not_ok(struct tm const * tm0,struct tm const * tm1,parser_control const * pc,bool time_zone_seen)1650 debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1,
1651                      parser_control const *pc, bool time_zone_seen)
1652 {
1653   /* TODO: handle t == -1 (as in 'mktime_ok').  */
1654   char tmp[DBGBUFSIZE];
1655   int i;
1656   const bool eq_sec   = (tm0->tm_sec  == tm1->tm_sec);
1657   const bool eq_min   = (tm0->tm_min  == tm1->tm_min);
1658   const bool eq_hour  = (tm0->tm_hour == tm1->tm_hour);
1659   const bool eq_mday  = (tm0->tm_mday == tm1->tm_mday);
1660   const bool eq_month = (tm0->tm_mon  == tm1->tm_mon);
1661   const bool eq_year  = (tm0->tm_year == tm1->tm_year);
1662 
1663   const bool dst_shift = eq_sec && eq_min && !eq_hour
1664                          && eq_mday && eq_month && eq_year;
1665 
1666   if (!debugging (pc))
1667     return;
1668 
1669   dbg_printf (_("error: invalid date/time value:\n"));
1670   dbg_printf (_("    user provided time: '%s'\n"),
1671               debug_strfdatetime (tm0, pc, tmp, sizeof tmp));
1672   dbg_printf (_("       normalized time: '%s'\n"),
1673               debug_strfdatetime (tm1, pc, tmp, sizeof tmp));
1674   /* The format must be aligned with debug_strfdatetime and the two
1675      DEBUG statements above.  This string is not translated.  */
1676   i = snprintf (tmp, sizeof tmp,
1677                 "                                 %4s %2s %2s %2s %2s %2s",
1678                 eq_year ? "" : "----",
1679                 eq_month ? "" : "--",
1680                 eq_mday ? "" : "--",
1681                 eq_hour ? "" : "--",
1682                 eq_min ? "" : "--",
1683                 eq_sec ? "" : "--");
1684   /* Trim trailing whitespace.  */
1685   if (0 <= i)
1686     {
1687       if (sizeof tmp - 1 < i)
1688         i = sizeof tmp - 1;
1689       while (0 < i && tmp[i - 1] == ' ')
1690         --i;
1691       tmp[i] = '\0';
1692     }
1693   dbg_printf ("%s\n", tmp);
1694 
1695   dbg_printf (_("     possible reasons:\n"));
1696   if (dst_shift)
1697     dbg_printf (_("       non-existing due to daylight-saving time;\n"));
1698   if (!eq_mday && !eq_month)
1699     dbg_printf (_("       invalid day/month combination;\n"));
1700   dbg_printf (_("       numeric values overflow;\n"));
1701   dbg_printf ("       %s\n", (time_zone_seen ? _("incorrect timezone")
1702                               : _("missing timezone")));
1703 }
1704 
1705 /* Parse a date/time string, storing the resulting time value into *RESULT.
1706    The string itself is pointed to by P.  Return true if successful.
1707    P can be an incomplete or relative time specification; if so, use
1708    *NOW as the basis for the returned time.  Default to timezone
1709    TZDEFAULT, which corresponds to tzalloc (TZSTRING).  */
1710 static bool
parse_datetime_body(struct timespec * result,char const * p,struct timespec const * now,unsigned int flags,timezone_t tzdefault,char const * tzstring)1711 parse_datetime_body (struct timespec *result, char const *p,
1712                      struct timespec const *now, unsigned int flags,
1713                      timezone_t tzdefault, char const *tzstring)
1714 {
1715   struct tm tm;
1716   struct tm tm0;
1717   char time_zone_buf[TIME_ZONE_BUFSIZE];
1718   char dbg_tm[DBGBUFSIZE];
1719   bool ok = false;
1720   char const *input_sentinel = p + strlen (p);
1721   char *tz1alloc = NULL;
1722 
1723   /* A reasonable upper bound for the size of ordinary TZ strings.
1724      Use heap allocation if TZ's length exceeds this.  */
1725   enum { TZBUFSIZE = 100 };
1726   char tz1buf[TZBUFSIZE];
1727 
1728   struct timespec gettime_buffer;
1729   if (! now)
1730     {
1731       gettime (&gettime_buffer);
1732       now = &gettime_buffer;
1733     }
1734 
1735   time_t Start = now->tv_sec;
1736   int Start_ns = now->tv_nsec;
1737 
1738   unsigned char c;
1739   while (c = *p, c_isspace (c))
1740     p++;
1741 
1742   timezone_t tz = tzdefault;
1743 
1744   /* Store a local copy prior to first "goto".  Without this, a prior use
1745      below of RELATIVE_TIME_0 on the RHS might translate to an assignment-
1746      to-temporary, which would trigger a -Wjump-misses-init warning.  */
1747   const relative_time rel_time_0 = RELATIVE_TIME_0;
1748 
1749   if (strncmp (p, "TZ=\"", 4) == 0)
1750     {
1751       char const *tzbase = p + 4;
1752       idx_t tzsize = 1;
1753       char const *s;
1754 
1755       for (s = tzbase; *s; s++, tzsize++)
1756         if (*s == '\\')
1757           {
1758             s++;
1759             if (! (*s == '\\' || *s == '"'))
1760               break;
1761           }
1762         else if (*s == '"')
1763           {
1764             timezone_t tz1;
1765             char *tz1string = tz1buf;
1766             char *z;
1767             if (TZBUFSIZE < tzsize)
1768               {
1769                 tz1alloc = malloc (tzsize);
1770                 if (!tz1alloc)
1771                   goto fail;
1772                 tz1string = tz1alloc;
1773               }
1774             z = tz1string;
1775             for (s = tzbase; *s != '"'; s++)
1776               *z++ = *(s += *s == '\\');
1777             *z = '\0';
1778             tz1 = tzalloc (tz1string);
1779             if (!tz1)
1780               goto fail;
1781             tz = tz1;
1782             tzstring = tz1string;
1783 
1784             p = s + 1;
1785             while (c = *p, c_isspace (c))
1786               p++;
1787 
1788             break;
1789           }
1790     }
1791 
1792   struct tm tmp;
1793   if (! localtime_rz (tz, &now->tv_sec, &tmp))
1794     goto fail;
1795 
1796   /* As documented, be careful to treat the empty string just like
1797      a date string of "0".  Without this, an empty string would be
1798      declared invalid when parsed during a DST transition.  */
1799   if (*p == '\0')
1800     p = "0";
1801 
1802   parser_control pc;
1803   pc.input = p;
1804 #ifdef GNULIB_PARSE_DATETIME2
1805   pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG) != 0;
1806 #endif
1807   if (INT_ADD_WRAPV (tmp.tm_year, TM_YEAR_BASE, &pc.year.value))
1808     {
1809       if (debugging (&pc))
1810         dbg_printf (_("error: initial year out of range\n"));
1811       goto fail;
1812     }
1813   pc.year.digits = 0;
1814   pc.month = tmp.tm_mon + 1;
1815   pc.day = tmp.tm_mday;
1816   pc.hour = tmp.tm_hour;
1817   pc.minutes = tmp.tm_min;
1818   pc.seconds.tv_sec = tmp.tm_sec;
1819   pc.seconds.tv_nsec = Start_ns;
1820   tm.tm_isdst = tmp.tm_isdst;
1821 
1822   pc.meridian = MER24;
1823   pc.rel = rel_time_0;
1824   pc.timespec_seen = false;
1825   pc.rels_seen = false;
1826   pc.dates_seen = 0;
1827   pc.days_seen = 0;
1828   pc.times_seen = 0;
1829   pc.local_zones_seen = 0;
1830   pc.dsts_seen = 0;
1831   pc.zones_seen = 0;
1832   pc.year_seen = false;
1833   pc.debug_dates_seen = false;
1834   pc.debug_days_seen = false;
1835   pc.debug_times_seen = false;
1836   pc.debug_local_zones_seen = false;
1837   pc.debug_zones_seen = false;
1838   pc.debug_year_seen = false;
1839   pc.debug_ordinal_day_seen = false;
1840 
1841 #if HAVE_STRUCT_TM_TM_ZONE
1842   pc.local_time_zone_table[0].name = tmp.tm_zone;
1843   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1844   pc.local_time_zone_table[0].value = tmp.tm_isdst;
1845   pc.local_time_zone_table[1].name = NULL;
1846 
1847   /* Probe the names used in the next three calendar quarters, looking
1848      for a tm_isdst different from the one we already have.  */
1849   {
1850     int quarter;
1851     for (quarter = 1; quarter <= 3; quarter++)
1852       {
1853         time_t probe;
1854         if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &probe))
1855           break;
1856         struct tm probe_tm;
1857         if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone
1858             && probe_tm.tm_isdst != pc.local_time_zone_table[0].value)
1859           {
1860               {
1861                 pc.local_time_zone_table[1].name = probe_tm.tm_zone;
1862                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1863                 pc.local_time_zone_table[1].value = probe_tm.tm_isdst;
1864                 pc.local_time_zone_table[2].name = NULL;
1865               }
1866             break;
1867           }
1868       }
1869   }
1870 #else
1871 #if HAVE_TZNAME
1872   {
1873 # if !HAVE_DECL_TZNAME
1874     extern char *tzname[];
1875 # endif
1876     int i;
1877     for (i = 0; i < 2; i++)
1878       {
1879         pc.local_time_zone_table[i].name = tzname[i];
1880         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1881         pc.local_time_zone_table[i].value = i;
1882       }
1883     pc.local_time_zone_table[i].name = NULL;
1884   }
1885 #else
1886   pc.local_time_zone_table[0].name = NULL;
1887 #endif
1888 #endif
1889 
1890   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1891       && ! strcmp (pc.local_time_zone_table[0].name,
1892                    pc.local_time_zone_table[1].name))
1893     {
1894       /* This locale uses the same abbreviation for standard and
1895          daylight times.  So if we see that abbreviation, we don't
1896          know whether it's daylight time.  */
1897       pc.local_time_zone_table[0].value = -1;
1898       pc.local_time_zone_table[1].name = NULL;
1899     }
1900 
1901   if (yyparse (&pc) != 0)
1902     {
1903       if (debugging (&pc))
1904         dbg_printf ((input_sentinel <= pc.input
1905                      ? _("error: parsing failed\n")
1906                      : _("error: parsing failed, stopped at '%s'\n")),
1907                     pc.input);
1908       goto fail;
1909     }
1910 
1911 
1912   /* Determine effective timezone source.  */
1913 
1914   if (debugging (&pc))
1915     {
1916       dbg_printf (_("input timezone: "));
1917 
1918       if (pc.timespec_seen)
1919         fprintf (stderr, _("'@timespec' - always UTC"));
1920       else if (pc.zones_seen)
1921         fprintf (stderr, _("parsed date/time string"));
1922       else if (tzstring)
1923         {
1924           if (tz != tzdefault)
1925             fprintf (stderr, _("TZ=\"%s\" in date string"), tzstring);
1926           else if (STREQ (tzstring, "UTC0"))
1927             {
1928               /* Special case: 'date -u' sets TZ="UTC0".  */
1929               fprintf (stderr, _("TZ=\"UTC0\" environment value or -u"));
1930             }
1931           else
1932             fprintf (stderr, _("TZ=\"%s\" environment value"), tzstring);
1933         }
1934       else
1935         fprintf (stderr, _("system default"));
1936 
1937       /* Account for DST changes if tLOCAL_ZONE was seen.
1938          local timezone only changes DST and is relative to the
1939          default timezone.*/
1940       if (pc.local_zones_seen && !pc.zones_seen && 0 < pc.local_isdst)
1941         fprintf (stderr, ", dst");
1942 
1943       if (pc.zones_seen)
1944         fprintf (stderr, " (%s)", time_zone_str (pc.time_zone, time_zone_buf));
1945 
1946       fputc ('\n', stderr);
1947     }
1948 
1949   if (pc.timespec_seen)
1950     *result = pc.seconds;
1951   else
1952     {
1953       if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1954                | (pc.local_zones_seen + pc.zones_seen)))
1955         {
1956           if (debugging (&pc))
1957             {
1958               if (pc.times_seen > 1)
1959                 dbg_printf ("error: seen multiple time parts\n");
1960               if (pc.dates_seen > 1)
1961                 dbg_printf ("error: seen multiple date parts\n");
1962               if (pc.days_seen > 1)
1963                 dbg_printf ("error: seen multiple days parts\n");
1964               if (pc.dsts_seen > 1)
1965                 dbg_printf ("error: seen multiple daylight-saving parts\n");
1966               if ((pc.local_zones_seen + pc.zones_seen) > 1)
1967                 dbg_printf ("error: seen multiple time-zone parts\n");
1968             }
1969           goto fail;
1970         }
1971 
1972       if (! to_tm_year (pc.year, debugging (&pc), &tm.tm_year)
1973           || INT_ADD_WRAPV (pc.month, -1, &tm.tm_mon)
1974           || INT_ADD_WRAPV (pc.day, 0, &tm.tm_mday))
1975         {
1976           if (debugging (&pc))
1977             dbg_printf (_("error: year, month, or day overflow\n"));
1978           goto fail;
1979         }
1980       if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1981         {
1982           tm.tm_hour = to_hour (pc.hour, pc.meridian);
1983           if (tm.tm_hour < 0)
1984             {
1985               char const *mrd = (pc.meridian == MERam ? "am"
1986                                  : pc.meridian == MERpm ?"pm" : "");
1987               if (debugging (&pc))
1988                 dbg_printf (_("error: invalid hour %"PRIdMAX"%s\n"),
1989                             pc.hour, mrd);
1990               goto fail;
1991             }
1992           tm.tm_min = pc.minutes;
1993           tm.tm_sec = pc.seconds.tv_sec;
1994           if (debugging (&pc))
1995             dbg_printf ((pc.times_seen
1996                          ? _("using specified time as starting value: '%s'\n")
1997                          : _("using current time as starting value: '%s'\n")),
1998                         debug_strftime (&tm, dbg_tm, sizeof dbg_tm));
1999         }
2000       else
2001         {
2002           tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
2003           pc.seconds.tv_nsec = 0;
2004           if (debugging (&pc))
2005             dbg_printf ("warning: using midnight as starting time: 00:00:00\n");
2006         }
2007 
2008       /* Let mktime deduce tm_isdst if we have an absolute timestamp.  */
2009       if (pc.dates_seen | pc.days_seen | pc.times_seen)
2010         tm.tm_isdst = -1;
2011 
2012       /* But if the input explicitly specifies local time with or without
2013          DST, give mktime that information.  */
2014       if (pc.local_zones_seen)
2015         tm.tm_isdst = pc.local_isdst;
2016 
2017       tm0.tm_sec = tm.tm_sec;
2018       tm0.tm_min = tm.tm_min;
2019       tm0.tm_hour = tm.tm_hour;
2020       tm0.tm_mday = tm.tm_mday;
2021       tm0.tm_mon = tm.tm_mon;
2022       tm0.tm_year = tm.tm_year;
2023       tm0.tm_isdst = tm.tm_isdst;
2024       tm.tm_wday = -1;
2025 
2026       Start = mktime_z (tz, &tm);
2027 
2028       if (! mktime_ok (&tm0, &tm))
2029         {
2030           bool repaired = false;
2031           bool time_zone_seen = pc.zones_seen != 0;
2032           if (time_zone_seen)
2033             {
2034               /* Guard against falsely reporting errors near the time_t
2035                  boundaries when parsing times in other time zones.  For
2036                  example, suppose the input string "1969-12-31 23:00:00 -0100",
2037                  the current time zone is 8 hours ahead of UTC, and the min
2038                  time_t value is 1970-01-01 00:00:00 UTC.  Then the min
2039                  localtime value is 1970-01-01 08:00:00, and mktime will
2040                  therefore fail on 1969-12-31 23:00:00.  To work around the
2041                  problem, set the time zone to 1 hour behind UTC temporarily
2042                  by setting TZ="XXX1:00" and try mktime again.  */
2043 
2044               char tz2buf[sizeof "XXX" - 1 + TIME_ZONE_BUFSIZE];
2045               tz2buf[0] = tz2buf[1] = tz2buf[2] = 'X';
2046               time_zone_str (pc.time_zone, &tz2buf[3]);
2047               timezone_t tz2 = tzalloc (tz2buf);
2048               if (!tz2)
2049                 {
2050                   if (debugging (&pc))
2051                     dbg_printf (_("error: tzalloc (\"%s\") failed\n"), tz2buf);
2052                   goto fail;
2053                 }
2054               tm.tm_sec = tm0.tm_sec;
2055               tm.tm_min = tm0.tm_min;
2056               tm.tm_hour = tm0.tm_hour;
2057               tm.tm_mday = tm0.tm_mday;
2058               tm.tm_mon = tm0.tm_mon;
2059               tm.tm_year = tm0.tm_year;
2060               tm.tm_isdst = tm0.tm_isdst;
2061               tm.tm_wday = -1;
2062               Start = mktime_z (tz2, &tm);
2063               repaired = mktime_ok (&tm0, &tm);
2064               tzfree (tz2);
2065             }
2066 
2067           if (! repaired)
2068             {
2069               debug_mktime_not_ok (&tm0, &tm, &pc, time_zone_seen);
2070               goto fail;
2071             }
2072         }
2073 
2074       char dbg_ord[DBGBUFSIZE];
2075 
2076       if (pc.days_seen && ! pc.dates_seen)
2077         {
2078           intmax_t dayincr;
2079           if (INT_MULTIPLY_WRAPV ((pc.day_ordinal
2080                                    - (0 < pc.day_ordinal
2081                                       && tm.tm_wday != pc.day_number)),
2082                                   7, &dayincr)
2083               || INT_ADD_WRAPV ((pc.day_number - tm.tm_wday + 7) % 7,
2084                                 dayincr, &dayincr)
2085               || INT_ADD_WRAPV (dayincr, tm.tm_mday, &tm.tm_mday))
2086             Start = -1;
2087           else
2088             {
2089               tm.tm_isdst = -1;
2090               Start = mktime_z (tz, &tm);
2091             }
2092 
2093           if (Start == (time_t) -1)
2094             {
2095               if (debugging (&pc))
2096                 dbg_printf (_("error: day '%s' "
2097                               "(day ordinal=%"PRIdMAX" number=%d) "
2098                               "resulted in an invalid date: '%s'\n"),
2099                             str_days (&pc, dbg_ord, sizeof dbg_ord),
2100                             pc.day_ordinal, pc.day_number,
2101                             debug_strfdatetime (&tm, &pc, dbg_tm,
2102                                                 sizeof dbg_tm));
2103               goto fail;
2104             }
2105 
2106           if (debugging (&pc))
2107             dbg_printf (_("new start date: '%s' is '%s'\n"),
2108                         str_days (&pc, dbg_ord, sizeof dbg_ord),
2109                         debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2110 
2111         }
2112 
2113       if (debugging (&pc))
2114         {
2115           if (!pc.dates_seen && !pc.days_seen)
2116             dbg_printf (_("using current date as starting value: '%s'\n"),
2117                         debug_strfdate (&tm, dbg_tm, sizeof dbg_tm));
2118 
2119           if (pc.days_seen && pc.dates_seen)
2120             dbg_printf (_("warning: day (%s) ignored when explicit dates "
2121                           "are given\n"),
2122                         str_days (&pc, dbg_ord, sizeof dbg_ord));
2123 
2124           dbg_printf (_("starting date/time: '%s'\n"),
2125                       debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2126         }
2127 
2128       /* Add relative date.  */
2129       if (pc.rel.year | pc.rel.month | pc.rel.day)
2130         {
2131           if (debugging (&pc))
2132             {
2133               if ((pc.rel.year != 0 || pc.rel.month != 0) && tm.tm_mday != 15)
2134                 dbg_printf (_("warning: when adding relative months/years, "
2135                               "it is recommended to specify the 15th of the "
2136                               "months\n"));
2137 
2138               if (pc.rel.day != 0 && tm.tm_hour != 12)
2139                 dbg_printf (_("warning: when adding relative days, "
2140                               "it is recommended to specify noon\n"));
2141             }
2142 
2143           int year, month, day;
2144           if (INT_ADD_WRAPV (tm.tm_year, pc.rel.year, &year)
2145               || INT_ADD_WRAPV (tm.tm_mon, pc.rel.month, &month)
2146               || INT_ADD_WRAPV (tm.tm_mday, pc.rel.day, &day))
2147             {
2148               if (debugging (&pc))
2149                 dbg_printf (_("error: %s:%d\n"), __FILE__, __LINE__);
2150               goto fail;
2151             }
2152           tm.tm_year = year;
2153           tm.tm_mon = month;
2154           tm.tm_mday = day;
2155           tm.tm_hour = tm0.tm_hour;
2156           tm.tm_min = tm0.tm_min;
2157           tm.tm_sec = tm0.tm_sec;
2158           tm.tm_isdst = tm0.tm_isdst;
2159           Start = mktime_z (tz, &tm);
2160           if (Start == (time_t) -1)
2161             {
2162               if (debugging (&pc))
2163                 dbg_printf (_("error: adding relative date resulted "
2164                               "in an invalid date: '%s'\n"),
2165                             debug_strfdatetime (&tm, &pc, dbg_tm,
2166                                                 sizeof dbg_tm));
2167               goto fail;
2168             }
2169 
2170           if (debugging (&pc))
2171             {
2172               dbg_printf (_("after date adjustment "
2173                             "(%+"PRIdMAX" years, %+"PRIdMAX" months, "
2174                             "%+"PRIdMAX" days),\n"),
2175                           pc.rel.year, pc.rel.month, pc.rel.day);
2176               dbg_printf (_("    new date/time = '%s'\n"),
2177                           debug_strfdatetime (&tm, &pc, dbg_tm,
2178                                               sizeof dbg_tm));
2179 
2180               /* Warn about crossing DST due to time adjustment.
2181                  Example: https://bugs.gnu.org/8357
2182                  env TZ=Europe/Helsinki \
2183                    date --debug \
2184                         -d 'Mon Mar 28 00:36:07 2011 EEST 1 day ago'
2185 
2186                  This case is different than DST changes due to time adjustment,
2187                  i.e., "1 day ago" vs "24 hours ago" are calculated in different
2188                  places.
2189 
2190                  'tm0.tm_isdst' contains the DST of the input date,
2191                  'tm.tm_isdst' is the normalized result after calling
2192                  mktime (&tm).
2193               */
2194               if (tm0.tm_isdst != -1 && tm.tm_isdst != tm0.tm_isdst)
2195                 dbg_printf (_("warning: daylight saving time changed after "
2196                               "date adjustment\n"));
2197 
2198               /* Warn if the user did not ask to adjust days but mday changed,
2199                  or
2200                  user did not ask to adjust months/days but the month changed.
2201 
2202                  Example for first case:
2203                  2016-05-31 + 1 month => 2016-06-31 => 2016-07-01.
2204                  User asked to adjust month, but the day changed from 31 to 01.
2205 
2206                  Example for second case:
2207                  2016-02-29 + 1 year => 2017-02-29 => 2017-03-01.
2208                  User asked to adjust year, but the month changed from 02 to 03.
2209               */
2210               if (pc.rel.day == 0
2211                   && (tm.tm_mday != day
2212                       || (pc.rel.month == 0 && tm.tm_mon != month)))
2213                 {
2214                   dbg_printf (_("warning: month/year adjustment resulted in "
2215                                 "shifted dates:\n"));
2216                   char tm_year_buf[TM_YEAR_BUFSIZE];
2217                   dbg_printf (_("     adjusted Y M D: %s %02d %02d\n"),
2218                               tm_year_str (year, tm_year_buf), month + 1, day);
2219                   dbg_printf (_("   normalized Y M D: %s %02d %02d\n"),
2220                               tm_year_str (tm.tm_year, tm_year_buf),
2221                               tm.tm_mon + 1, tm.tm_mday);
2222                 }
2223             }
2224 
2225         }
2226 
2227       /* The only "output" of this if-block is an updated Start value,
2228          so this block must follow others that clobber Start.  */
2229       if (pc.zones_seen)
2230         {
2231           bool overflow = false;
2232 #ifdef HAVE_TM_GMTOFF
2233           long int utcoff = tm.tm_gmtoff;
2234 #else
2235           time_t t = Start;
2236           struct tm gmt;
2237           int utcoff = (gmtime_r (&t, &gmt)
2238                         ? tm_diff (&tm, &gmt)
2239                         : (overflow = true, 0));
2240 #endif
2241           intmax_t delta;
2242           overflow |= INT_SUBTRACT_WRAPV (pc.time_zone, utcoff, &delta);
2243           time_t t1;
2244           overflow |= INT_SUBTRACT_WRAPV (Start, delta, &t1);
2245           if (overflow)
2246             {
2247               if (debugging (&pc))
2248                 dbg_printf (_("error: timezone %d caused time_t overflow\n"),
2249                             pc.time_zone);
2250               goto fail;
2251             }
2252           Start = t1;
2253         }
2254 
2255       if (debugging (&pc))
2256         {
2257           intmax_t Starti = Start;
2258           dbg_printf (_("'%s' = %"PRIdMAX" epoch-seconds\n"),
2259                       debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm),
2260                       Starti);
2261         }
2262 
2263 
2264       /* Add relative hours, minutes, and seconds.  On hosts that support
2265          leap seconds, ignore the possibility of leap seconds; e.g.,
2266          "+ 10 minutes" adds 600 seconds, even if one of them is a
2267          leap second.  Typically this is not what the user wants, but it's
2268          too hard to do it the other way, because the time zone indicator
2269          must be applied before relative times, and if mktime is applied
2270          again the time zone will be lost.  */
2271       {
2272         intmax_t orig_ns = pc.seconds.tv_nsec;
2273         intmax_t sum_ns = orig_ns + pc.rel.ns;
2274         int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
2275         int d4 = (sum_ns - normalized_ns) / BILLION;
2276         intmax_t d1, t1, d2, t2, t3;
2277         time_t t4;
2278         if (INT_MULTIPLY_WRAPV (pc.rel.hour, 60 * 60, &d1)
2279             || INT_ADD_WRAPV (Start, d1, &t1)
2280             || INT_MULTIPLY_WRAPV (pc.rel.minutes, 60, &d2)
2281             || INT_ADD_WRAPV (t1, d2, &t2)
2282             || INT_ADD_WRAPV (t2, pc.rel.seconds, &t3)
2283             || INT_ADD_WRAPV (t3, d4, &t4))
2284           {
2285             if (debugging (&pc))
2286               dbg_printf (_("error: adding relative time caused an "
2287                             "overflow\n"));
2288             goto fail;
2289           }
2290 
2291         result->tv_sec = t4;
2292         result->tv_nsec = normalized_ns;
2293 
2294         if (debugging (&pc)
2295             && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns))
2296           {
2297             dbg_printf (_("after time adjustment (%+"PRIdMAX" hours, "
2298                           "%+"PRIdMAX" minutes, "
2299                           "%+"PRIdMAX" seconds, %+d ns),\n"),
2300                         pc.rel.hour, pc.rel.minutes, pc.rel.seconds,
2301                         pc.rel.ns);
2302             intmax_t t4i = t4;
2303             dbg_printf (_("    new time = %"PRIdMAX" epoch-seconds\n"), t4i);
2304 
2305             /* Warn about crossing DST due to time adjustment.
2306                Example: https://bugs.gnu.org/8357
2307                env TZ=Europe/Helsinki           \
2308                date --debug                                             \
2309                -d 'Mon Mar 28 00:36:07 2011 EEST 24 hours ago'
2310 
2311                This case is different than DST changes due to days adjustment,
2312                i.e., "1 day ago" vs "24 hours ago" are calculated in different
2313                places.
2314 
2315                'tm.tm_isdst' contains the date after date adjustment.  */
2316             struct tm lmt;
2317             if (tm.tm_isdst != -1 && localtime_rz (tz, &result->tv_sec, &lmt)
2318                 && tm.tm_isdst != lmt.tm_isdst)
2319               dbg_printf (_("warning: daylight saving time changed after "
2320                             "time adjustment\n"));
2321           }
2322       }
2323     }
2324 
2325   if (debugging (&pc))
2326     {
2327       /* Special case: using 'date -u' simply set TZ=UTC0 */
2328       if (! tzstring)
2329         dbg_printf (_("timezone: system default\n"));
2330       else if (STREQ (tzstring, "UTC0"))
2331         dbg_printf (_("timezone: Universal Time\n"));
2332       else
2333         dbg_printf (_("timezone: TZ=\"%s\" environment value\n"), tzstring);
2334 
2335       intmax_t sec = result->tv_sec;
2336       int nsec = result->tv_nsec;
2337       dbg_printf (_("final: %"PRIdMAX".%09d (epoch-seconds)\n"),
2338                   sec, nsec);
2339 
2340       struct tm gmt, lmt;
2341       bool got_utc = !!gmtime_r (&result->tv_sec, &gmt);
2342       if (got_utc)
2343         dbg_printf (_("final: %s (UTC)\n"),
2344                     debug_strfdatetime (&gmt, NULL,
2345                                         dbg_tm, sizeof dbg_tm));
2346       if (localtime_rz (tz, &result->tv_sec, &lmt))
2347         {
2348 #ifdef HAVE_TM_GMTOFF
2349           bool got_utcoff = true;
2350           long int utcoff = lmt.tm_gmtoff;
2351 #else
2352           bool got_utcoff = got_utc;
2353           int utcoff;
2354           if (got_utcoff)
2355             utcoff = tm_diff (&lmt, &gmt);
2356 #endif
2357           if (got_utcoff)
2358             dbg_printf (_("final: %s (UTC%s)\n"),
2359                         debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm),
2360                         time_zone_str (utcoff, time_zone_buf));
2361           else
2362             dbg_printf (_("final: %s (unknown time zone offset)\n"),
2363                         debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm));
2364         }
2365     }
2366 
2367   ok = true;
2368 
2369  fail:
2370   if (tz != tzdefault)
2371     tzfree (tz);
2372   free (tz1alloc);
2373   return ok;
2374 }
2375 
2376 #ifdef GNULIB_PARSE_DATETIME2
2377 /* Parse a date/time string, storing the resulting time value into *RESULT.
2378    The string itself is pointed to by P.  Return true if successful.
2379    P can be an incomplete or relative time specification; if so, use
2380    *NOW as the basis for the returned time.  Default to timezone
2381    TZDEFAULT, which corresponds to tzalloc (TZSTRING).  */
2382 bool
parse_datetime2(struct timespec * result,char const * p,struct timespec const * now,unsigned int flags,timezone_t tzdefault,char const * tzstring)2383 parse_datetime2 (struct timespec *result, char const *p,
2384                  struct timespec const *now, unsigned int flags,
2385                  timezone_t tzdefault, char const *tzstring)
2386 {
2387   return parse_datetime_body (result, p, now, flags, tzdefault, tzstring);
2388 }
2389 #endif
2390 
2391 
2392 /* The plain interface: run with debug=false and the default timezone.   */
2393 bool
parse_datetime(struct timespec * result,char const * p,struct timespec const * now)2394 parse_datetime (struct timespec *result, char const *p,
2395                 struct timespec const *now)
2396 {
2397   char const *tzstring = getenv ("TZ");
2398   timezone_t tz = tzalloc (tzstring);
2399   if (!tz)
2400     return false;
2401   bool ok = parse_datetime_body (result, p, now, 0, tz, tzstring);
2402   tzfree (tz);
2403   return ok;
2404 }
2405 
2406 #if TEST
2407 
2408 int
main(int ac,char ** av)2409 main (int ac, char **av)
2410 {
2411   char buff[BUFSIZ];
2412 
2413   printf ("Enter date, or blank line to exit.\n\t> ");
2414   fflush (stdout);
2415 
2416   buff[BUFSIZ - 1] = '\0';
2417   while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
2418     {
2419       struct timespec d;
2420       struct tm const *tm;
2421       if (! parse_datetime (&d, buff, NULL))
2422         printf ("Bad format - couldn't convert.\n");
2423       else if (! (tm = localtime (&d.tv_sec)))
2424         {
2425           intmax_t sec = d.tv_sec;
2426           printf ("localtime (%"PRIdMAX") failed\n", sec);
2427         }
2428       else
2429         {
2430           int ns = d.tv_nsec;
2431           char tm_year_buf[TM_YEAR_BUFSIZE];
2432           printf ("%s-%02d-%02d %02d:%02d:%02d.%09d\n",
2433                   tm_year_str (tm->tm_year, tm_year_buf),
2434                   tm->tm_mon + 1, tm->tm_mday,
2435                   tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
2436         }
2437       printf ("\t> ");
2438       fflush (stdout);
2439     }
2440   return 0;
2441 }
2442 #endif /* TEST */
2443