.
[coreutils.git] / lib / getdate.y
blobdf9614f1f06321c90e30de54d33a217b6e9fb1a2
1 %{
2 /*
3 ** Originally written by Steven M. Bellovin <smb@research.att.com> while
4 ** at the University of North Carolina at Chapel Hill. Later tweaked by
5 ** a couple of people on Usenet. Completely overhauled by Rich $alz
6 ** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
7 **
8 ** This code is in the public domain and has no copyright.
9 */
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 # ifdef FORCE_ALLOCA_H
14 # include <alloca.h>
15 # endif
16 #endif
18 /* Since the code of getdate.y is not included in the Emacs executable
19 itself, there is no need to #define static in this file. Even if
20 the code were included in the Emacs executable, it probably
21 wouldn't do any harm to #undef it here; this will only cause
22 problems if we try to write to a static variable, which I don't
23 think this code needs to do. */
24 #ifdef emacs
25 # undef static
26 #endif
28 #include <stdio.h>
29 #include <ctype.h>
31 #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
32 # define IN_CTYPE_DOMAIN(c) 1
33 #else
34 # define IN_CTYPE_DOMAIN(c) isascii(c)
35 #endif
37 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
38 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
39 #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
40 #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
42 /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
43 - Its arg may be any int or unsigned int; it need not be an unsigned char.
44 - It's guaranteed to evaluate its argument exactly once.
45 - It's typically faster.
46 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
47 only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless
48 it's important to use the locale's definition of `digit' even when the
49 host does not conform to Posix. */
50 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
52 #if defined (STDC_HEADERS) || defined (USG)
53 # include <string.h>
54 #endif
56 /* Some old versions of bison generate parsers that use bcopy.
57 That loses on systems that don't provide the function, so we have
58 to redefine it here. */
59 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
60 # define bcopy(from, to, len) memcpy ((to), (from), (len))
61 #endif
63 /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
64 as well as gratuitiously global symbol names, so we can have multiple
65 yacc generated parsers in the same program. Note that these are only
66 the variables produced by yacc. If other parser generators (bison,
67 byacc, etc) produce additional global names that conflict at link time,
68 then those parser generators need to be fixed instead of adding those
69 names to this list. */
71 #define yymaxdepth gd_maxdepth
72 #define yyparse gd_parse
73 #define yylex gd_lex
74 #define yyerror gd_error
75 #define yylval gd_lval
76 #define yychar gd_char
77 #define yydebug gd_debug
78 #define yypact gd_pact
79 #define yyr1 gd_r1
80 #define yyr2 gd_r2
81 #define yydef gd_def
82 #define yychk gd_chk
83 #define yypgo gd_pgo
84 #define yyact gd_act
85 #define yyexca gd_exca
86 #define yyerrflag gd_errflag
87 #define yynerrs gd_nerrs
88 #define yyps gd_ps
89 #define yypv gd_pv
90 #define yys gd_s
91 #define yy_yys gd_yys
92 #define yystate gd_state
93 #define yytmp gd_tmp
94 #define yyv gd_v
95 #define yy_yyv gd_yyv
96 #define yyval gd_val
97 #define yylloc gd_lloc
98 #define yyreds gd_reds /* With YYDEBUG defined */
99 #define yytoks gd_toks /* With YYDEBUG defined */
100 #define yylhs gd_yylhs
101 #define yylen gd_yylen
102 #define yydefred gd_yydefred
103 #define yydgoto gd_yydgoto
104 #define yysindex gd_yysindex
105 #define yyrindex gd_yyrindex
106 #define yygindex gd_yygindex
107 #define yytable gd_yytable
108 #define yycheck gd_yycheck
110 static int yylex ();
111 static int yyerror ();
113 #define EPOCH 1970
114 #define HOUR(x) ((x) * 60)
116 #define MAX_BUFF_LEN 128 /* size of buffer to read the date into */
119 ** An entry in the lexical lookup table.
121 typedef struct _TABLE {
122 const char *name;
123 int type;
124 int value;
125 } TABLE;
129 ** Meridian: am, pm, or 24-hour style.
131 typedef enum _MERIDIAN {
132 MERam, MERpm, MER24
133 } MERIDIAN;
137 ** Global variables. We could get rid of most of these by using a good
138 ** union as the yacc stack. (This routine was originally written before
139 ** yacc had the %union construct.) Maybe someday; right now we only use
140 ** the %union very rarely.
142 static const char *yyInput;
143 static int yyDayOrdinal;
144 static int yyDayNumber;
145 static int yyHaveDate;
146 static int yyHaveDay;
147 static int yyHaveRel;
148 static int yyHaveTime;
149 static int yyHaveZone;
150 static int yyTimezone;
151 static int yyDay;
152 static int yyHour;
153 static int yyMinutes;
154 static int yyMonth;
155 static int yySeconds;
156 static int yyYear;
157 static MERIDIAN yyMeridian;
158 static int yyRelDay;
159 static int yyRelHour;
160 static int yyRelMinutes;
161 static int yyRelMonth;
162 static int yyRelSeconds;
163 static int yyRelYear;
167 /* This grammar has 13 shift/reduce conflicts. */
168 %expect 13
170 %union {
171 int Number;
172 enum _MERIDIAN Meridian;
175 %token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID
176 %token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
177 %token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
179 %type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
180 %type <Number> tMONTH tMONTH_UNIT
181 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
182 %type <Meridian> tMERIDIAN o_merid
186 spec : /* NULL */
187 | spec item
190 item : time {
191 yyHaveTime++;
193 | zone {
194 yyHaveZone++;
196 | date {
197 yyHaveDate++;
199 | day {
200 yyHaveDay++;
202 | rel {
203 yyHaveRel++;
205 | number
208 time : tUNUMBER tMERIDIAN {
209 yyHour = $1;
210 yyMinutes = 0;
211 yySeconds = 0;
212 yyMeridian = $2;
214 | tUNUMBER ':' tUNUMBER o_merid {
215 yyHour = $1;
216 yyMinutes = $3;
217 yySeconds = 0;
218 yyMeridian = $4;
220 | tUNUMBER ':' tUNUMBER tSNUMBER {
221 yyHour = $1;
222 yyMinutes = $3;
223 yyMeridian = MER24;
224 yyHaveZone++;
225 yyTimezone = ($4 < 0
226 ? -$4 % 100 + (-$4 / 100) * 60
227 : - ($4 % 100 + ($4 / 100) * 60));
229 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
230 yyHour = $1;
231 yyMinutes = $3;
232 yySeconds = $5;
233 yyMeridian = $6;
235 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
236 yyHour = $1;
237 yyMinutes = $3;
238 yySeconds = $5;
239 yyMeridian = MER24;
240 yyHaveZone++;
241 yyTimezone = ($6 < 0
242 ? -$6 % 100 + (-$6 / 100) * 60
243 : - ($6 % 100 + ($6 / 100) * 60));
247 zone : tZONE {
248 yyTimezone = $1;
250 | tDAYZONE {
251 yyTimezone = $1 - 60;
254 tZONE tDST {
255 yyTimezone = $1 - 60;
259 day : tDAY {
260 yyDayOrdinal = 1;
261 yyDayNumber = $1;
263 | tDAY ',' {
264 yyDayOrdinal = 1;
265 yyDayNumber = $1;
267 | tUNUMBER tDAY {
268 yyDayOrdinal = $1;
269 yyDayNumber = $2;
273 date : tUNUMBER '/' tUNUMBER {
274 yyMonth = $1;
275 yyDay = $3;
277 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
278 /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
279 The goal in recognizing YYYY/MM/DD is solely to support legacy
280 machine-generated dates like those in an RCS log listing. If
281 you want portability, use the ISO 8601 format. */
282 if ($1 >= 1000)
284 yyYear = $1;
285 yyMonth = $3;
286 yyDay = $5;
288 else
290 yyMonth = $1;
291 yyDay = $3;
292 yyYear = $5;
295 | tUNUMBER tSNUMBER tSNUMBER {
296 /* ISO 8601 format. yyyy-mm-dd. */
297 yyYear = $1;
298 yyMonth = -$2;
299 yyDay = -$3;
301 | tUNUMBER tMONTH tSNUMBER {
302 /* e.g. 17-JUN-1992. */
303 yyDay = $1;
304 yyMonth = $2;
305 yyYear = -$3;
307 | tMONTH tUNUMBER {
308 yyMonth = $1;
309 yyDay = $2;
311 | tMONTH tUNUMBER ',' tUNUMBER {
312 yyMonth = $1;
313 yyDay = $2;
314 yyYear = $4;
316 | tUNUMBER tMONTH {
317 yyMonth = $2;
318 yyDay = $1;
320 | tUNUMBER tMONTH tUNUMBER {
321 yyMonth = $2;
322 yyDay = $1;
323 yyYear = $3;
327 rel : relunit tAGO {
328 yyRelSeconds = -yyRelSeconds;
329 yyRelMinutes = -yyRelMinutes;
330 yyRelHour = -yyRelHour;
331 yyRelDay = -yyRelDay;
332 yyRelMonth = -yyRelMonth;
333 yyRelYear = -yyRelYear;
335 | relunit
338 relunit : tUNUMBER tYEAR_UNIT {
339 yyRelYear += $1 * $2;
341 | tSNUMBER tYEAR_UNIT {
342 yyRelYear += $1 * $2;
344 | tYEAR_UNIT {
345 yyRelYear += $1;
347 | tUNUMBER tMONTH_UNIT {
348 yyRelMonth += $1 * $2;
350 | tSNUMBER tMONTH_UNIT {
351 yyRelMonth += $1 * $2;
353 | tMONTH_UNIT {
354 yyRelMonth += $1;
356 | tUNUMBER tDAY_UNIT {
357 yyRelDay += $1 * $2;
359 | tSNUMBER tDAY_UNIT {
360 yyRelDay += $1 * $2;
362 | tDAY_UNIT {
363 yyRelDay += $1;
365 | tUNUMBER tHOUR_UNIT {
366 yyRelHour += $1 * $2;
368 | tSNUMBER tHOUR_UNIT {
369 yyRelHour += $1 * $2;
371 | tHOUR_UNIT {
372 yyRelHour += $1;
374 | tUNUMBER tMINUTE_UNIT {
375 yyRelMinutes += $1 * $2;
377 | tSNUMBER tMINUTE_UNIT {
378 yyRelMinutes += $1 * $2;
380 | tMINUTE_UNIT {
381 yyRelMinutes += $1;
383 | tUNUMBER tSEC_UNIT {
384 yyRelSeconds += $1 * $2;
386 | tSNUMBER tSEC_UNIT {
387 yyRelSeconds += $1 * $2;
389 | tSEC_UNIT {
390 yyRelSeconds += $1;
394 number : tUNUMBER
396 if (yyHaveTime && yyHaveDate && !yyHaveRel)
397 yyYear = $1;
398 else
400 if ($1>10000)
402 yyHaveDate++;
403 yyDay= ($1)%100;
404 yyMonth= ($1/100)%100;
405 yyYear = $1/10000;
407 else
409 yyHaveTime++;
410 if ($1 < 100)
412 yyHour = $1;
413 yyMinutes = 0;
415 else
417 yyHour = $1 / 100;
418 yyMinutes = $1 % 100;
420 yySeconds = 0;
421 yyMeridian = MER24;
427 o_merid : /* NULL */
429 $$ = MER24;
431 | tMERIDIAN
433 $$ = $1;
439 /* Include this file down here because bison inserts code above which
440 may define-away `const'. We want the prototype for get_date to have
441 the same signature as the function definition does. */
442 #include "getdate.h"
444 extern struct tm *gmtime ();
445 extern struct tm *localtime ();
446 extern time_t mktime ();
448 /* Month and day table. */
449 static TABLE const MonthDayTable[] = {
450 { "january", tMONTH, 1 },
451 { "february", tMONTH, 2 },
452 { "march", tMONTH, 3 },
453 { "april", tMONTH, 4 },
454 { "may", tMONTH, 5 },
455 { "june", tMONTH, 6 },
456 { "july", tMONTH, 7 },
457 { "august", tMONTH, 8 },
458 { "september", tMONTH, 9 },
459 { "sept", tMONTH, 9 },
460 { "october", tMONTH, 10 },
461 { "november", tMONTH, 11 },
462 { "december", tMONTH, 12 },
463 { "sunday", tDAY, 0 },
464 { "monday", tDAY, 1 },
465 { "tuesday", tDAY, 2 },
466 { "tues", tDAY, 2 },
467 { "wednesday", tDAY, 3 },
468 { "wednes", tDAY, 3 },
469 { "thursday", tDAY, 4 },
470 { "thur", tDAY, 4 },
471 { "thurs", tDAY, 4 },
472 { "friday", tDAY, 5 },
473 { "saturday", tDAY, 6 },
474 { NULL }
477 /* Time units table. */
478 static TABLE const UnitsTable[] = {
479 { "year", tYEAR_UNIT, 1 },
480 { "month", tMONTH_UNIT, 1 },
481 { "fortnight", tDAY_UNIT, 14 },
482 { "week", tDAY_UNIT, 7 },
483 { "day", tDAY_UNIT, 1 },
484 { "hour", tHOUR_UNIT, 1 },
485 { "minute", tMINUTE_UNIT, 1 },
486 { "min", tMINUTE_UNIT, 1 },
487 { "second", tSEC_UNIT, 1 },
488 { "sec", tSEC_UNIT, 1 },
489 { NULL }
492 /* Assorted relative-time words. */
493 static TABLE const OtherTable[] = {
494 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
495 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
496 { "today", tMINUTE_UNIT, 0 },
497 { "now", tMINUTE_UNIT, 0 },
498 { "last", tUNUMBER, -1 },
499 { "this", tMINUTE_UNIT, 0 },
500 { "next", tUNUMBER, 1 },
501 { "first", tUNUMBER, 1 },
502 /* { "second", tUNUMBER, 2 }, */
503 { "third", tUNUMBER, 3 },
504 { "fourth", tUNUMBER, 4 },
505 { "fifth", tUNUMBER, 5 },
506 { "sixth", tUNUMBER, 6 },
507 { "seventh", tUNUMBER, 7 },
508 { "eighth", tUNUMBER, 8 },
509 { "ninth", tUNUMBER, 9 },
510 { "tenth", tUNUMBER, 10 },
511 { "eleventh", tUNUMBER, 11 },
512 { "twelfth", tUNUMBER, 12 },
513 { "ago", tAGO, 1 },
514 { NULL }
517 /* The timezone table. */
518 static TABLE const TimezoneTable[] = {
519 { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
520 { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
521 { "utc", tZONE, HOUR ( 0) },
522 { "wet", tZONE, HOUR ( 0) }, /* Western European */
523 { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
524 { "wat", tZONE, HOUR ( 1) }, /* West Africa */
525 { "at", tZONE, HOUR ( 2) }, /* Azores */
526 #if 0
527 /* For completeness. BST is also British Summer, and GST is
528 * also Guam Standard. */
529 { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */
530 { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */
531 #endif
532 #if 0
533 { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */
534 { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */
535 { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */
536 #endif
537 { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
538 { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
539 { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
540 { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
541 { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
542 { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
543 { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
544 { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
545 { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
546 { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
547 { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */
548 { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */
549 { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
550 { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */
551 { "cat", tZONE, HOUR (10) }, /* Central Alaska */
552 { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */
553 { "nt", tZONE, HOUR (11) }, /* Nome */
554 { "idlw", tZONE, HOUR (12) }, /* International Date Line West */
555 { "cet", tZONE, -HOUR (1) }, /* Central European */
556 { "met", tZONE, -HOUR (1) }, /* Middle European */
557 { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */
558 { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
559 { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
560 { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */
561 { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */
562 { "fwt", tZONE, -HOUR (1) }, /* French Winter */
563 { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */
564 { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
565 { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */
566 #if 0
567 { "it", tZONE, -HOUR (3.5) },/* Iran */
568 #endif
569 { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */
570 { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */
571 #if 0
572 { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */
573 #endif
574 { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */
575 #if 0
576 /* For completeness. NST is also Newfoundland Standard, and SST is
577 * also Swedish Summer. */
578 { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */
579 { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */
580 #endif /* 0 */
581 { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */
582 { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */
583 #if 0
584 { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */
585 #endif
586 { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */
587 { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
588 #if 0
589 { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */
590 { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */
591 #endif
592 { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */
593 { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */
594 { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */
595 { "nzt", tZONE, -HOUR (12) }, /* New Zealand */
596 { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
597 { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
598 { "idle", tZONE, -HOUR (12) }, /* International Date Line East */
599 { NULL }
602 /* Military timezone table. */
603 static TABLE const MilitaryTable[] = {
604 { "a", tZONE, HOUR ( 1) },
605 { "b", tZONE, HOUR ( 2) },
606 { "c", tZONE, HOUR ( 3) },
607 { "d", tZONE, HOUR ( 4) },
608 { "e", tZONE, HOUR ( 5) },
609 { "f", tZONE, HOUR ( 6) },
610 { "g", tZONE, HOUR ( 7) },
611 { "h", tZONE, HOUR ( 8) },
612 { "i", tZONE, HOUR ( 9) },
613 { "k", tZONE, HOUR ( 10) },
614 { "l", tZONE, HOUR ( 11) },
615 { "m", tZONE, HOUR ( 12) },
616 { "n", tZONE, HOUR (- 1) },
617 { "o", tZONE, HOUR (- 2) },
618 { "p", tZONE, HOUR (- 3) },
619 { "q", tZONE, HOUR (- 4) },
620 { "r", tZONE, HOUR (- 5) },
621 { "s", tZONE, HOUR (- 6) },
622 { "t", tZONE, HOUR (- 7) },
623 { "u", tZONE, HOUR (- 8) },
624 { "v", tZONE, HOUR (- 9) },
625 { "w", tZONE, HOUR (-10) },
626 { "x", tZONE, HOUR (-11) },
627 { "y", tZONE, HOUR (-12) },
628 { "z", tZONE, HOUR ( 0) },
629 { NULL }
635 /* ARGSUSED */
636 static int
637 yyerror (s)
638 char *s;
640 return 0;
643 static int
644 ToHour (Hours, Meridian)
645 int Hours;
646 MERIDIAN Meridian;
648 switch (Meridian)
650 case MER24:
651 if (Hours < 0 || Hours > 23)
652 return -1;
653 return Hours;
654 case MERam:
655 if (Hours < 1 || Hours > 12)
656 return -1;
657 if (Hours == 12)
658 Hours = 0;
659 return Hours;
660 case MERpm:
661 if (Hours < 1 || Hours > 12)
662 return -1;
663 if (Hours == 12)
664 Hours = 0;
665 return Hours + 12;
666 default:
667 abort ();
669 /* NOTREACHED */
672 static int
673 ToYear (Year)
674 int Year;
676 if (Year < 0)
677 Year = -Year;
679 /* XPG4 suggests that years 00-68 map to 2000-2068, and
680 years 69-99 map to 1969-1999. */
681 if (Year < 69)
682 Year += 2000;
683 else if (Year < 100)
684 Year += 1900;
686 return Year;
689 static int
690 LookupWord (buff)
691 char *buff;
693 register char *p;
694 register char *q;
695 register const TABLE *tp;
696 int i;
697 int abbrev;
699 /* Make it lowercase. */
700 for (p = buff; *p; p++)
701 if (ISUPPER (*p))
702 *p = tolower (*p);
704 if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
706 yylval.Meridian = MERam;
707 return tMERIDIAN;
709 if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
711 yylval.Meridian = MERpm;
712 return tMERIDIAN;
715 /* See if we have an abbreviation for a month. */
716 if (strlen (buff) == 3)
717 abbrev = 1;
718 else if (strlen (buff) == 4 && buff[3] == '.')
720 abbrev = 1;
721 buff[3] = '\0';
723 else
724 abbrev = 0;
726 for (tp = MonthDayTable; tp->name; tp++)
728 if (abbrev)
730 if (strncmp (buff, tp->name, 3) == 0)
732 yylval.Number = tp->value;
733 return tp->type;
736 else if (strcmp (buff, tp->name) == 0)
738 yylval.Number = tp->value;
739 return tp->type;
743 for (tp = TimezoneTable; tp->name; tp++)
744 if (strcmp (buff, tp->name) == 0)
746 yylval.Number = tp->value;
747 return tp->type;
750 if (strcmp (buff, "dst") == 0)
751 return tDST;
753 for (tp = UnitsTable; tp->name; tp++)
754 if (strcmp (buff, tp->name) == 0)
756 yylval.Number = tp->value;
757 return tp->type;
760 /* Strip off any plural and try the units table again. */
761 i = strlen (buff) - 1;
762 if (buff[i] == 's')
764 buff[i] = '\0';
765 for (tp = UnitsTable; tp->name; tp++)
766 if (strcmp (buff, tp->name) == 0)
768 yylval.Number = tp->value;
769 return tp->type;
771 buff[i] = 's'; /* Put back for "this" in OtherTable. */
774 for (tp = OtherTable; tp->name; tp++)
775 if (strcmp (buff, tp->name) == 0)
777 yylval.Number = tp->value;
778 return tp->type;
781 /* Military timezones. */
782 if (buff[1] == '\0' && ISALPHA (*buff))
784 for (tp = MilitaryTable; tp->name; tp++)
785 if (strcmp (buff, tp->name) == 0)
787 yylval.Number = tp->value;
788 return tp->type;
792 /* Drop out any periods and try the timezone table again. */
793 for (i = 0, p = q = buff; *q; q++)
794 if (*q != '.')
795 *p++ = *q;
796 else
797 i++;
798 *p = '\0';
799 if (i)
800 for (tp = TimezoneTable; tp->name; tp++)
801 if (strcmp (buff, tp->name) == 0)
803 yylval.Number = tp->value;
804 return tp->type;
807 return tID;
810 static int
811 yylex ()
813 register char c;
814 register char *p;
815 char buff[20];
816 int Count;
817 int sign;
819 for (;;)
821 while (ISSPACE (*yyInput))
822 yyInput++;
824 if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
826 if (c == '-' || c == '+')
828 sign = c == '-' ? -1 : 1;
829 if (!ISDIGIT (*++yyInput))
830 /* skip the '-' sign */
831 continue;
833 else
834 sign = 0;
835 for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
836 yylval.Number = 10 * yylval.Number + c - '0';
837 yyInput--;
838 if (sign < 0)
839 yylval.Number = -yylval.Number;
840 return sign ? tSNUMBER : tUNUMBER;
842 if (ISALPHA (c))
844 for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
845 if (p < &buff[sizeof buff - 1])
846 *p++ = c;
847 *p = '\0';
848 yyInput--;
849 return LookupWord (buff);
851 if (c != '(')
852 return *yyInput++;
853 Count = 0;
856 c = *yyInput++;
857 if (c == '\0')
858 return c;
859 if (c == '(')
860 Count++;
861 else if (c == ')')
862 Count--;
864 while (Count > 0);
868 #define TM_YEAR_ORIGIN 1900
870 /* Yield A - B, measured in seconds. */
871 static long
872 difftm (a, b)
873 struct tm *a, *b;
875 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
876 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
877 long days = (
878 /* difference in day of year */
879 a->tm_yday - b->tm_yday
880 /* + intervening leap days */
881 + ((ay >> 2) - (by >> 2))
882 - (ay / 100 - by / 100)
883 + ((ay / 100 >> 2) - (by / 100 >> 2))
884 /* + difference in years * 365 */
885 + (long) (ay - by) * 365
887 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
888 + (a->tm_min - b->tm_min))
889 + (a->tm_sec - b->tm_sec));
892 time_t
893 get_date (const char *p, const time_t *now)
895 struct tm tm, tm0, *tmp;
896 time_t Start;
898 yyInput = p;
899 Start = now ? *now : time ((time_t *) NULL);
900 tmp = localtime (&Start);
901 yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
902 yyMonth = tmp->tm_mon + 1;
903 yyDay = tmp->tm_mday;
904 yyHour = tmp->tm_hour;
905 yyMinutes = tmp->tm_min;
906 yySeconds = tmp->tm_sec;
907 yyMeridian = MER24;
908 yyRelSeconds = 0;
909 yyRelMinutes = 0;
910 yyRelHour = 0;
911 yyRelDay = 0;
912 yyRelMonth = 0;
913 yyRelYear = 0;
914 yyHaveDate = 0;
915 yyHaveDay = 0;
916 yyHaveRel = 0;
917 yyHaveTime = 0;
918 yyHaveZone = 0;
920 if (yyparse ()
921 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
922 return -1;
924 tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
925 tm.tm_mon = yyMonth - 1 + yyRelMonth;
926 tm.tm_mday = yyDay + yyRelDay;
927 if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
929 tm.tm_hour = ToHour (yyHour, yyMeridian);
930 if (tm.tm_hour < 0)
931 return -1;
932 tm.tm_min = yyMinutes;
933 tm.tm_sec = yySeconds;
935 else
937 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
939 tm.tm_hour += yyRelHour;
940 tm.tm_min += yyRelMinutes;
941 tm.tm_sec += yyRelSeconds;
942 tm.tm_isdst = -1;
943 tm0 = tm;
945 Start = mktime (&tm);
947 if (Start == (time_t) -1)
950 /* Guard against falsely reporting errors near the time_t boundaries
951 when parsing times in other time zones. For example, if the min
952 time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
953 of UTC, then the min localtime value is 1970-01-01 08:00:00; if
954 we apply mktime to 1970-01-01 00:00:00 we will get an error, so
955 we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
956 zone by 24 hours to compensate. This algorithm assumes that
957 there is no DST transition within a day of the time_t boundaries. */
958 if (yyHaveZone)
960 tm = tm0;
961 if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
963 tm.tm_mday++;
964 yyTimezone -= 24 * 60;
966 else
968 tm.tm_mday--;
969 yyTimezone += 24 * 60;
971 Start = mktime (&tm);
974 if (Start == (time_t) -1)
975 return Start;
978 if (yyHaveDay && !yyHaveDate)
980 tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
981 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
982 Start = mktime (&tm);
983 if (Start == (time_t) -1)
984 return Start;
987 if (yyHaveZone)
989 long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start));
990 if ((Start + delta < Start) != (delta < 0))
991 return -1; /* time_t overflow */
992 Start += delta;
995 return Start;
998 #if defined (TEST)
1000 /* ARGSUSED */
1002 main (ac, av)
1003 int ac;
1004 char *av[];
1006 char buff[MAX_BUFF_LEN + 1];
1007 time_t d;
1009 (void) printf ("Enter date, or blank line to exit.\n\t> ");
1010 (void) fflush (stdout);
1012 buff[MAX_BUFF_LEN] = 0;
1013 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
1015 d = get_date (buff, (time_t *) NULL);
1016 if (d == -1)
1017 (void) printf ("Bad format - couldn't convert.\n");
1018 else
1019 (void) printf ("%s", ctime (&d));
1020 (void) printf ("\t> ");
1021 (void) fflush (stdout);
1023 exit (0);
1024 /* NOTREACHED */
1026 #endif /* defined (TEST) */