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