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