turns printfs back on
[freebsd-src/fkvm-freebsd.git] / contrib / cvs / lib / getdate.y
blob0e128d74ef6098188f77d8e5698f4356a99c128c
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 10 shift/reduce conflicts.
9 **
10 ** This code is in the public domain and has no copyright.
12 /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13 /* SUPPRESS 288 on yyerrlab *//* Label unused */
15 #ifdef HAVE_CONFIG_H
16 #if defined (emacs) || defined (CONFIG_BROKETS)
17 #include <config.h>
18 #else
19 #include "config.h"
20 #endif
21 #endif
23 /* Since the code of getdate.y is not included in the Emacs executable
24 itself, there is no need to #define static in this file. Even if
25 the code were included in the Emacs executable, it probably
26 wouldn't do any harm to #undef it here; this will only cause
27 problems if we try to write to a static variable, which I don't
28 think this code needs to do. */
29 #ifdef emacs
30 #undef static
31 #endif
33 #include <stdio.h>
34 #include <ctype.h>
36 /* The code at the top of get_date which figures out the offset of the
37 current time zone checks various CPP symbols to see if special
38 tricks are need, but defaults to using the gettimeofday system call.
39 Include <sys/time.h> if that will be used. */
41 #if defined(vms)
42 # include <types.h>
43 #else /* defined(vms) */
44 # include <sys/types.h>
45 #endif /* !defined(vms) */
46 # include "xtime.h"
48 #if defined (STDC_HEADERS) || defined (USG)
49 #include <string.h>
50 #endif
52 /* Some old versions of bison generate parsers that use bcopy.
53 That loses on systems that don't provide the function, so we have
54 to redefine it here. */
55 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
56 #define bcopy(from, to, len) memcpy ((to), (from), (len))
57 #endif
59 #if defined (STDC_HEADERS)
60 #include <stdlib.h>
61 #endif
63 /* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
64 releases):
66 We don't want to mess with all the portability hassles of alloca.
67 In particular, most (all?) versions of bison will use alloca in
68 their parser. If bison works on your system (e.g. it should work
69 with gcc), then go ahead and use it, but the more general solution
70 is to use byacc instead of bison, which should generate a portable
71 parser. I played with adding "#define alloca dont_use_alloca", to
72 give an error if the parser generator uses alloca (and thus detect
73 unportable getdate.c's), but that seems to cause as many problems
74 as it solves. */
76 extern struct tm *gmtime();
77 extern struct tm *localtime();
79 #define yyparse getdate_yyparse
80 #define yylex getdate_yylex
81 #define yyerror getdate_yyerror
83 static int yyparse ();
84 static int yylex ();
85 static int yyerror ();
87 #define EPOCH 1970
88 #define HOUR(x) ((time_t)(x) * 60)
89 #define SECSPERDAY (24L * 60L * 60L)
93 ** An entry in the lexical lookup table.
95 typedef struct _TABLE {
96 char *name;
97 int type;
98 time_t value;
99 } TABLE;
103 ** Daylight-savings mode: on, off, or not yet known.
105 typedef enum _DSTMODE {
106 DSTon, DSToff, DSTmaybe
107 } DSTMODE;
110 ** Meridian: am, pm, or 24-hour style.
112 typedef enum _MERIDIAN {
113 MERam, MERpm, MER24
114 } MERIDIAN;
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.
123 static char *yyInput;
124 static DSTMODE yyDSTmode;
125 static time_t yyDayOrdinal;
126 static time_t yyDayNumber;
127 static int yyHaveDate;
128 static int yyHaveDay;
129 static int yyHaveRel;
130 static int yyHaveTime;
131 static int yyHaveZone;
132 static time_t yyTimezone;
133 static time_t yyDay;
134 static time_t yyHour;
135 static time_t yyMinutes;
136 static time_t yyMonth;
137 static time_t yySeconds;
138 static time_t yyYear;
139 static MERIDIAN yyMeridian;
140 static time_t yyRelMonth;
141 static time_t yyRelSeconds;
145 %union {
146 time_t Number;
147 enum _MERIDIAN Meridian;
150 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
151 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
153 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
154 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
155 %type <Meridian> tMERIDIAN o_merid
159 spec : /* NULL */
160 | spec item
163 item : time {
164 yyHaveTime++;
166 | zone {
167 yyHaveZone++;
169 | date {
170 yyHaveDate++;
172 | day {
173 yyHaveDay++;
175 | rel {
176 yyHaveRel++;
178 | cvsstamp {
179 yyHaveTime++;
180 yyHaveDate++;
181 yyHaveZone++;
183 | number
186 cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
187 yyYear = $1;
188 if (yyYear < 100) yyYear += 1900;
189 yyMonth = $3;
190 yyDay = $5;
191 yyHour = $7;
192 yyMinutes = $9;
193 yySeconds = $11;
194 yyDSTmode = DSToff;
195 yyTimezone = 0;
199 time : tUNUMBER tMERIDIAN {
200 yyHour = $1;
201 yyMinutes = 0;
202 yySeconds = 0;
203 yyMeridian = $2;
205 | tUNUMBER ':' tUNUMBER o_merid {
206 yyHour = $1;
207 yyMinutes = $3;
208 yySeconds = 0;
209 yyMeridian = $4;
211 | tUNUMBER ':' tUNUMBER tSNUMBER {
212 yyHour = $1;
213 yyMinutes = $3;
214 yyMeridian = MER24;
215 yyDSTmode = DSToff;
216 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
218 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
219 yyHour = $1;
220 yyMinutes = $3;
221 yySeconds = $5;
222 yyMeridian = $6;
224 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
225 yyHour = $1;
226 yyMinutes = $3;
227 yySeconds = $5;
228 yyMeridian = MER24;
229 yyDSTmode = DSToff;
230 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
234 zone : tZONE {
235 yyTimezone = $1;
236 yyDSTmode = DSToff;
238 | tDAYZONE {
239 yyTimezone = $1;
240 yyDSTmode = DSTon;
243 tZONE tDST {
244 yyTimezone = $1;
245 yyDSTmode = DSTon;
249 day : tDAY {
250 yyDayOrdinal = 1;
251 yyDayNumber = $1;
253 | tDAY ',' {
254 yyDayOrdinal = 1;
255 yyDayNumber = $1;
257 | tUNUMBER tDAY {
258 yyDayOrdinal = $1;
259 yyDayNumber = $2;
263 date : tUNUMBER '/' tUNUMBER {
264 yyMonth = $1;
265 yyDay = $3;
267 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
268 if ($1 >= 100) {
269 yyYear = $1;
270 yyMonth = $3;
271 yyDay = $5;
272 } else {
273 yyMonth = $1;
274 yyDay = $3;
275 yyYear = $5;
278 | tUNUMBER tSNUMBER tSNUMBER {
279 /* ISO 8601 format. yyyy-mm-dd. */
280 yyYear = $1;
281 yyMonth = -$2;
282 yyDay = -$3;
284 | tUNUMBER tMONTH tSNUMBER {
285 /* e.g. 17-JUN-1992. */
286 yyDay = $1;
287 yyMonth = $2;
288 yyYear = -$3;
290 | tMONTH tUNUMBER {
291 yyMonth = $1;
292 yyDay = $2;
294 | tMONTH tUNUMBER ',' tUNUMBER {
295 yyMonth = $1;
296 yyDay = $2;
297 yyYear = $4;
299 | tUNUMBER tMONTH {
300 yyMonth = $2;
301 yyDay = $1;
303 | tUNUMBER tMONTH tUNUMBER {
304 yyMonth = $2;
305 yyDay = $1;
306 yyYear = $3;
310 rel : relunit tAGO {
311 yyRelSeconds = -yyRelSeconds;
312 yyRelMonth = -yyRelMonth;
314 | relunit
317 relunit : tUNUMBER tMINUTE_UNIT {
318 yyRelSeconds += $1 * $2 * 60L;
320 | tSNUMBER tMINUTE_UNIT {
321 yyRelSeconds += $1 * $2 * 60L;
323 | tMINUTE_UNIT {
324 yyRelSeconds += $1 * 60L;
326 | tSNUMBER tSEC_UNIT {
327 yyRelSeconds += $1;
329 | tUNUMBER tSEC_UNIT {
330 yyRelSeconds += $1;
332 | tSEC_UNIT {
333 yyRelSeconds++;
335 | tSNUMBER tMONTH_UNIT {
336 yyRelMonth += $1 * $2;
338 | tUNUMBER tMONTH_UNIT {
339 yyRelMonth += $1 * $2;
341 | tMONTH_UNIT {
342 yyRelMonth += $1;
346 number : tUNUMBER {
347 if (yyHaveTime && yyHaveDate && !yyHaveRel)
348 yyYear = $1;
349 else {
350 if($1>10000) {
351 yyHaveDate++;
352 yyDay= ($1)%100;
353 yyMonth= ($1/100)%100;
354 yyYear = $1/10000;
356 else {
357 yyHaveTime++;
358 if ($1 < 100) {
359 yyHour = $1;
360 yyMinutes = 0;
362 else {
363 yyHour = $1 / 100;
364 yyMinutes = $1 % 100;
366 yySeconds = 0;
367 yyMeridian = MER24;
373 o_merid : /* NULL */ {
374 $$ = MER24;
376 | tMERIDIAN {
377 $$ = $1;
383 /* Month and day table. */
384 static TABLE const MonthDayTable[] = {
385 { "january", tMONTH, 1 },
386 { "february", tMONTH, 2 },
387 { "march", tMONTH, 3 },
388 { "april", tMONTH, 4 },
389 { "may", tMONTH, 5 },
390 { "june", tMONTH, 6 },
391 { "july", tMONTH, 7 },
392 { "august", tMONTH, 8 },
393 { "september", tMONTH, 9 },
394 { "sept", tMONTH, 9 },
395 { "october", tMONTH, 10 },
396 { "november", tMONTH, 11 },
397 { "december", tMONTH, 12 },
398 { "sunday", tDAY, 0 },
399 { "monday", tDAY, 1 },
400 { "tuesday", tDAY, 2 },
401 { "tues", tDAY, 2 },
402 { "wednesday", tDAY, 3 },
403 { "wednes", tDAY, 3 },
404 { "thursday", tDAY, 4 },
405 { "thur", tDAY, 4 },
406 { "thurs", tDAY, 4 },
407 { "friday", tDAY, 5 },
408 { "saturday", tDAY, 6 },
409 { NULL }
412 /* Time units table. */
413 static TABLE const UnitsTable[] = {
414 { "year", tMONTH_UNIT, 12 },
415 { "month", tMONTH_UNIT, 1 },
416 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
417 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
418 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
419 { "hour", tMINUTE_UNIT, 60 },
420 { "minute", tMINUTE_UNIT, 1 },
421 { "min", tMINUTE_UNIT, 1 },
422 { "second", tSEC_UNIT, 1 },
423 { "sec", tSEC_UNIT, 1 },
424 { NULL }
427 /* Assorted relative-time words. */
428 static TABLE const OtherTable[] = {
429 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
430 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
431 { "today", tMINUTE_UNIT, 0 },
432 { "now", tMINUTE_UNIT, 0 },
433 { "last", tUNUMBER, -1 },
434 { "this", tMINUTE_UNIT, 0 },
435 { "next", tUNUMBER, 2 },
436 { "first", tUNUMBER, 1 },
437 /* { "second", tUNUMBER, 2 }, */
438 { "third", tUNUMBER, 3 },
439 { "fourth", tUNUMBER, 4 },
440 { "fifth", tUNUMBER, 5 },
441 { "sixth", tUNUMBER, 6 },
442 { "seventh", tUNUMBER, 7 },
443 { "eighth", tUNUMBER, 8 },
444 { "ninth", tUNUMBER, 9 },
445 { "tenth", tUNUMBER, 10 },
446 { "eleventh", tUNUMBER, 11 },
447 { "twelfth", tUNUMBER, 12 },
448 { "ago", tAGO, 1 },
449 { NULL }
452 /* The timezone table. */
453 /* Some of these are commented out because a time_t can't store a float. */
454 static TABLE const TimezoneTable[] = {
455 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
456 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
457 { "utc", tZONE, HOUR( 0) },
458 { "wet", tZONE, HOUR( 0) }, /* Western European */
459 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
460 { "wat", tZONE, HOUR( 1) }, /* West Africa */
461 { "at", tZONE, HOUR( 2) }, /* Azores */
462 #if 0
463 /* For completeness. BST is also British Summer, and GST is
464 * also Guam Standard. */
465 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
466 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
467 #endif
468 #if 0
469 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
470 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
471 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
472 #endif
473 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
474 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
475 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
476 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
477 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
478 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
479 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
480 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
481 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
482 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
483 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
484 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
485 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
486 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
487 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
488 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
489 { "nt", tZONE, HOUR(11) }, /* Nome */
490 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
491 { "cet", tZONE, -HOUR(1) }, /* Central European */
492 { "met", tZONE, -HOUR(1) }, /* Middle European */
493 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
494 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
495 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
496 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
497 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
498 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
499 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
500 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
501 #if 0
502 { "it", tZONE, -HOUR(3.5) },/* Iran */
503 #endif
504 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
505 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
506 #if 0
507 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
508 #endif
509 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
510 #if 0
511 /* For completeness. NST is also Newfoundland Stanard, and SST is
512 * also Swedish Summer. */
513 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
514 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
515 #endif /* 0 */
516 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
517 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
518 #if 0
519 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
520 #endif
521 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
522 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
523 #if 0
524 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
525 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
526 #endif
527 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
528 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
529 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
530 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
531 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
532 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
533 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
534 { NULL }
537 /* Military timezone table. */
538 static TABLE const MilitaryTable[] = {
539 { "a", tZONE, HOUR( 1) },
540 { "b", tZONE, HOUR( 2) },
541 { "c", tZONE, HOUR( 3) },
542 { "d", tZONE, HOUR( 4) },
543 { "e", tZONE, HOUR( 5) },
544 { "f", tZONE, HOUR( 6) },
545 { "g", tZONE, HOUR( 7) },
546 { "h", tZONE, HOUR( 8) },
547 { "i", tZONE, HOUR( 9) },
548 { "k", tZONE, HOUR( 10) },
549 { "l", tZONE, HOUR( 11) },
550 { "m", tZONE, HOUR( 12) },
551 { "n", tZONE, HOUR(- 1) },
552 { "o", tZONE, HOUR(- 2) },
553 { "p", tZONE, HOUR(- 3) },
554 { "q", tZONE, HOUR(- 4) },
555 { "r", tZONE, HOUR(- 5) },
556 { "s", tZONE, HOUR(- 6) },
557 { "t", tZONE, HOUR(- 7) },
558 { "u", tZONE, HOUR(- 8) },
559 { "v", tZONE, HOUR(- 9) },
560 { "w", tZONE, HOUR(-10) },
561 { "x", tZONE, HOUR(-11) },
562 { "y", tZONE, HOUR(-12) },
563 { "z", tZONE, HOUR( 0) },
564 { NULL }
570 /* ARGSUSED */
571 static int
572 yyerror(s)
573 char *s;
575 return 0;
579 static time_t
580 ToSeconds(Hours, Minutes, Seconds, Meridian)
581 time_t Hours;
582 time_t Minutes;
583 time_t Seconds;
584 MERIDIAN Meridian;
586 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
587 return -1;
588 switch (Meridian) {
589 case MER24:
590 if (Hours < 0 || Hours > 23)
591 return -1;
592 return (Hours * 60L + Minutes) * 60L + Seconds;
593 case MERam:
594 if (Hours < 1 || Hours > 12)
595 return -1;
596 if (Hours == 12)
597 Hours = 0;
598 return (Hours * 60L + Minutes) * 60L + Seconds;
599 case MERpm:
600 if (Hours < 1 || Hours > 12)
601 return -1;
602 if (Hours == 12)
603 Hours = 0;
604 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
605 default:
606 abort ();
608 /* NOTREACHED */
612 /* Year is either
613 * A negative number, which means to use its absolute value (why?)
614 * A number from 0 to 99, which means a year from 1900 to 1999, or
615 * The actual year (>=100). */
616 static time_t
617 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
618 time_t Month;
619 time_t Day;
620 time_t Year;
621 time_t Hours;
622 time_t Minutes;
623 time_t Seconds;
624 MERIDIAN Meridian;
625 DSTMODE DSTmode;
627 static int DaysInMonth[12] = {
628 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
630 time_t tod;
631 time_t Julian;
632 int i;
634 if (Year < 0)
635 Year = -Year;
636 if (Year < 69)
637 Year += 2000;
638 else if (Year < 100)
639 Year += 1900;
640 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
641 ? 29 : 28;
642 /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
643 I'm too lazy to try to check for time_t overflow in another way. */
644 if (Year < EPOCH || Year > 2038
645 || Month < 1 || Month > 12
646 /* Lint fluff: "conversion from long may lose accuracy" */
647 || Day < 1 || Day > DaysInMonth[(int)--Month])
648 /* FIXME:
649 * It would be nice to set a global error string here.
650 * "February 30 is not a valid date" is much more informative than
651 * "Can't parse date/time: 100 months" when the user input was
652 * "100 months" and addition resolved that to February 30, for
653 * example. See rcs2-7 in src/sanity.sh for more. */
654 return -1;
656 for (Julian = Day - 1, i = 0; i < Month; i++)
657 Julian += DaysInMonth[i];
658 for (i = EPOCH; i < Year; i++)
659 Julian += 365 + (i % 4 == 0);
660 Julian *= SECSPERDAY;
661 Julian += yyTimezone * 60L;
662 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
663 return -1;
664 Julian += tod;
665 if (DSTmode == DSTon
666 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
667 Julian -= 60 * 60;
668 return Julian;
672 static time_t
673 DSTcorrect(Start, Future)
674 time_t Start;
675 time_t Future;
677 time_t StartDay;
678 time_t FutureDay;
680 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
681 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
682 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
686 static time_t
687 RelativeDate(Start, DayOrdinal, DayNumber)
688 time_t Start;
689 time_t DayOrdinal;
690 time_t DayNumber;
692 struct tm *tm;
693 time_t now;
695 now = Start;
696 tm = localtime(&now);
697 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
698 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
699 return DSTcorrect(Start, now);
703 static time_t
704 RelativeMonth(Start, RelMonth)
705 time_t Start;
706 time_t RelMonth;
708 struct tm *tm;
709 time_t Month;
710 time_t Year;
712 if (RelMonth == 0)
713 return 0;
714 tm = localtime(&Start);
715 Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
716 Year = Month / 12;
717 Month = Month % 12 + 1;
718 return DSTcorrect(Start,
719 Convert(Month, (time_t)tm->tm_mday, Year,
720 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
721 MER24, DSTmaybe));
725 static int
726 LookupWord(buff)
727 char *buff;
729 register char *p;
730 register char *q;
731 register const TABLE *tp;
732 int i;
733 int abbrev;
735 /* Make it lowercase. */
736 for (p = buff; *p; p++)
737 if (isupper(*p))
738 *p = tolower(*p);
740 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
741 yylval.Meridian = MERam;
742 return tMERIDIAN;
744 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
745 yylval.Meridian = MERpm;
746 return tMERIDIAN;
749 /* See if we have an abbreviation for a month. */
750 if (strlen(buff) == 3)
751 abbrev = 1;
752 else if (strlen(buff) == 4 && buff[3] == '.') {
753 abbrev = 1;
754 buff[3] = '\0';
756 else
757 abbrev = 0;
759 for (tp = MonthDayTable; tp->name; tp++) {
760 if (abbrev) {
761 if (strncmp(buff, tp->name, 3) == 0) {
762 yylval.Number = tp->value;
763 return tp->type;
766 else if (strcmp(buff, tp->name) == 0) {
767 yylval.Number = tp->value;
768 return tp->type;
772 for (tp = TimezoneTable; tp->name; tp++)
773 if (strcmp(buff, tp->name) == 0) {
774 yylval.Number = tp->value;
775 return tp->type;
778 if (strcmp(buff, "dst") == 0)
779 return tDST;
781 for (tp = UnitsTable; tp->name; tp++)
782 if (strcmp(buff, tp->name) == 0) {
783 yylval.Number = tp->value;
784 return tp->type;
787 /* Strip off any plural and try the units table again. */
788 i = strlen(buff) - 1;
789 if (buff[i] == 's') {
790 buff[i] = '\0';
791 for (tp = UnitsTable; tp->name; tp++)
792 if (strcmp(buff, tp->name) == 0) {
793 yylval.Number = tp->value;
794 return tp->type;
796 buff[i] = 's'; /* Put back for "this" in OtherTable. */
799 for (tp = OtherTable; tp->name; tp++)
800 if (strcmp(buff, tp->name) == 0) {
801 yylval.Number = tp->value;
802 return tp->type;
805 /* Military timezones. */
806 if (buff[1] == '\0' && isalpha(*buff)) {
807 for (tp = MilitaryTable; tp->name; tp++)
808 if (strcmp(buff, tp->name) == 0) {
809 yylval.Number = tp->value;
810 return tp->type;
814 /* Drop out any periods and try the timezone table again. */
815 for (i = 0, p = q = buff; *q; q++)
816 if (*q != '.')
817 *p++ = *q;
818 else
819 i++;
820 *p = '\0';
821 if (i)
822 for (tp = TimezoneTable; tp->name; tp++)
823 if (strcmp(buff, tp->name) == 0) {
824 yylval.Number = tp->value;
825 return tp->type;
828 return tID;
832 static int
833 yylex()
835 register char c;
836 register char *p;
837 char buff[20];
838 int Count;
839 int sign;
841 for ( ; ; ) {
842 while (isspace(*yyInput))
843 yyInput++;
845 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
846 if (c == '-' || c == '+') {
847 sign = c == '-' ? -1 : 1;
848 if (!isdigit(*++yyInput))
849 /* skip the '-' sign */
850 continue;
852 else
853 sign = 0;
854 for (yylval.Number = 0; isdigit(c = *yyInput++); )
855 yylval.Number = 10 * yylval.Number + c - '0';
856 yyInput--;
857 if (sign < 0)
858 yylval.Number = -yylval.Number;
859 return sign ? tSNUMBER : tUNUMBER;
861 if (isalpha(c)) {
862 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
863 if (p < &buff[sizeof buff - 1])
864 *p++ = c;
865 *p = '\0';
866 yyInput--;
867 return LookupWord(buff);
869 if (c != '(')
870 return *yyInput++;
871 Count = 0;
872 do {
873 c = *yyInput++;
874 if (c == '\0')
875 return c;
876 if (c == '(')
877 Count++;
878 else if (c == ')')
879 Count--;
880 } while (Count > 0);
884 #define TM_YEAR_ORIGIN 1900
886 /* Yield A - B, measured in seconds. */
887 static long
888 difftm (a, b)
889 struct tm *a, *b;
891 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
892 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
893 int days = (
894 /* difference in day of year */
895 a->tm_yday - b->tm_yday
896 /* + intervening leap days */
897 + ((ay >> 2) - (by >> 2))
898 - (ay/100 - by/100)
899 + ((ay/100 >> 2) - (by/100 >> 2))
900 /* + difference in years * 365 */
901 + (long)(ay-by) * 365
903 return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
904 + (a->tm_min - b->tm_min))
905 + (a->tm_sec - b->tm_sec));
908 time_t
909 get_date(p, now)
910 char *p;
911 struct timeb *now;
913 struct tm *tm, gmt;
914 struct timeb ftz;
915 time_t Start;
916 time_t tod;
917 time_t nowtime;
919 yyInput = p;
920 if (now == NULL) {
921 struct tm *gmt_ptr;
923 now = &ftz;
924 (void)time (&nowtime);
926 gmt_ptr = gmtime (&nowtime);
927 if (gmt_ptr != NULL)
929 /* Make a copy, in case localtime modifies *tm (I think
930 that comment now applies to *gmt_ptr, but I am too
931 lazy to dig into how gmtime and locatime allocate the
932 structures they return pointers to). */
933 gmt = *gmt_ptr;
936 if (! (tm = localtime (&nowtime)))
937 return -1;
939 if (gmt_ptr != NULL)
940 ftz.timezone = difftm (&gmt, tm) / 60;
941 else
942 /* We are on a system like VMS, where the system clock is
943 in local time and the system has no concept of timezones.
944 Hopefully we can fake this out (for the case in which the
945 user specifies no timezone) by just saying the timezone
946 is zero. */
947 ftz.timezone = 0;
949 if(tm->tm_isdst)
950 ftz.timezone += 60;
952 else
954 nowtime = now->time;
957 tm = localtime(&nowtime);
958 yyYear = tm->tm_year + 1900;
959 yyMonth = tm->tm_mon + 1;
960 yyDay = tm->tm_mday;
961 yyTimezone = now->timezone;
962 yyDSTmode = DSTmaybe;
963 yyHour = 0;
964 yyMinutes = 0;
965 yySeconds = 0;
966 yyMeridian = MER24;
967 yyRelSeconds = 0;
968 yyRelMonth = 0;
969 yyHaveDate = 0;
970 yyHaveDay = 0;
971 yyHaveRel = 0;
972 yyHaveTime = 0;
973 yyHaveZone = 0;
975 if (yyparse()
976 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
977 return -1;
979 if (yyHaveDate || yyHaveTime || yyHaveDay) {
980 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
981 yyMeridian, yyDSTmode);
982 if (Start < 0)
983 return -1;
985 else {
986 Start = nowtime;
987 if (!yyHaveRel)
988 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
991 Start += yyRelSeconds;
992 Start += RelativeMonth(Start, yyRelMonth);
994 if (yyHaveDay && !yyHaveDate) {
995 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
996 Start += tod;
999 /* Have to do *something* with a legitimate -1 so it's distinguishable
1000 * from the error return value. (Alternately could set errno on error.) */
1001 return Start == -1 ? 0 : Start;
1005 #if defined(TEST)
1007 /* ARGSUSED */
1009 main(ac, av)
1010 int ac;
1011 char *av[];
1013 char buff[128];
1014 time_t d;
1016 (void)printf("Enter date, or blank line to exit.\n\t> ");
1017 (void)fflush(stdout);
1018 while (gets(buff) && buff[0]) {
1019 d = get_date(buff, (struct timeb *)NULL);
1020 if (d == -1)
1021 (void)printf("Bad format - couldn't convert.\n");
1022 else
1023 (void)printf("%s", ctime(&d));
1024 (void)printf("\t> ");
1025 (void)fflush(stdout);
1027 exit(0);
1028 /* NOTREACHED */
1030 #endif /* defined(TEST) */