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