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