Use Bison 1.875 features, and some minor
[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, and in February 2004 to support
26    nanosecond-resolution time stamps.  Unlike previous versions, this
27    version is reentrant.  */
28
29 /* FIXME: Check for arithmetic overflow in all cases, not just
30    some of them.
31
32    FIXME: The current code uses 'int' to count seconds; it should use
33    something like 'intmax_t' to support time stamps that don't fit in
34    32 bits.  */
35
36 #ifdef HAVE_CONFIG_H
37 # include <config.h>
38 #endif
39
40 #include "getdate.h"
41
42 #include <alloca.h>
43
44 /* Since the code of getdate.y is not included in the Emacs executable
45    itself, there is no need to #define static in this file.  Even if
46    the code were included in the Emacs executable, it probably
47    wouldn't do any harm to #undef it here; this will only cause
48    problems if we try to write to a static variable, which I don't
49    think this code needs to do.  */
50 #ifdef emacs
51 # undef static
52 #endif
53
54 #include <ctype.h>
55 #include <limits.h>
56
57 #if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
58 # define IN_CTYPE_DOMAIN(c) 1
59 #else
60 # define IN_CTYPE_DOMAIN(c) isascii (c)
61 #endif
62
63 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
64 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
65 #define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
66 #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
67
68 /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
69    - Its arg may be any int or unsigned int; it need not be an unsigned char.
70    - It's guaranteed to evaluate its argument exactly once.
71    - It's typically faster.
72    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
73    ISDIGIT_LOCALE unless it's important to use the locale's definition
74    of `digit' even when the host does not conform to POSIX.  */
75 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
76
77 #include <string.h>
78
79 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
80 # define __attribute__(x)
81 #endif
82
83 #ifndef ATTRIBUTE_UNUSED
84 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
85 #endif
86
87 #define EPOCH_YEAR 1970
88 #define TM_YEAR_BASE 1900
89
90 #define HOUR(x) ((x) * 60)
91
92 /* An integer value, and the number of digits in its textual
93    representation.  */
94 typedef struct
95 {
96   long int value;
97   size_t digits;
98 } textint;
99
100 /* An entry in the lexical lookup table.  */
101 typedef struct
102 {
103   char const *name;
104   int type;
105   int value;
106 } table;
107
108 /* Meridian: am, pm, or 24-hour style.  */
109 enum { MERam, MERpm, MER24 };
110
111 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
112
113 /* Information passed to and from the parser.  */
114 typedef struct
115 {
116   /* The input string remaining to be parsed. */
117   const char *input;
118
119   /* N, if this is the Nth Tuesday.  */
120   long int day_ordinal;
121
122   /* Day of week; Sunday is 0.  */
123   int day_number;
124
125   /* tm_isdst flag for the local zone.  */
126   int local_isdst;
127
128   /* Time zone, in minutes east of UTC.  */
129   long int time_zone;
130
131   /* Style used for time.  */
132   int meridian;
133
134   /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds.  */
135   textint year;
136   long int month;
137   long int day;
138   long int hour;
139   long int minutes;
140   struct timespec seconds; /* includes nanoseconds */
141
142   /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
143   long int rel_year;
144   long int rel_month;
145   long int rel_day;
146   long int rel_hour;
147   long int rel_minutes;
148   long int rel_seconds;
149   long int rel_ns;
150
151   /* Counts of nonterminals of various flavors parsed so far.  */
152   bool timespec_seen;
153   size_t dates_seen;
154   size_t days_seen;
155   size_t local_zones_seen;
156   size_t rels_seen;
157   size_t times_seen;
158   size_t zones_seen;
159
160   /* Table of local time zone abbrevations, terminated by a null entry.  */
161   table local_time_zone_table[3];
162 } parser_control;
163
164 union YYSTYPE;
165 static int yylex (union YYSTYPE *, parser_control *);
166 static int yyerror (parser_control *, char *);
167
168 %}
169
170 /* We want a reentrant parser.  */
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 /* Parse a date/time string, storing the resulting time value into *RESULT.
994    The string itself is pointed to by P.  Return true if successful.
995    P can be an incomplete or relative time specification; if so, use
996    *NOW as the basis for the returned time.  */
997 bool
998 get_date (struct timespec *result, char const *p, struct timespec const *now)
999 {
1000   time_t Start;
1001   long int Start_ns;
1002   struct tm const *tmp;
1003   struct tm tm;
1004   struct tm tm0;
1005   parser_control pc;
1006   struct timespec gettime_buffer;
1007
1008   if (! now)
1009     {
1010       if (gettime (&gettime_buffer) != 0)
1011         return false;
1012       now = &gettime_buffer;
1013     }
1014
1015   Start = now->tv_sec;
1016   Start_ns = now->tv_nsec;
1017
1018   tmp = localtime (&now->tv_sec);
1019   if (! tmp)
1020     return false;
1021
1022   pc.input = p;
1023   pc.year.value = tmp->tm_year;
1024   pc.year.value += TM_YEAR_BASE;
1025   pc.year.digits = 4;
1026   pc.month = tmp->tm_mon + 1;
1027   pc.day = tmp->tm_mday;
1028   pc.hour = tmp->tm_hour;
1029   pc.minutes = tmp->tm_min;
1030   pc.seconds.tv_sec = tmp->tm_sec;
1031   pc.seconds.tv_nsec = Start_ns;
1032   tm.tm_isdst = tmp->tm_isdst;
1033
1034   pc.meridian = MER24;
1035   pc.rel_ns = 0;
1036   pc.rel_seconds = 0;
1037   pc.rel_minutes = 0;
1038   pc.rel_hour = 0;
1039   pc.rel_day = 0;
1040   pc.rel_month = 0;
1041   pc.rel_year = 0;
1042   pc.timespec_seen = false;
1043   pc.dates_seen = 0;
1044   pc.days_seen = 0;
1045   pc.rels_seen = 0;
1046   pc.times_seen = 0;
1047   pc.local_zones_seen = 0;
1048   pc.zones_seen = 0;
1049
1050 #if HAVE_STRUCT_TM_TM_ZONE
1051   pc.local_time_zone_table[0].name = tmp->tm_zone;
1052   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1053   pc.local_time_zone_table[0].value = tmp->tm_isdst;
1054   pc.local_time_zone_table[1].name = NULL;
1055
1056   /* Probe the names used in the next three calendar quarters, looking
1057      for a tm_isdst different from the one we already have.  */
1058   {
1059     int quarter;
1060     for (quarter = 1; quarter <= 3; quarter++)
1061       {
1062         time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1063         struct tm const *probe_tm = localtime (&probe);
1064         if (probe_tm && probe_tm->tm_zone
1065             && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1066           {
1067               {
1068                 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1069                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1070                 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1071                 pc.local_time_zone_table[2].name = NULL;
1072               }
1073             break;
1074           }
1075       }
1076   }
1077 #else
1078 #if HAVE_TZNAME
1079   {
1080 # ifndef tzname
1081     extern char *tzname[];
1082 # endif
1083     int i;
1084     for (i = 0; i < 2; i++)
1085       {
1086         pc.local_time_zone_table[i].name = tzname[i];
1087         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1088         pc.local_time_zone_table[i].value = i;
1089       }
1090     pc.local_time_zone_table[i].name = NULL;
1091   }
1092 #else
1093   pc.local_time_zone_table[0].name = NULL;
1094 #endif
1095 #endif
1096
1097   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1098       && ! strcmp (pc.local_time_zone_table[0].name,
1099                    pc.local_time_zone_table[1].name))
1100     {
1101       /* This locale uses the same abbrevation for standard and
1102          daylight times.  So if we see that abbreviation, we don't
1103          know whether it's daylight time.  */
1104       pc.local_time_zone_table[0].value = -1;
1105       pc.local_time_zone_table[1].name = NULL;
1106     }
1107
1108   if (yyparse (&pc) != 0)
1109     return false;
1110
1111   if (pc.timespec_seen)
1112     {
1113       *result = pc.seconds;
1114       return true;
1115     }
1116
1117   if (1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
1118       || 1 < (pc.local_zones_seen + pc.zones_seen)
1119       || (pc.local_zones_seen && 1 < pc.local_isdst))
1120     return false;
1121
1122   tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
1123   tm.tm_mon = pc.month - 1 + pc.rel_month;
1124   tm.tm_mday = pc.day + pc.rel_day;
1125   if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1126     {
1127       tm.tm_hour = to_hour (pc.hour, pc.meridian);
1128       if (tm.tm_hour < 0)
1129         return false;
1130       tm.tm_min = pc.minutes;
1131       tm.tm_sec = pc.seconds.tv_sec;
1132     }
1133   else
1134     {
1135       tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1136       pc.seconds.tv_nsec = 0;
1137     }
1138
1139   /* Let mktime deduce tm_isdst if we have an absolute time stamp,
1140      or if the relative time stamp mentions days, months, or years.  */
1141   if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
1142       | pc.rel_month | pc.rel_year)
1143     tm.tm_isdst = -1;
1144
1145   /* But if the input explicitly specifies local time with or without
1146      DST, give mktime that information.  */
1147   if (pc.local_zones_seen)
1148     tm.tm_isdst = pc.local_isdst;
1149
1150   tm0 = tm;
1151
1152   Start = mktime (&tm);
1153
1154   if (Start == (time_t) -1)
1155     {
1156
1157       /* Guard against falsely reporting errors near the time_t boundaries
1158          when parsing times in other time zones.  For example, if the min
1159          time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
1160          of UTC, then the min localtime value is 1970-01-01 08:00:00; if
1161          we apply mktime to 1970-01-01 00:00:00 we will get an error, so
1162          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
1163          zone by 24 hours to compensate.  This algorithm assumes that
1164          there is no DST transition within a day of the time_t boundaries.  */
1165       if (pc.zones_seen)
1166         {
1167           tm = tm0;
1168           if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
1169             {
1170               tm.tm_mday++;
1171               pc.time_zone += 24 * 60;
1172             }
1173           else
1174             {
1175               tm.tm_mday--;
1176               pc.time_zone -= 24 * 60;
1177             }
1178           Start = mktime (&tm);
1179         }
1180
1181       if (Start == (time_t) -1)
1182         return false;
1183     }
1184
1185   if (pc.days_seen && ! pc.dates_seen)
1186     {
1187       tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1188                      + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1189       tm.tm_isdst = -1;
1190       Start = mktime (&tm);
1191       if (Start == (time_t) -1)
1192         return false;
1193     }
1194
1195   if (pc.zones_seen)
1196     {
1197       long int delta = pc.time_zone * 60;
1198       time_t t1;
1199 #ifdef HAVE_TM_GMTOFF
1200       delta -= tm.tm_gmtoff;
1201 #else
1202       time_t t = Start;
1203       struct tm const *gmt = gmtime (&t);
1204       if (! gmt)
1205         return false;
1206       delta -= tm_diff (&tm, gmt);
1207 #endif
1208       t1 = Start - delta;
1209       if ((Start < t1) != (delta < 0))
1210         return false;   /* time_t overflow */
1211       Start = t1;
1212     }
1213
1214   /* Add relative hours, minutes, and seconds.  Ignore leap seconds;
1215      i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
1216      leap second.  Typically this is not what the user wants, but it's
1217      too hard to do it the other way, because the time zone indicator
1218      must be applied before relative times, and if mktime is applied
1219      again the time zone will be lost.  */
1220   {
1221     long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
1222     long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1223     time_t t0 = Start;
1224     long int d1 = 60 * 60 * pc.rel_hour;
1225     time_t t1 = t0 + d1;
1226     long int d2 = 60 * pc.rel_minutes;
1227     time_t t2 = t1 + d2;
1228     long int d3 = pc.rel_seconds;
1229     time_t t3 = t2 + d3;
1230     long int d4 = (sum_ns - normalized_ns) / BILLION;
1231     time_t t4 = t3 + d4;
1232
1233     if ((d1 / (60 * 60) ^ pc.rel_hour)
1234         | (d2 / 60 ^ pc.rel_minutes)
1235         | ((t1 < t0) ^ (d1 < 0))
1236         | ((t2 < t1) ^ (d2 < 0))
1237         | ((t3 < t2) ^ (d3 < 0))
1238         | ((t4 < t3) ^ (d4 < 0)))
1239       return false;
1240
1241     result->tv_sec = t4;
1242     result->tv_nsec = normalized_ns;
1243     return true;
1244   }
1245 }
1246
1247 #if TEST
1248
1249 #include <stdio.h>
1250
1251 int
1252 main (int ac, char **av)
1253 {
1254   char buff[BUFSIZ];
1255
1256   printf ("Enter date, or blank line to exit.\n\t> ");
1257   fflush (stdout);
1258
1259   buff[BUFSIZ - 1] = '\0';
1260   while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1261     {
1262       struct timespec d;
1263       struct tm const *tm;
1264       if (! get_date (&d, buff, NULL))
1265         printf ("Bad format - couldn't convert.\n");
1266       else if (! (tm = localtime (&d.tv_sec)))
1267         {
1268           long int sec = d.tv_sec;
1269           printf ("localtime (%ld) failed\n", sec);
1270         }
1271       else
1272         {
1273           int ns = d.tv_nsec;
1274           printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1275                   tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1276                   tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1277         }
1278       printf ("\t> ");
1279       fflush (stdout);
1280     }
1281   return 0;
1282 }
1283 #endif /* TEST */