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