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