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