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