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