source: vendor/3.6.0/source3/modules/getdate.y

Last change on this file was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

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