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