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