1%{
2/* Parse a string into an internal time stamp.
3
4   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software
5   Foundation, Inc.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10   any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software Foundation,
19   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
20
21/* Originally written by Steven M. Bellovin <smb@research.att.com> while
22   at the University of North Carolina at Chapel Hill.  Later tweaked by
23   a couple of people on Usenet.  Completely overhauled by Rich $alz
24   <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
25
26   Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
27   the right thing about local DST.  Also modified by Paul Eggert
28   <eggert@cs.ucla.edu> in February 2004 to support
29   nanosecond-resolution time stamps, and in October 2004 to support
30   TZ strings in dates.  */
31
32/* FIXME: Check for arithmetic overflow in all cases, not just
33   some of them.  */
34
35#include <config.h>
36
37#include "getdate.h"
38#include "timespec.h"
39
40/* There's no need to extend the stack, so there's no need to involve
41   alloca.  */
42#define YYSTACK_USE_ALLOCA 0
43
44/* Tell Bison how much stack space is needed.  20 should be plenty for
45   this grammar, which is not right recursive.  Beware setting it too
46   high, since that might cause problems on machines whose
47   implementations have lame stack-overflow checking.  */
48#define YYMAXDEPTH 20
49#define YYINITDEPTH YYMAXDEPTH
50
51/* Since the code of getdate.y is not included in the Emacs executable
52   itself, there is no need to #define static in this file.  Even if
53   the code were included in the Emacs executable, it probably
54   wouldn't do any harm to #undef it here; this will only cause
55   problems if we try to write to a static variable, which I don't
56   think this code needs to do.  */
57#ifdef emacs
58# undef static
59#endif
60
61#include <ctype.h>
62#include <limits.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66
67#include "setenv.h"
68#include "xalloc.h"
69
70
71/* ISDIGIT differs from isdigit, as follows:
72   - Its arg may be any int or unsigned int; it need not be an unsigned char
73     or EOF.
74   - It's typically faster.
75   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
76   isdigit unless it's important to use the locale's definition
77   of `digit' even when the host does not conform to POSIX.  */
78#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
79
80#ifndef __attribute__
81# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
82#  define __attribute__(x)
83# endif
84#endif
85
86#ifndef ATTRIBUTE_UNUSED
87# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
88#endif
89
90/* Shift A right by B bits portably, by dividing A by 2**B and
91   truncating towards minus infinity.  A and B should be free of side
92   effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
93   INT_BITS is the number of useful bits in an int.  GNU code can
94   assume that INT_BITS is at least 32.
95
96   ISO C99 says that A >> B is implementation-defined if A < 0.  Some
97   implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
98   right in the usual way when A < 0, so SHR falls back on division if
99   ordinary A >> B doesn't seem to be the usual signed shift.  */
100#define SHR(a, b)	\
101  (-1 >> 1 == -1	\
102   ? (a) >> (b)		\
103   : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
104
105#define EPOCH_YEAR 1970
106#define TM_YEAR_BASE 1900
107
108#define HOUR(x) ((x) * 60)
109
110/* An integer value, and the number of digits in its textual
111   representation.  */
112typedef struct
113{
114  bool negative;
115  long int value;
116  size_t digits;
117} textint;
118
119/* An entry in the lexical lookup table.  */
120typedef struct
121{
122  char const *name;
123  int type;
124  int value;
125} table;
126
127/* Meridian: am, pm, or 24-hour style.  */
128enum { MERam, MERpm, MER24 };
129
130enum { BILLION = 1000000000, LOG10_BILLION = 9 };
131
132/* Relative times.  */
133typedef struct
134{
135  /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
136  long int year;
137  long int month;
138  long int day;
139  long int hour;
140  long int minutes;
141  long int seconds;
142  long int ns;
143} relative_time;
144
145#if HAVE_COMPOUND_LITERALS
146# define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
147#else
148static relative_time const RELATIVE_TIME_0;
149#endif
150
151/* Information passed to and from the parser.  */
152typedef struct
153{
154  /* The input string remaining to be parsed. */
155  const char *input;
156
157  /* N, if this is the Nth Tuesday.  */
158  long int day_ordinal;
159
160  /* Day of week; Sunday is 0.  */
161  int day_number;
162
163  /* tm_isdst flag for the local zone.  */
164  int local_isdst;
165
166  /* Time zone, in minutes east of UTC.  */
167  long int time_zone;
168
169  /* Style used for time.  */
170  int meridian;
171
172  /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds.  */
173  textint year;
174  long int month;
175  long int day;
176  long int hour;
177  long int minutes;
178  struct timespec seconds; /* includes nanoseconds */
179
180  /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
181  relative_time rel;
182
183  /* Presence or counts of nonterminals of various flavors parsed so far.  */
184  bool timespec_seen;
185  bool rels_seen;
186  size_t dates_seen;
187  size_t days_seen;
188  size_t local_zones_seen;
189  size_t dsts_seen;
190  size_t times_seen;
191  size_t zones_seen;
192
193  /* Table of local time zone abbrevations, terminated by a null entry.  */
194  table local_time_zone_table[3];
195} parser_control;
196
197union YYSTYPE;
198static int yylex (union YYSTYPE *, parser_control *);
199static int yyerror (parser_control const *, char const *);
200static long int time_zone_hhmm (textint, long int);
201
202%}
203
204/* We want a reentrant parser, even if the TZ manipulation and the calls to
205   localtime and gmtime are not reentrant.  */
206%pure-parser
207%parse-param { parser_control *pc }
208%lex-param { parser_control *pc }
209
210/* This grammar has 20 shift/reduce conflicts. */
211%expect 20
212
213%union
214{
215  long int intval;
216  textint textintval;
217  struct timespec timespec;
218  relative_time rel;
219}
220
221%token tAGO tDST
222
223%token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
224%token <intval> tDAY_UNIT
225
226%token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
227%token <intval> tMONTH tORDINAL tZONE
228
229%token <textintval> tSNUMBER tUNUMBER
230%token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
231
232%type <intval> o_colon_minutes o_merid
233%type <timespec> seconds signed_seconds unsigned_seconds
234
235%type <rel> relunit relunit_snumber
236
237%%
238
239spec:
240    timespec
241  | items
242  ;
243
244timespec:
245    '@' seconds
246      {
247	pc->seconds = $2;
248	pc->timespec_seen = true;
249      }
250  ;
251
252items:
253    /* empty */
254  | items item
255  ;
256
257item:
258    time
259      { pc->times_seen++; }
260  | local_zone
261      { pc->local_zones_seen++; }
262  | zone
263      { pc->zones_seen++; }
264  | date
265      { pc->dates_seen++; }
266  | day
267      { pc->days_seen++; }
268  | rel
269      { pc->rels_seen = true; }
270  | number
271  ;
272
273time:
274    tUNUMBER tMERIDIAN
275      {
276	pc->hour = $1.value;
277	pc->minutes = 0;
278	pc->seconds.tv_sec = 0;
279	pc->seconds.tv_nsec = 0;
280	pc->meridian = $2;
281      }
282  | tUNUMBER ':' tUNUMBER o_merid
283      {
284	pc->hour = $1.value;
285	pc->minutes = $3.value;
286	pc->seconds.tv_sec = 0;
287	pc->seconds.tv_nsec = 0;
288	pc->meridian = $4;
289      }
290  | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
291      {
292	pc->hour = $1.value;
293	pc->minutes = $3.value;
294	pc->seconds.tv_sec = 0;
295	pc->seconds.tv_nsec = 0;
296	pc->meridian = MER24;
297	pc->zones_seen++;
298	pc->time_zone = time_zone_hhmm ($4, $5);
299      }
300  | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
301      {
302	pc->hour = $1.value;
303	pc->minutes = $3.value;
304	pc->seconds = $5;
305	pc->meridian = $6;
306      }
307  | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
308      {
309	pc->hour = $1.value;
310	pc->minutes = $3.value;
311	pc->seconds = $5;
312	pc->meridian = MER24;
313	pc->zones_seen++;
314	pc->time_zone = time_zone_hhmm ($6, $7);
315      }
316  ;
317
318local_zone:
319    tLOCAL_ZONE
320      {
321	pc->local_isdst = $1;
322	pc->dsts_seen += (0 < $1);
323      }
324  | tLOCAL_ZONE tDST
325      {
326	pc->local_isdst = 1;
327	pc->dsts_seen += (0 < $1) + 1;
328      }
329  ;
330
331zone:
332    tZONE
333      { pc->time_zone = $1; }
334  | tZONE relunit_snumber
335      { pc->time_zone = $1;
336	pc->rel.ns += $2.ns;
337	pc->rel.seconds += $2.seconds;
338	pc->rel.minutes += $2.minutes;
339	pc->rel.hour += $2.hour;
340	pc->rel.day += $2.day;
341	pc->rel.month += $2.month;
342	pc->rel.year += $2.year;
343        pc->rels_seen = true; }
344  | tZONE tSNUMBER o_colon_minutes
345      { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
346  | tDAYZONE
347      { pc->time_zone = $1 + 60; }
348  | tZONE tDST
349      { pc->time_zone = $1 + 60; }
350  ;
351
352day:
353    tDAY
354      {
355	pc->day_ordinal = 1;
356	pc->day_number = $1;
357      }
358  | tDAY ','
359      {
360	pc->day_ordinal = 1;
361	pc->day_number = $1;
362      }
363  | tORDINAL tDAY
364      {
365	pc->day_ordinal = $1;
366	pc->day_number = $2;
367      }
368  | tUNUMBER tDAY
369      {
370	pc->day_ordinal = $1.value;
371	pc->day_number = $2;
372      }
373  ;
374
375date:
376    tUNUMBER '/' tUNUMBER
377      {
378	pc->month = $1.value;
379	pc->day = $3.value;
380      }
381  | tUNUMBER '/' tUNUMBER '/' tUNUMBER
382      {
383	/* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
384	   otherwise as MM/DD/YY.
385	   The goal in recognizing YYYY/MM/DD is solely to support legacy
386	   machine-generated dates like those in an RCS log listing.  If
387	   you want portability, use the ISO 8601 format.  */
388	if (4 <= $1.digits)
389	  {
390	    pc->year = $1;
391	    pc->month = $3.value;
392	    pc->day = $5.value;
393	  }
394	else
395	  {
396	    pc->month = $1.value;
397	    pc->day = $3.value;
398	    pc->year = $5;
399	  }
400      }
401  | tUNUMBER tSNUMBER tSNUMBER
402      {
403	/* ISO 8601 format.  YYYY-MM-DD.  */
404	pc->year = $1;
405	pc->month = -$2.value;
406	pc->day = -$3.value;
407      }
408  | tUNUMBER tMONTH tSNUMBER
409      {
410	/* e.g. 17-JUN-1992.  */
411	pc->day = $1.value;
412	pc->month = $2;
413	pc->year.value = -$3.value;
414	pc->year.digits = $3.digits;
415      }
416  | tMONTH tSNUMBER tSNUMBER
417      {
418	/* e.g. JUN-17-1992.  */
419	pc->month = $1;
420	pc->day = -$2.value;
421	pc->year.value = -$3.value;
422	pc->year.digits = $3.digits;
423      }
424  | tMONTH tUNUMBER
425      {
426	pc->month = $1;
427	pc->day = $2.value;
428      }
429  | tMONTH tUNUMBER ',' tUNUMBER
430      {
431	pc->month = $1;
432	pc->day = $2.value;
433	pc->year = $4;
434      }
435  | tUNUMBER tMONTH
436      {
437	pc->day = $1.value;
438	pc->month = $2;
439      }
440  | tUNUMBER tMONTH tUNUMBER
441      {
442	pc->day = $1.value;
443	pc->month = $2;
444	pc->year = $3;
445      }
446  ;
447
448rel:
449    relunit tAGO
450      {
451	pc->rel.ns -= $1.ns;
452	pc->rel.seconds -= $1.seconds;
453	pc->rel.minutes -= $1.minutes;
454	pc->rel.hour -= $1.hour;
455	pc->rel.day -= $1.day;
456	pc->rel.month -= $1.month;
457	pc->rel.year -= $1.year;
458      }
459  | relunit
460      {
461	pc->rel.ns += $1.ns;
462	pc->rel.seconds += $1.seconds;
463	pc->rel.minutes += $1.minutes;
464	pc->rel.hour += $1.hour;
465	pc->rel.day += $1.day;
466	pc->rel.month += $1.month;
467	pc->rel.year += $1.year;
468      }
469  ;
470
471relunit:
472    tORDINAL tYEAR_UNIT
473      { $$ = RELATIVE_TIME_0; $$.year = $1; }
474  | tUNUMBER tYEAR_UNIT
475      { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
476  | tYEAR_UNIT
477      { $$ = RELATIVE_TIME_0; $$.year = 1; }
478  | tORDINAL tMONTH_UNIT
479      { $$ = RELATIVE_TIME_0; $$.month = $1; }
480  | tUNUMBER tMONTH_UNIT
481      { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
482  | tMONTH_UNIT
483      { $$ = RELATIVE_TIME_0; $$.month = 1; }
484  | tORDINAL tDAY_UNIT
485      { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
486  | tUNUMBER tDAY_UNIT
487      { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
488  | tDAY_UNIT
489      { $$ = RELATIVE_TIME_0; $$.day = $1; }
490  | tORDINAL tHOUR_UNIT
491      { $$ = RELATIVE_TIME_0; $$.hour = $1; }
492  | tUNUMBER tHOUR_UNIT
493      { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
494  | tHOUR_UNIT
495      { $$ = RELATIVE_TIME_0; $$.hour = 1; }
496  | tORDINAL tMINUTE_UNIT
497      { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
498  | tUNUMBER tMINUTE_UNIT
499      { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
500  | tMINUTE_UNIT
501      { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
502  | tORDINAL tSEC_UNIT
503      { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
504  | tUNUMBER tSEC_UNIT
505      { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
506  | tSDECIMAL_NUMBER tSEC_UNIT
507      { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
508  | tUDECIMAL_NUMBER tSEC_UNIT
509      { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
510  | tSEC_UNIT
511      { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
512  | relunit_snumber
513  ;
514
515relunit_snumber:
516    tSNUMBER tYEAR_UNIT
517      { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
518  | tSNUMBER tMONTH_UNIT
519      { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
520  | tSNUMBER tDAY_UNIT
521      { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
522  | tSNUMBER tHOUR_UNIT
523      { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
524  | tSNUMBER tMINUTE_UNIT
525      { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
526  | tSNUMBER tSEC_UNIT
527      { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
528  ;
529
530seconds: signed_seconds | unsigned_seconds;
531
532signed_seconds:
533    tSDECIMAL_NUMBER
534  | tSNUMBER
535      { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
536  ;
537
538unsigned_seconds:
539    tUDECIMAL_NUMBER
540  | tUNUMBER
541      { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
542  ;
543
544number:
545    tUNUMBER
546      {
547	if (pc->dates_seen && ! pc->year.digits
548	    && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
549	  pc->year = $1;
550	else
551	  {
552	    if (4 < $1.digits)
553	      {
554		pc->dates_seen++;
555		pc->day = $1.value % 100;
556		pc->month = ($1.value / 100) % 100;
557		pc->year.value = $1.value / 10000;
558		pc->year.digits = $1.digits - 4;
559	      }
560	    else
561	      {
562		pc->times_seen++;
563		if ($1.digits <= 2)
564		  {
565		    pc->hour = $1.value;
566		    pc->minutes = 0;
567		  }
568		else
569		  {
570		    pc->hour = $1.value / 100;
571		    pc->minutes = $1.value % 100;
572		  }
573		pc->seconds.tv_sec = 0;
574		pc->seconds.tv_nsec = 0;
575		pc->meridian = MER24;
576	      }
577	  }
578      }
579  ;
580
581o_colon_minutes:
582    /* empty */
583      { $$ = -1; }
584  | ':' tUNUMBER
585      { $$ = $2.value; }
586  ;
587
588o_merid:
589    /* empty */
590      { $$ = MER24; }
591  | tMERIDIAN
592      { $$ = $1; }
593  ;
594
595%%
596
597static table const meridian_table[] =
598{
599  { "AM",   tMERIDIAN, MERam },
600  { "A.M.", tMERIDIAN, MERam },
601  { "PM",   tMERIDIAN, MERpm },
602  { "P.M.", tMERIDIAN, MERpm },
603  { NULL, 0, 0 }
604};
605
606static table const dst_table[] =
607{
608  { "DST", tDST, 0 }
609};
610
611static table const month_and_day_table[] =
612{
613  { "JANUARY",	tMONTH,	 1 },
614  { "FEBRUARY",	tMONTH,	 2 },
615  { "MARCH",	tMONTH,	 3 },
616  { "APRIL",	tMONTH,	 4 },
617  { "MAY",	tMONTH,	 5 },
618  { "JUNE",	tMONTH,	 6 },
619  { "JULY",	tMONTH,	 7 },
620  { "AUGUST",	tMONTH,	 8 },
621  { "SEPTEMBER",tMONTH,	 9 },
622  { "SEPT",	tMONTH,	 9 },
623  { "OCTOBER",	tMONTH,	10 },
624  { "NOVEMBER",	tMONTH,	11 },
625  { "DECEMBER",	tMONTH,	12 },
626  { "SUNDAY",	tDAY,	 0 },
627  { "MONDAY",	tDAY,	 1 },
628  { "TUESDAY",	tDAY,	 2 },
629  { "TUES",	tDAY,	 2 },
630  { "WEDNESDAY",tDAY,	 3 },
631  { "WEDNES",	tDAY,	 3 },
632  { "THURSDAY",	tDAY,	 4 },
633  { "THUR",	tDAY,	 4 },
634  { "THURS",	tDAY,	 4 },
635  { "FRIDAY",	tDAY,	 5 },
636  { "SATURDAY",	tDAY,	 6 },
637  { NULL, 0, 0 }
638};
639
640static table const time_units_table[] =
641{
642  { "YEAR",	tYEAR_UNIT,	 1 },
643  { "MONTH",	tMONTH_UNIT,	 1 },
644  { "FORTNIGHT",tDAY_UNIT,	14 },
645  { "WEEK",	tDAY_UNIT,	 7 },
646  { "DAY",	tDAY_UNIT,	 1 },
647  { "HOUR",	tHOUR_UNIT,	 1 },
648  { "MINUTE",	tMINUTE_UNIT,	 1 },
649  { "MIN",	tMINUTE_UNIT,	 1 },
650  { "SECOND",	tSEC_UNIT,	 1 },
651  { "SEC",	tSEC_UNIT,	 1 },
652  { NULL, 0, 0 }
653};
654
655/* Assorted relative-time words. */
656static table const relative_time_table[] =
657{
658  { "TOMORROW",	tDAY_UNIT,	 1 },
659  { "YESTERDAY",tDAY_UNIT,	-1 },
660  { "TODAY",	tDAY_UNIT,	 0 },
661  { "NOW",	tDAY_UNIT,	 0 },
662  { "LAST",	tORDINAL,	-1 },
663  { "THIS",	tORDINAL,	 0 },
664  { "NEXT",	tORDINAL,	 1 },
665  { "FIRST",	tORDINAL,	 1 },
666/*{ "SECOND",	tORDINAL,	 2 }, */
667  { "THIRD",	tORDINAL,	 3 },
668  { "FOURTH",	tORDINAL,	 4 },
669  { "FIFTH",	tORDINAL,	 5 },
670  { "SIXTH",	tORDINAL,	 6 },
671  { "SEVENTH",	tORDINAL,	 7 },
672  { "EIGHTH",	tORDINAL,	 8 },
673  { "NINTH",	tORDINAL,	 9 },
674  { "TENTH",	tORDINAL,	10 },
675  { "ELEVENTH",	tORDINAL,	11 },
676  { "TWELFTH",	tORDINAL,	12 },
677  { "AGO",	tAGO,		 1 },
678  { NULL, 0, 0 }
679};
680
681/* The universal time zone table.  These labels can be used even for
682   time stamps that would not otherwise be valid, e.g., GMT time
683   stamps in London during summer.  */
684static table const universal_time_zone_table[] =
685{
686  { "GMT",	tZONE,     HOUR ( 0) },	/* Greenwich Mean */
687  { "UT",	tZONE,     HOUR ( 0) },	/* Universal (Coordinated) */
688  { "UTC",	tZONE,     HOUR ( 0) },
689  { NULL, 0, 0 }
690};
691
692/* The time zone table.  This table is necessarily incomplete, as time
693   zone abbreviations are ambiguous; e.g. Australians interpret "EST"
694   as Eastern time in Australia, not as US Eastern Standard Time.
695   You cannot rely on getdate to handle arbitrary time zone
696   abbreviations; use numeric abbreviations like `-0500' instead.  */
697static table const time_zone_table[] =
698{
699  { "WET",	tZONE,     HOUR ( 0) },	/* Western European */
700  { "WEST",	tDAYZONE,  HOUR ( 0) },	/* Western European Summer */
701  { "BST",	tDAYZONE,  HOUR ( 0) },	/* British Summer */
702  { "ART",	tZONE,	  -HOUR ( 3) },	/* Argentina */
703  { "BRT",	tZONE,	  -HOUR ( 3) },	/* Brazil */
704  { "BRST",	tDAYZONE, -HOUR ( 3) },	/* Brazil Summer */
705  { "NST",	tZONE,	 -(HOUR ( 3) + 30) },	/* Newfoundland Standard */
706  { "NDT",	tDAYZONE,-(HOUR ( 3) + 30) },	/* Newfoundland Daylight */
707  { "AST",	tZONE,    -HOUR ( 4) },	/* Atlantic Standard */
708  { "ADT",	tDAYZONE, -HOUR ( 4) },	/* Atlantic Daylight */
709  { "CLT",	tZONE,    -HOUR ( 4) },	/* Chile */
710  { "CLST",	tDAYZONE, -HOUR ( 4) },	/* Chile Summer */
711  { "EST",	tZONE,    -HOUR ( 5) },	/* Eastern Standard */
712  { "EDT",	tDAYZONE, -HOUR ( 5) },	/* Eastern Daylight */
713  { "CST",	tZONE,    -HOUR ( 6) },	/* Central Standard */
714  { "CDT",	tDAYZONE, -HOUR ( 6) },	/* Central Daylight */
715  { "MST",	tZONE,    -HOUR ( 7) },	/* Mountain Standard */
716  { "MDT",	tDAYZONE, -HOUR ( 7) },	/* Mountain Daylight */
717  { "PST",	tZONE,    -HOUR ( 8) },	/* Pacific Standard */
718  { "PDT",	tDAYZONE, -HOUR ( 8) },	/* Pacific Daylight */
719  { "AKST",	tZONE,    -HOUR ( 9) },	/* Alaska Standard */
720  { "AKDT",	tDAYZONE, -HOUR ( 9) },	/* Alaska Daylight */
721  { "HST",	tZONE,    -HOUR (10) },	/* Hawaii Standard */
722  { "HAST",	tZONE,	  -HOUR (10) },	/* Hawaii-Aleutian Standard */
723  { "HADT",	tDAYZONE, -HOUR (10) },	/* Hawaii-Aleutian Daylight */
724  { "SST",	tZONE,    -HOUR (12) },	/* Samoa Standard */
725  { "WAT",	tZONE,     HOUR ( 1) },	/* West Africa */
726  { "CET",	tZONE,     HOUR ( 1) },	/* Central European */
727  { "CEST",	tDAYZONE,  HOUR ( 1) },	/* Central European Summer */
728  { "MET",	tZONE,     HOUR ( 1) },	/* Middle European */
729  { "MEZ",	tZONE,     HOUR ( 1) },	/* Middle European */
730  { "MEST",	tDAYZONE,  HOUR ( 1) },	/* Middle European Summer */
731  { "MESZ",	tDAYZONE,  HOUR ( 1) },	/* Middle European Summer */
732  { "EET",	tZONE,     HOUR ( 2) },	/* Eastern European */
733  { "EEST",	tDAYZONE,  HOUR ( 2) },	/* Eastern European Summer */
734  { "CAT",	tZONE,	   HOUR ( 2) },	/* Central Africa */
735  { "SAST",	tZONE,	   HOUR ( 2) },	/* South Africa Standard */
736  { "EAT",	tZONE,	   HOUR ( 3) },	/* East Africa */
737  { "MSK",	tZONE,	   HOUR ( 3) },	/* Moscow */
738  { "MSD",	tDAYZONE,  HOUR ( 3) },	/* Moscow Daylight */
739  { "IST",	tZONE,	  (HOUR ( 5) + 30) },	/* India Standard */
740  { "SGT",	tZONE,     HOUR ( 8) },	/* Singapore */
741  { "KST",	tZONE,     HOUR ( 9) },	/* Korea Standard */
742  { "JST",	tZONE,     HOUR ( 9) },	/* Japan Standard */
743  { "GST",	tZONE,     HOUR (10) },	/* Guam Standard */
744  { "NZST",	tZONE,     HOUR (12) },	/* New Zealand Standard */
745  { "NZDT",	tDAYZONE,  HOUR (12) },	/* New Zealand Daylight */
746  { NULL, 0, 0 }
747};
748
749/* Military time zone table. */
750static table const military_table[] =
751{
752  { "A", tZONE,	-HOUR ( 1) },
753  { "B", tZONE,	-HOUR ( 2) },
754  { "C", tZONE,	-HOUR ( 3) },
755  { "D", tZONE,	-HOUR ( 4) },
756  { "E", tZONE,	-HOUR ( 5) },
757  { "F", tZONE,	-HOUR ( 6) },
758  { "G", tZONE,	-HOUR ( 7) },
759  { "H", tZONE,	-HOUR ( 8) },
760  { "I", tZONE,	-HOUR ( 9) },
761  { "K", tZONE,	-HOUR (10) },
762  { "L", tZONE,	-HOUR (11) },
763  { "M", tZONE,	-HOUR (12) },
764  { "N", tZONE,	 HOUR ( 1) },
765  { "O", tZONE,	 HOUR ( 2) },
766  { "P", tZONE,	 HOUR ( 3) },
767  { "Q", tZONE,	 HOUR ( 4) },
768  { "R", tZONE,	 HOUR ( 5) },
769  { "S", tZONE,	 HOUR ( 6) },
770  { "T", tZONE,	 HOUR ( 7) },
771  { "U", tZONE,	 HOUR ( 8) },
772  { "V", tZONE,	 HOUR ( 9) },
773  { "W", tZONE,	 HOUR (10) },
774  { "X", tZONE,	 HOUR (11) },
775  { "Y", tZONE,	 HOUR (12) },
776  { "Z", tZONE,	 HOUR ( 0) },
777  { NULL, 0, 0 }
778};
779
780
781
782/* Convert a time zone expressed as HH:MM into an integer count of
783   minutes.  If MM is negative, then S is of the form HHMM and needs
784   to be picked apart; otherwise, S is of the form HH.  */
785
786static long int
787time_zone_hhmm (textint s, long int mm)
788{
789  if (mm < 0)
790    return (s.value / 100) * 60 + s.value % 100;
791  else
792    return s.value * 60 + (s.negative ? -mm : mm);
793}
794
795static int
796to_hour (long int hours, int meridian)
797{
798  switch (meridian)
799    {
800    default: /* Pacify GCC.  */
801    case MER24:
802      return 0 <= hours && hours < 24 ? hours : -1;
803    case MERam:
804      return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
805    case MERpm:
806      return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
807    }
808}
809
810static long int
811to_year (textint textyear)
812{
813  long int year = textyear.value;
814
815  if (year < 0)
816    year = -year;
817
818  /* XPG4 suggests that years 00-68 map to 2000-2068, and
819     years 69-99 map to 1969-1999.  */
820  else if (textyear.digits == 2)
821    year += year < 69 ? 2000 : 1900;
822
823  return year;
824}
825
826static table const *
827lookup_zone (parser_control const *pc, char const *name)
828{
829  table const *tp;
830
831  for (tp = universal_time_zone_table; tp->name; tp++)
832    if (strcmp (name, tp->name) == 0)
833      return tp;
834
835  /* Try local zone abbreviations before those in time_zone_table, as
836     the local ones are more likely to be right.  */
837  for (tp = pc->local_time_zone_table; tp->name; tp++)
838    if (strcmp (name, tp->name) == 0)
839      return tp;
840
841  for (tp = time_zone_table; tp->name; tp++)
842    if (strcmp (name, tp->name) == 0)
843      return tp;
844
845  return NULL;
846}
847
848#if ! HAVE_TM_GMTOFF
849/* Yield the difference between *A and *B,
850   measured in seconds, ignoring leap seconds.
851   The body of this function is taken directly from the GNU C Library;
852   see src/strftime.c.  */
853static long int
854tm_diff (struct tm const *a, struct tm const *b)
855{
856  /* Compute intervening leap days correctly even if year is negative.
857     Take care to avoid int overflow in leap day calculations.  */
858  int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
859  int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
860  int a100 = a4 / 25 - (a4 % 25 < 0);
861  int b100 = b4 / 25 - (b4 % 25 < 0);
862  int a400 = SHR (a100, 2);
863  int b400 = SHR (b100, 2);
864  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
865  long int ayear = a->tm_year;
866  long int years = ayear - b->tm_year;
867  long int days = (365 * years + intervening_leap_days
868		   + (a->tm_yday - b->tm_yday));
869  return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
870		+ (a->tm_min - b->tm_min))
871	  + (a->tm_sec - b->tm_sec));
872}
873#endif /* ! HAVE_TM_GMTOFF */
874
875static table const *
876lookup_word (parser_control const *pc, char *word)
877{
878  char *p;
879  char *q;
880  size_t wordlen;
881  table const *tp;
882  bool period_found;
883  bool abbrev;
884
885  /* Make it uppercase.  */
886  for (p = word; *p; p++)
887    {
888      unsigned char ch = *p;
889      *p = toupper (ch);
890    }
891
892  for (tp = meridian_table; tp->name; tp++)
893    if (strcmp (word, tp->name) == 0)
894      return tp;
895
896  /* See if we have an abbreviation for a month. */
897  wordlen = strlen (word);
898  abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
899
900  for (tp = month_and_day_table; tp->name; tp++)
901    if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
902      return tp;
903
904  if ((tp = lookup_zone (pc, word)))
905    return tp;
906
907  if (strcmp (word, dst_table[0].name) == 0)
908    return dst_table;
909
910  for (tp = time_units_table; tp->name; tp++)
911    if (strcmp (word, tp->name) == 0)
912      return tp;
913
914  /* Strip off any plural and try the units table again. */
915  if (word[wordlen - 1] == 'S')
916    {
917      word[wordlen - 1] = '\0';
918      for (tp = time_units_table; tp->name; tp++)
919	if (strcmp (word, tp->name) == 0)
920	  return tp;
921      word[wordlen - 1] = 'S';	/* For "this" in relative_time_table.  */
922    }
923
924  for (tp = relative_time_table; tp->name; tp++)
925    if (strcmp (word, tp->name) == 0)
926      return tp;
927
928  /* Military time zones. */
929  if (wordlen == 1)
930    for (tp = military_table; tp->name; tp++)
931      if (word[0] == tp->name[0])
932	return tp;
933
934  /* Drop out any periods and try the time zone table again. */
935  for (period_found = false, p = q = word; (*p = *q); q++)
936    if (*q == '.')
937      period_found = true;
938    else
939      p++;
940  if (period_found && (tp = lookup_zone (pc, word)))
941    return tp;
942
943  return NULL;
944}
945
946static int
947yylex (YYSTYPE *lvalp, parser_control *pc)
948{
949  unsigned char c;
950  size_t count;
951
952  for (;;)
953    {
954      while (c = *pc->input, isspace (c))
955	pc->input++;
956
957      if (ISDIGIT (c) || c == '-' || c == '+')
958	{
959	  char const *p;
960	  int sign;
961	  unsigned long int value;
962	  if (c == '-' || c == '+')
963	    {
964	      sign = c == '-' ? -1 : 1;
965	      while (c = *++pc->input, isspace (c))
966		continue;
967	      if (! ISDIGIT (c))
968		/* skip the '-' sign */
969		continue;
970	    }
971	  else
972	    sign = 0;
973	  p = pc->input;
974	  for (value = 0; ; value *= 10)
975	    {
976	      unsigned long int value1 = value + (c - '0');
977	      if (value1 < value)
978		return '?';
979	      value = value1;
980	      c = *++p;
981	      if (! ISDIGIT (c))
982		break;
983	      if (ULONG_MAX / 10 < value)
984		return '?';
985	    }
986	  if ((c == '.' || c == ',') && ISDIGIT (p[1]))
987	    {
988	      time_t s;
989	      int ns;
990	      int digits;
991	      unsigned long int value1;
992
993	      /* Check for overflow when converting value to time_t.  */
994	      if (sign < 0)
995		{
996		  s = - value;
997		  if (0 < s)
998		    return '?';
999		  value1 = -s;
1000		}
1001	      else
1002		{
1003		  s = value;
1004		  if (s < 0)
1005		    return '?';
1006		  value1 = s;
1007		}
1008	      if (value != value1)
1009		return '?';
1010
1011	      /* Accumulate fraction, to ns precision.  */
1012	      p++;
1013	      ns = *p++ - '0';
1014	      for (digits = 2; digits <= LOG10_BILLION; digits++)
1015		{
1016		  ns *= 10;
1017		  if (ISDIGIT (*p))
1018		    ns += *p++ - '0';
1019		}
1020
1021	      /* Skip excess digits, truncating toward -Infinity.  */
1022	      if (sign < 0)
1023		for (; ISDIGIT (*p); p++)
1024		  if (*p != '0')
1025		    {
1026		      ns++;
1027		      break;
1028		    }
1029	      while (ISDIGIT (*p))
1030		p++;
1031
1032	      /* Adjust to the timespec convention, which is that
1033		 tv_nsec is always a positive offset even if tv_sec is
1034		 negative.  */
1035	      if (sign < 0 && ns)
1036		{
1037		  s--;
1038		  if (! (s < 0))
1039		    return '?';
1040		  ns = BILLION - ns;
1041		}
1042
1043	      lvalp->timespec.tv_sec = s;
1044	      lvalp->timespec.tv_nsec = ns;
1045	      pc->input = p;
1046	      return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1047	    }
1048	  else
1049	    {
1050	      lvalp->textintval.negative = sign < 0;
1051	      if (sign < 0)
1052		{
1053		  lvalp->textintval.value = - value;
1054		  if (0 < lvalp->textintval.value)
1055		    return '?';
1056		}
1057	      else
1058		{
1059		  lvalp->textintval.value = value;
1060		  if (lvalp->textintval.value < 0)
1061		    return '?';
1062		}
1063	      lvalp->textintval.digits = p - pc->input;
1064	      pc->input = p;
1065	      return sign ? tSNUMBER : tUNUMBER;
1066	    }
1067	}
1068
1069      if (isalpha (c))
1070	{
1071	  char buff[20];
1072	  char *p = buff;
1073	  table const *tp;
1074
1075	  do
1076	    {
1077	      if (p < buff + sizeof buff - 1)
1078		*p++ = c;
1079	      c = *++pc->input;
1080	    }
1081	  while (isalpha (c) || c == '.');
1082
1083	  *p = '\0';
1084	  tp = lookup_word (pc, buff);
1085	  if (! tp)
1086	    return '?';
1087	  lvalp->intval = tp->value;
1088	  return tp->type;
1089	}
1090
1091      if (c != '(')
1092	return *pc->input++;
1093      count = 0;
1094      do
1095	{
1096	  c = *pc->input++;
1097	  if (c == '\0')
1098	    return c;
1099	  if (c == '(')
1100	    count++;
1101	  else if (c == ')')
1102	    count--;
1103	}
1104      while (count != 0);
1105    }
1106}
1107
1108/* Do nothing if the parser reports an error.  */
1109static int
1110yyerror (parser_control const *pc ATTRIBUTE_UNUSED,
1111	 char const *s ATTRIBUTE_UNUSED)
1112{
1113  return 0;
1114}
1115
1116/* If *TM0 is the old and *TM1 is the new value of a struct tm after
1117   passing it to mktime, return true if it's OK that mktime returned T.
1118   It's not OK if *TM0 has out-of-range members.  */
1119
1120static bool
1121mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1122{
1123  if (t == (time_t) -1)
1124    {
1125      /* Guard against falsely reporting an error when parsing a time
1126	 stamp that happens to equal (time_t) -1, on a host that
1127	 supports such a time stamp.  */
1128      tm1 = localtime (&t);
1129      if (!tm1)
1130	return false;
1131    }
1132
1133  return ! ((tm0->tm_sec ^ tm1->tm_sec)
1134	    | (tm0->tm_min ^ tm1->tm_min)
1135	    | (tm0->tm_hour ^ tm1->tm_hour)
1136	    | (tm0->tm_mday ^ tm1->tm_mday)
1137	    | (tm0->tm_mon ^ tm1->tm_mon)
1138	    | (tm0->tm_year ^ tm1->tm_year));
1139}
1140
1141/* A reasonable upper bound for the size of ordinary TZ strings.
1142   Use heap allocation if TZ's length exceeds this.  */
1143enum { TZBUFSIZE = 100 };
1144
1145/* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1146   otherwise.  */
1147static char *
1148get_tz (char tzbuf[TZBUFSIZE])
1149{
1150  char *tz = getenv ("TZ");
1151  if (tz)
1152    {
1153      size_t tzsize = strlen (tz) + 1;
1154      tz = (tzsize <= TZBUFSIZE
1155	    ? memcpy (tzbuf, tz, tzsize)
1156	    : xmemdup (tz, tzsize));
1157    }
1158  return tz;
1159}
1160
1161/* Parse a date/time string, storing the resulting time value into *RESULT.
1162   The string itself is pointed to by P.  Return true if successful.
1163   P can be an incomplete or relative time specification; if so, use
1164   *NOW as the basis for the returned time.  */
1165bool
1166get_date (struct timespec *result, char const *p, struct timespec const *now)
1167{
1168  time_t Start;
1169  long int Start_ns;
1170  struct tm const *tmp;
1171  struct tm tm;
1172  struct tm tm0;
1173  parser_control pc;
1174  struct timespec gettime_buffer;
1175  unsigned char c;
1176  bool tz_was_altered = false;
1177  char *tz0 = NULL;
1178  char tz0buf[TZBUFSIZE];
1179  bool ok = true;
1180
1181  if (! now)
1182    {
1183      gettime (&gettime_buffer);
1184      now = &gettime_buffer;
1185    }
1186
1187  Start = now->tv_sec;
1188  Start_ns = now->tv_nsec;
1189
1190  tmp = localtime (&now->tv_sec);
1191  if (! tmp)
1192    return false;
1193
1194  while (c = *p, isspace (c))
1195    p++;
1196
1197  if (strncmp (p, "TZ=\"", 4) == 0)
1198    {
1199      char const *tzbase = p + 4;
1200      size_t tzsize = 1;
1201      char const *s;
1202
1203      for (s = tzbase; *s; s++, tzsize++)
1204	if (*s == '\\')
1205	  {
1206	    s++;
1207	    if (! (*s == '\\' || *s == '"'))
1208	      break;
1209	  }
1210	else if (*s == '"')
1211	  {
1212	    char *z;
1213	    char *tz1;
1214	    char tz1buf[TZBUFSIZE];
1215	    bool large_tz = TZBUFSIZE < tzsize;
1216	    bool setenv_ok;
1217	    tz0 = get_tz (tz0buf);
1218	    z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1219	    for (s = tzbase; *s != '"'; s++)
1220	      *z++ = *(s += *s == '\\');
1221	    *z = '\0';
1222	    setenv_ok = setenv ("TZ", tz1, 1) == 0;
1223	    if (large_tz)
1224	      free (tz1);
1225	    if (!setenv_ok)
1226	      goto fail;
1227	    tz_was_altered = true;
1228	    p = s + 1;
1229	  }
1230    }
1231
1232  pc.input = p;
1233  pc.year.value = tmp->tm_year;
1234  pc.year.value += TM_YEAR_BASE;
1235  pc.year.digits = 0;
1236  pc.month = tmp->tm_mon + 1;
1237  pc.day = tmp->tm_mday;
1238  pc.hour = tmp->tm_hour;
1239  pc.minutes = tmp->tm_min;
1240  pc.seconds.tv_sec = tmp->tm_sec;
1241  pc.seconds.tv_nsec = Start_ns;
1242  tm.tm_isdst = tmp->tm_isdst;
1243
1244  pc.meridian = MER24;
1245  pc.rel = RELATIVE_TIME_0;
1246  pc.timespec_seen = false;
1247  pc.rels_seen = false;
1248  pc.dates_seen = 0;
1249  pc.days_seen = 0;
1250  pc.times_seen = 0;
1251  pc.local_zones_seen = 0;
1252  pc.dsts_seen = 0;
1253  pc.zones_seen = 0;
1254
1255#if HAVE_STRUCT_TM_TM_ZONE
1256  pc.local_time_zone_table[0].name = tmp->tm_zone;
1257  pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1258  pc.local_time_zone_table[0].value = tmp->tm_isdst;
1259  pc.local_time_zone_table[1].name = NULL;
1260
1261  /* Probe the names used in the next three calendar quarters, looking
1262     for a tm_isdst different from the one we already have.  */
1263  {
1264    int quarter;
1265    for (quarter = 1; quarter <= 3; quarter++)
1266      {
1267	time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1268	struct tm const *probe_tm = localtime (&probe);
1269	if (probe_tm && probe_tm->tm_zone
1270	    && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1271	  {
1272	      {
1273		pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1274		pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1275		pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1276		pc.local_time_zone_table[2].name = NULL;
1277	      }
1278	    break;
1279	  }
1280      }
1281  }
1282#else
1283#if HAVE_TZNAME
1284  {
1285# ifndef tzname
1286    extern char *tzname[];
1287# endif
1288    int i;
1289    for (i = 0; i < 2; i++)
1290      {
1291	pc.local_time_zone_table[i].name = tzname[i];
1292	pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1293	pc.local_time_zone_table[i].value = i;
1294      }
1295    pc.local_time_zone_table[i].name = NULL;
1296  }
1297#else
1298  pc.local_time_zone_table[0].name = NULL;
1299#endif
1300#endif
1301
1302  if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1303      && ! strcmp (pc.local_time_zone_table[0].name,
1304		   pc.local_time_zone_table[1].name))
1305    {
1306      /* This locale uses the same abbrevation for standard and
1307	 daylight times.  So if we see that abbreviation, we don't
1308	 know whether it's daylight time.  */
1309      pc.local_time_zone_table[0].value = -1;
1310      pc.local_time_zone_table[1].name = NULL;
1311    }
1312
1313  if (yyparse (&pc) != 0)
1314    goto fail;
1315
1316  if (pc.timespec_seen)
1317    *result = pc.seconds;
1318  else
1319    {
1320      if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1321	       | (pc.local_zones_seen + pc.zones_seen)))
1322	goto fail;
1323
1324      tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1325      tm.tm_mon = pc.month - 1;
1326      tm.tm_mday = pc.day;
1327      if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1328	{
1329	  tm.tm_hour = to_hour (pc.hour, pc.meridian);
1330	  if (tm.tm_hour < 0)
1331	    goto fail;
1332	  tm.tm_min = pc.minutes;
1333	  tm.tm_sec = pc.seconds.tv_sec;
1334	}
1335      else
1336	{
1337	  tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1338	  pc.seconds.tv_nsec = 0;
1339	}
1340
1341      /* Let mktime deduce tm_isdst if we have an absolute time stamp.  */
1342      if (pc.dates_seen | pc.days_seen | pc.times_seen)
1343	tm.tm_isdst = -1;
1344
1345      /* But if the input explicitly specifies local time with or without
1346	 DST, give mktime that information.  */
1347      if (pc.local_zones_seen)
1348	tm.tm_isdst = pc.local_isdst;
1349
1350      tm0 = tm;
1351
1352      Start = mktime (&tm);
1353
1354      if (! mktime_ok (&tm0, &tm, Start))
1355	{
1356	  if (! pc.zones_seen)
1357	    goto fail;
1358	  else
1359	    {
1360	      /* Guard against falsely reporting errors near the time_t
1361		 boundaries when parsing times in other time zones.  For
1362		 example, suppose the input string "1969-12-31 23:00:00 -0100",
1363		 the current time zone is 8 hours ahead of UTC, and the min
1364		 time_t value is 1970-01-01 00:00:00 UTC.  Then the min
1365		 localtime value is 1970-01-01 08:00:00, and mktime will
1366		 therefore fail on 1969-12-31 23:00:00.  To work around the
1367		 problem, set the time zone to 1 hour behind UTC temporarily
1368		 by setting TZ="XXX1:00" and try mktime again.  */
1369
1370	      long int time_zone = pc.time_zone;
1371	      long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1372	      long int abs_time_zone_hour = abs_time_zone / 60;
1373	      int abs_time_zone_min = abs_time_zone % 60;
1374	      char tz1buf[sizeof "XXX+0:00"
1375			  + sizeof pc.time_zone * CHAR_BIT / 3];
1376	      if (!tz_was_altered)
1377		tz0 = get_tz (tz0buf);
1378	      sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1379		       abs_time_zone_hour, abs_time_zone_min);
1380	      if (setenv ("TZ", tz1buf, 1) != 0)
1381		goto fail;
1382	      tz_was_altered = true;
1383	      tm = tm0;
1384	      Start = mktime (&tm);
1385	      if (! mktime_ok (&tm0, &tm, Start))
1386		goto fail;
1387	    }
1388	}
1389
1390      if (pc.days_seen && ! pc.dates_seen)
1391	{
1392	  tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1393			 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1394	  tm.tm_isdst = -1;
1395	  Start = mktime (&tm);
1396	  if (Start == (time_t) -1)
1397	    goto fail;
1398	}
1399
1400      if (pc.zones_seen)
1401	{
1402	  long int delta = pc.time_zone * 60;
1403	  time_t t1;
1404#ifdef HAVE_TM_GMTOFF
1405	  delta -= tm.tm_gmtoff;
1406#else
1407	  time_t t = Start;
1408	  struct tm const *gmt = gmtime (&t);
1409	  if (! gmt)
1410	    goto fail;
1411	  delta -= tm_diff (&tm, gmt);
1412#endif
1413	  t1 = Start - delta;
1414	  if ((Start < t1) != (delta < 0))
1415	    goto fail;	/* time_t overflow */
1416	  Start = t1;
1417	}
1418
1419      /* Add relative date.  */
1420      if (pc.rel.year | pc.rel.month | pc.rel.day)
1421	{
1422	  int year = tm.tm_year + pc.rel.year;
1423	  int month = tm.tm_mon + pc.rel.month;
1424	  int day = tm.tm_mday + pc.rel.day;
1425	  if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1426	      | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1427	      | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1428	    goto fail;
1429	  tm.tm_year = year;
1430	  tm.tm_mon = month;
1431	  tm.tm_mday = day;
1432	  tm.tm_hour = tm0.tm_hour;
1433	  tm.tm_min = tm0.tm_min;
1434	  tm.tm_sec = tm0.tm_sec;
1435	  tm.tm_isdst = tm0.tm_isdst;
1436	  Start = mktime (&tm);
1437	  if (Start == (time_t) -1)
1438	    goto fail;
1439	}
1440
1441      /* Add relative hours, minutes, and seconds.  On hosts that support
1442	 leap seconds, ignore the possibility of leap seconds; e.g.,
1443	 "+ 10 minutes" adds 600 seconds, even if one of them is a
1444	 leap second.  Typically this is not what the user wants, but it's
1445	 too hard to do it the other way, because the time zone indicator
1446	 must be applied before relative times, and if mktime is applied
1447	 again the time zone will be lost.  */
1448      {
1449	long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1450	long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1451	time_t t0 = Start;
1452	long int d1 = 60 * 60 * pc.rel.hour;
1453	time_t t1 = t0 + d1;
1454	long int d2 = 60 * pc.rel.minutes;
1455	time_t t2 = t1 + d2;
1456	long int d3 = pc.rel.seconds;
1457	time_t t3 = t2 + d3;
1458	long int d4 = (sum_ns - normalized_ns) / BILLION;
1459	time_t t4 = t3 + d4;
1460
1461	if ((d1 / (60 * 60) ^ pc.rel.hour)
1462	    | (d2 / 60 ^ pc.rel.minutes)
1463	    | ((t1 < t0) ^ (d1 < 0))
1464	    | ((t2 < t1) ^ (d2 < 0))
1465	    | ((t3 < t2) ^ (d3 < 0))
1466	    | ((t4 < t3) ^ (d4 < 0)))
1467	  goto fail;
1468
1469	result->tv_sec = t4;
1470	result->tv_nsec = normalized_ns;
1471      }
1472    }
1473
1474  goto done;
1475
1476 fail:
1477  ok = false;
1478 done:
1479  if (tz_was_altered)
1480    ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1481  if (tz0 != tz0buf)
1482    free (tz0);
1483  return ok;
1484}
1485
1486#if TEST
1487
1488int
1489main (int ac, char **av)
1490{
1491  char buff[BUFSIZ];
1492
1493  printf ("Enter date, or blank line to exit.\n\t> ");
1494  fflush (stdout);
1495
1496  buff[BUFSIZ - 1] = '\0';
1497  while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1498    {
1499      struct timespec d;
1500      struct tm const *tm;
1501      if (! get_date (&d, buff, NULL))
1502	printf ("Bad format - couldn't convert.\n");
1503      else if (! (tm = localtime (&d.tv_sec)))
1504	{
1505	  long int sec = d.tv_sec;
1506	  printf ("localtime (%ld) failed\n", sec);
1507	}
1508      else
1509	{
1510	  int ns = d.tv_nsec;
1511	  printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1512		  tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1513		  tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1514	}
1515      printf ("\t> ");
1516      fflush (stdout);
1517    }
1518  return 0;
1519}
1520#endif /* TEST */
1521