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