52ed8530ba64fdf59e4de885e11f03ed2262ec27
[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 #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               c = *++pc->input;
848               if (! ISDIGIT (c))
849                 /* skip the '-' sign */
850                 continue;
851             }
852           else
853             sign = 0;
854           p = pc->input;
855           for (value = 0; ; value *= 10)
856             {
857               unsigned long int value1 = value + (c - '0');
858               if (value1 < value)
859                 return '?';
860               value = value1;
861               c = *++p;
862               if (! ISDIGIT (c))
863                 break;
864               if (ULONG_MAX / 10 < value)
865                 return '?';
866             }
867           if ((c == '.' || c == ',') && ISDIGIT (p[1]))
868             {
869               time_t s;
870               int ns;
871               int digits;
872               unsigned long int value1;
873
874               /* Check for overflow when converting value to time_t.  */
875               if (sign < 0)
876                 {
877                   s = - value;
878                   if (0 < s)
879                     return '?';
880                   value1 = -s;
881                 }
882               else
883                 {
884                   s = value;
885                   if (s < 0)
886                     return '?';
887                   value1 = s;
888                 }
889               if (value != value1)
890                 return '?';
891
892               /* Accumulate fraction, to ns precision.  */
893               p++;
894               ns = *p++ - '0';
895               for (digits = 2; digits <= LOG10_BILLION; digits++)
896                 {
897                   ns *= 10;
898                   if (ISDIGIT (*p))
899                     ns += *p++ - '0';
900                 }
901
902               /* Skip excess digits, truncating toward -Infinity.  */
903               if (sign < 0)
904                 for (; ISDIGIT (*p); p++)
905                   if (*p != '0')
906                     {
907                       ns++;
908                       break;
909                     }
910               while (ISDIGIT (*p))
911                 p++;
912
913               /* Adjust to the timespec convention, which is that
914                  tv_nsec is always a positive offset even if tv_sec is
915                  negative.  */
916               if (sign < 0 && ns)
917                 {
918                   s--;
919                   if (! (s < 0))
920                     return '?';
921                   ns = BILLION - ns;
922                 }
923
924               lvalp->timespec.tv_sec = s;
925               lvalp->timespec.tv_nsec = ns;
926               pc->input = p;
927               return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
928             }
929           else
930             {
931               if (sign < 0)
932                 {
933                   lvalp->textintval.value = - value;
934                   if (0 < lvalp->textintval.value)
935                     return '?';
936                 }
937               else
938                 {
939                   lvalp->textintval.value = value;
940                   if (lvalp->textintval.value < 0)
941                     return '?';
942                 }
943               lvalp->textintval.digits = p - pc->input;
944               pc->input = p;
945               return sign ? tSNUMBER : tUNUMBER;
946             }
947         }
948
949       if (ISALPHA (c))
950         {
951           char buff[20];
952           char *p = buff;
953           table const *tp;
954
955           do
956             {
957               if (p < buff + sizeof buff - 1)
958                 *p++ = c;
959               c = *++pc->input;
960             }
961           while (ISALPHA (c) || c == '.');
962
963           *p = '\0';
964           tp = lookup_word (pc, buff);
965           if (! tp)
966             return '?';
967           lvalp->intval = tp->value;
968           return tp->type;
969         }
970
971       if (c != '(')
972         return *pc->input++;
973       count = 0;
974       do
975         {
976           c = *pc->input++;
977           if (c == '\0')
978             return c;
979           if (c == '(')
980             count++;
981           else if (c == ')')
982             count--;
983         }
984       while (count != 0);
985     }
986 }
987
988 /* Do nothing if the parser reports an error.  */
989 static int
990 yyerror (char *s ATTRIBUTE_UNUSED)
991 {
992   return 0;
993 }
994
995 /* Parse a date/time string, storing the resulting time value into *RESULT.
996    The string itself is pointed to by P.  Return true if successful.
997    P can be an incomplete or relative time specification; if so, use
998    *NOW as the basis for the returned time.  */
999 bool
1000 get_date (struct timespec *result, char const *p, struct timespec const *now)
1001 {
1002   time_t Start;
1003   long int Start_ns;
1004   struct tm const *tmp;
1005   struct tm tm;
1006   struct tm tm0;
1007   parser_control pc;
1008   struct timespec gettime_buffer;
1009
1010   if (! now)
1011     {
1012       if (gettime (&gettime_buffer) != 0)
1013         return false;
1014       now = &gettime_buffer;
1015     }
1016
1017   Start = now->tv_sec;
1018   Start_ns = now->tv_nsec;
1019
1020   tmp = localtime (&now->tv_sec);
1021   if (! tmp)
1022     return false;
1023
1024   pc.input = p;
1025   pc.year.value = tmp->tm_year;
1026   pc.year.value += TM_YEAR_BASE;
1027   pc.year.digits = 4;
1028   pc.month = tmp->tm_mon + 1;
1029   pc.day = tmp->tm_mday;
1030   pc.hour = tmp->tm_hour;
1031   pc.minutes = tmp->tm_min;
1032   pc.seconds.tv_sec = tmp->tm_sec;
1033   pc.seconds.tv_nsec = Start_ns;
1034   tm.tm_isdst = tmp->tm_isdst;
1035
1036   pc.meridian = MER24;
1037   pc.rel_ns = 0;
1038   pc.rel_seconds = 0;
1039   pc.rel_minutes = 0;
1040   pc.rel_hour = 0;
1041   pc.rel_day = 0;
1042   pc.rel_month = 0;
1043   pc.rel_year = 0;
1044   pc.timespec_seen = false;
1045   pc.dates_seen = 0;
1046   pc.days_seen = 0;
1047   pc.rels_seen = 0;
1048   pc.times_seen = 0;
1049   pc.local_zones_seen = 0;
1050   pc.zones_seen = 0;
1051
1052 #if HAVE_STRUCT_TM_TM_ZONE
1053   pc.local_time_zone_table[0].name = tmp->tm_zone;
1054   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1055   pc.local_time_zone_table[0].value = tmp->tm_isdst;
1056   pc.local_time_zone_table[1].name = 0;
1057
1058   /* Probe the names used in the next three calendar quarters, looking
1059      for a tm_isdst different from the one we already have.  */
1060   {
1061     int quarter;
1062     for (quarter = 1; quarter <= 3; quarter++)
1063       {
1064         time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1065         struct tm const *probe_tm = localtime (&probe);
1066         if (probe_tm && probe_tm->tm_zone
1067             && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1068           {
1069               {
1070                 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1071                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1072                 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1073                 pc.local_time_zone_table[2].name = 0;
1074               }
1075             break;
1076           }
1077       }
1078   }
1079 #else
1080 #if HAVE_TZNAME
1081   {
1082 # ifndef tzname
1083     extern char *tzname[];
1084 # endif
1085     int i;
1086     for (i = 0; i < 2; i++)
1087       {
1088         pc.local_time_zone_table[i].name = tzname[i];
1089         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1090         pc.local_time_zone_table[i].value = i;
1091       }
1092     pc.local_time_zone_table[i].name = 0;
1093   }
1094 #else
1095   pc.local_time_zone_table[0].name = 0;
1096 #endif
1097 #endif
1098
1099   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1100       && ! strcmp (pc.local_time_zone_table[0].name,
1101                    pc.local_time_zone_table[1].name))
1102     {
1103       /* This locale uses the same abbrevation for standard and
1104          daylight times.  So if we see that abbreviation, we don't
1105          know whether it's daylight time.  */
1106       pc.local_time_zone_table[0].value = -1;
1107       pc.local_time_zone_table[1].name = 0;
1108     }
1109
1110   if (yyparse (&pc) != 0)
1111     return false;
1112
1113   if (pc.timespec_seen)
1114     {
1115       *result = pc.seconds;
1116       return true;
1117     }
1118
1119   if (1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
1120       || 1 < (pc.local_zones_seen + pc.zones_seen)
1121       || (pc.local_zones_seen && 1 < pc.local_isdst))
1122     return false;
1123
1124   tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
1125   tm.tm_mon = pc.month - 1 + pc.rel_month;
1126   tm.tm_mday = pc.day + pc.rel_day;
1127   if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1128     {
1129       tm.tm_hour = to_hour (pc.hour, pc.meridian);
1130       if (tm.tm_hour < 0)
1131         return false;
1132       tm.tm_min = pc.minutes;
1133       tm.tm_sec = pc.seconds.tv_sec;
1134     }
1135   else
1136     {
1137       tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1138       pc.seconds.tv_nsec = 0;
1139     }
1140
1141   /* Let mktime deduce tm_isdst if we have an absolute time stamp,
1142      or if the relative time stamp mentions days, months, or years.  */
1143   if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
1144       | pc.rel_month | pc.rel_year)
1145     tm.tm_isdst = -1;
1146
1147   /* But if the input explicitly specifies local time with or without
1148      DST, give mktime that information.  */
1149   if (pc.local_zones_seen)
1150     tm.tm_isdst = pc.local_isdst;
1151
1152   tm0 = tm;
1153
1154   Start = mktime (&tm);
1155
1156   if (Start == (time_t) -1)
1157     {
1158
1159       /* Guard against falsely reporting errors near the time_t boundaries
1160          when parsing times in other time zones.  For example, if the min
1161          time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
1162          of UTC, then the min localtime value is 1970-01-01 08:00:00; if
1163          we apply mktime to 1970-01-01 00:00:00 we will get an error, so
1164          we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
1165          zone by 24 hours to compensate.  This algorithm assumes that
1166          there is no DST transition within a day of the time_t boundaries.  */
1167       if (pc.zones_seen)
1168         {
1169           tm = tm0;
1170           if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
1171             {
1172               tm.tm_mday++;
1173               pc.time_zone += 24 * 60;
1174             }
1175           else
1176             {
1177               tm.tm_mday--;
1178               pc.time_zone -= 24 * 60;
1179             }
1180           Start = mktime (&tm);
1181         }
1182
1183       if (Start == (time_t) -1)
1184         return false;
1185     }
1186
1187   if (pc.days_seen && ! pc.dates_seen)
1188     {
1189       tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1190                      + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1191       tm.tm_isdst = -1;
1192       Start = mktime (&tm);
1193       if (Start == (time_t) -1)
1194         return false;
1195     }
1196
1197   if (pc.zones_seen)
1198     {
1199       long int delta = pc.time_zone * 60;
1200       time_t t1;
1201 #ifdef HAVE_TM_GMTOFF
1202       delta -= tm.tm_gmtoff;
1203 #else
1204       time_t t = Start;
1205       struct tm const *gmt = gmtime (&t);
1206       if (! gmt)
1207         return false;
1208       delta -= tm_diff (&tm, gmt);
1209 #endif
1210       t1 = Start - delta;
1211       if ((Start < t1) != (delta < 0))
1212         return false;   /* time_t overflow */
1213       Start = t1;
1214     }
1215
1216   /* Add relative hours, minutes, and seconds.  Ignore leap seconds;
1217      i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
1218      leap second.  Typically this is not what the user wants, but it's
1219      too hard to do it the other way, because the time zone indicator
1220      must be applied before relative times, and if mktime is applied
1221      again the time zone will be lost.  */
1222   {
1223     long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
1224     long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1225     time_t t0 = Start;
1226     long int d1 = 60 * 60 * pc.rel_hour;
1227     time_t t1 = t0 + d1;
1228     long int d2 = 60 * pc.rel_minutes;
1229     time_t t2 = t1 + d2;
1230     long int d3 = pc.rel_seconds;
1231     time_t t3 = t2 + d3;
1232     long int d4 = (sum_ns - normalized_ns) / BILLION;
1233     time_t t4 = t3 + d4;
1234
1235     if ((d1 / (60 * 60) ^ pc.rel_hour)
1236         | (d2 / 60 ^ pc.rel_minutes)
1237         | ((t1 < t0) ^ (d1 < 0))
1238         | ((t2 < t1) ^ (d2 < 0))
1239         | ((t3 < t2) ^ (d3 < 0))
1240         | ((t4 < t3) ^ (d4 < 0)))
1241       return false;
1242
1243     result->tv_sec = t4;
1244     result->tv_nsec = normalized_ns;
1245     return true;
1246   }
1247 }
1248
1249 #if TEST
1250
1251 #include <stdio.h>
1252
1253 int
1254 main (int ac, char **av)
1255 {
1256   char buff[BUFSIZ];
1257
1258   printf ("Enter date, or blank line to exit.\n\t> ");
1259   fflush (stdout);
1260
1261   buff[BUFSIZ - 1] = 0;
1262   while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1263     {
1264       struct timespec d;
1265       struct tm const *tm;
1266       if (! get_date (&d, buff, NULL))
1267         printf ("Bad format - couldn't convert.\n");
1268       else if (! (tm = localtime (&d.tv_sec)))
1269         {
1270           long int sec = d.tv_sec;
1271           printf ("localtime (%ld) failed\n", sec);
1272         }
1273       else
1274         {
1275           int ns = d.tv_nsec;
1276           printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1277                   tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1278                   tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1279         }
1280       printf ("\t> ");
1281       fflush (stdout);
1282     }
1283   return 0;
1284 }
1285 #endif /* defined TEST */