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