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