merge with 3.8.4c
[coreutils.git] / lib / getdate.y
blobdf1a08a98dbdfd9c3c92f033e2dc81ef2a3914e4
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 ** send any email to Rich.
8 **
9 ** This grammar has nine shift/reduce conflicts.
11 ** This code is in the public domain and has no copyright.
13 /* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
14 /* SUPPRESS 288 on yyerrlab *//* Label unused */
16 #ifdef HAVE_CONFIG_H
17 #if defined (emacs) || defined (CONFIG_BROKETS)
18 #include <config.h>
19 #else
20 #include "config.h"
21 #endif
22 #endif
24 /* Since the code of getdate.y is not included in the Emacs executable
25 itself, there is no need to #define static in this file. Even if
26 the code were included in the Emacs executable, it probably
27 wouldn't do any harm to #undef it here; this will only cause
28 problems if we try to write to a static variable, which I don't
29 think this code needs to do. */
30 #ifdef emacs
31 #undef static
32 #endif
34 /* The following block of alloca-related preprocessor directives is here
35 solely to allow compilation by non GNU-C compilers of the C parser
36 produced from this file by old versions of bison. Newer versions of
37 bison include a block similar to this one in bison.simple. */
39 #ifdef __GNUC__
40 #define alloca __builtin_alloca
41 #else
42 #ifdef HAVE_ALLOCA_H
43 #include <alloca.h>
44 #else
45 #ifdef _AIX
46 #pragma alloca
47 #else
48 void *alloca ();
49 #endif
50 #endif
51 #endif
53 #ifdef __GNUC__
54 #undef alloca
55 #define alloca __builtin_alloca
56 #else
57 #ifdef HAVE_ALLOCA_H
58 #include <alloca.h>
59 #else
60 #ifdef _AIX /* for Bison */
61 #pragma alloca
62 #else
63 void *alloca ();
64 #endif
65 #endif
66 #endif
68 #include <stdio.h>
69 #include <ctype.h>
71 /* The code at the top of get_date which figures out the offset of the
72 current time zone checks various CPP symbols to see if special
73 tricks are need, but defaults to using the gettimeofday system call.
74 Include <sys/time.h> if that will be used. */
76 #if defined(vms)
78 #include <types.h>
79 #include <time.h>
81 #else
83 #include <sys/types.h>
85 #ifdef TIME_WITH_SYS_TIME
86 #include <sys/time.h>
87 #include <time.h>
88 #else
89 #ifdef HAVE_SYS_TIME_H
90 #include <sys/time.h>
91 #else
92 #include <time.h>
93 #endif
94 #endif
96 #ifdef timezone
97 #undef timezone /* needed for sgi */
98 #endif
100 #if defined(HAVE_SYS_TIMEB_H) || (!defined(USG) && defined(HAVE_FTIME))
101 #include <sys/timeb.h>
102 #else
104 ** We use the obsolete `struct timeb' as part of our interface!
105 ** Since the system doesn't have it, we define it here;
106 ** our callers must do likewise.
108 struct timeb {
109 time_t time; /* Seconds since the epoch */
110 unsigned short millitm; /* Field not used */
111 short timezone; /* Minutes west of GMT */
112 short dstflag; /* Field not used */
114 #endif /* defined(HAVE_SYS_TIMEB_H) */
116 #endif /* defined(vms) */
118 #if defined (STDC_HEADERS) || defined (USG)
119 #include <string.h>
120 #endif
122 /* Some old versions of bison generate parsers that use bcopy.
123 That loses on systems that don't provide the function, so we have
124 to redefine it here. */
125 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
126 #define bcopy(from, to, len) memcpy ((to), (from), (len))
127 #endif
129 extern struct tm *gmtime();
130 extern struct tm *localtime();
132 #define yyparse getdate_yyparse
133 #define yylex getdate_yylex
134 #define yyerror getdate_yyerror
136 static int yylex ();
137 static int yyerror ();
139 #if !defined(lint) && !defined(SABER)
140 static char RCS[] =
141 "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
142 #endif /* !defined(lint) && !defined(SABER) */
145 #define EPOCH 1970
146 #define HOUR(x) ((time_t)(x) * 60)
147 #define SECSPERDAY (24L * 60L * 60L)
151 ** An entry in the lexical lookup table.
153 typedef struct _TABLE {
154 char *name;
155 int type;
156 time_t value;
157 } TABLE;
161 ** Daylight-savings mode: on, off, or not yet known.
163 typedef enum _DSTMODE {
164 DSTon, DSToff, DSTmaybe
165 } DSTMODE;
168 ** Meridian: am, pm, or 24-hour style.
170 typedef enum _MERIDIAN {
171 MERam, MERpm, MER24
172 } MERIDIAN;
176 ** Global variables. We could get rid of most of these by using a good
177 ** union as the yacc stack. (This routine was originally written before
178 ** yacc had the %union construct.) Maybe someday; right now we only use
179 ** the %union very rarely.
181 static char *yyInput;
182 static DSTMODE yyDSTmode;
183 static time_t yyDayOrdinal;
184 static time_t yyDayNumber;
185 static int yyHaveDate;
186 static int yyHaveDay;
187 static int yyHaveRel;
188 static int yyHaveTime;
189 static int yyHaveZone;
190 static time_t yyTimezone;
191 static time_t yyDay;
192 static time_t yyHour;
193 static time_t yyMinutes;
194 static time_t yyMonth;
195 static time_t yySeconds;
196 static time_t yyYear;
197 static MERIDIAN yyMeridian;
198 static time_t yyRelMonth;
199 static time_t yyRelSeconds;
203 %union {
204 time_t Number;
205 enum _MERIDIAN Meridian;
208 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
209 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
211 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
212 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
213 %type <Meridian> tMERIDIAN o_merid
217 spec : /* NULL */
218 | spec item
221 item : time {
222 yyHaveTime++;
224 | zone {
225 yyHaveZone++;
227 | date {
228 yyHaveDate++;
230 | day {
231 yyHaveDay++;
233 | rel {
234 yyHaveRel++;
236 | number
239 time : tUNUMBER tMERIDIAN {
240 yyHour = $1;
241 yyMinutes = 0;
242 yySeconds = 0;
243 yyMeridian = $2;
245 | tUNUMBER ':' tUNUMBER o_merid {
246 yyHour = $1;
247 yyMinutes = $3;
248 yySeconds = 0;
249 yyMeridian = $4;
251 | tUNUMBER ':' tUNUMBER tSNUMBER {
252 yyHour = $1;
253 yyMinutes = $3;
254 yyMeridian = MER24;
255 yyDSTmode = DSToff;
256 yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
258 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
259 yyHour = $1;
260 yyMinutes = $3;
261 yySeconds = $5;
262 yyMeridian = $6;
264 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
265 yyHour = $1;
266 yyMinutes = $3;
267 yySeconds = $5;
268 yyMeridian = MER24;
269 yyDSTmode = DSToff;
270 yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
274 zone : tZONE {
275 yyTimezone = $1;
276 yyDSTmode = DSToff;
278 | tDAYZONE {
279 yyTimezone = $1;
280 yyDSTmode = DSTon;
283 tZONE tDST {
284 yyTimezone = $1;
285 yyDSTmode = DSTon;
289 day : tDAY {
290 yyDayOrdinal = 1;
291 yyDayNumber = $1;
293 | tDAY ',' {
294 yyDayOrdinal = 1;
295 yyDayNumber = $1;
297 | tUNUMBER tDAY {
298 yyDayOrdinal = $1;
299 yyDayNumber = $2;
303 date : tUNUMBER '/' tUNUMBER {
304 yyMonth = $1;
305 yyDay = $3;
307 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
308 yyMonth = $1;
309 yyDay = $3;
310 yyYear = $5;
312 | tUNUMBER tSNUMBER tSNUMBER {
313 /* ISO 8601 format. yyyy-mm-dd. */
314 yyYear = $1;
315 yyMonth = -$2;
316 yyDay = -$3;
318 | tMONTH tUNUMBER {
319 yyMonth = $1;
320 yyDay = $2;
322 | tMONTH tUNUMBER ',' tUNUMBER {
323 yyMonth = $1;
324 yyDay = $2;
325 yyYear = $4;
327 | tUNUMBER tMONTH {
328 yyMonth = $2;
329 yyDay = $1;
331 | tUNUMBER tMONTH tUNUMBER {
332 yyMonth = $2;
333 yyDay = $1;
334 yyYear = $3;
338 rel : relunit tAGO {
339 yyRelSeconds = -yyRelSeconds;
340 yyRelMonth = -yyRelMonth;
342 | relunit
345 relunit : tUNUMBER tMINUTE_UNIT {
346 yyRelSeconds += $1 * $2 * 60L;
348 | tSNUMBER tMINUTE_UNIT {
349 yyRelSeconds += $1 * $2 * 60L;
351 | tMINUTE_UNIT {
352 yyRelSeconds += $1 * 60L;
354 | tSNUMBER tSEC_UNIT {
355 yyRelSeconds += $1;
357 | tUNUMBER tSEC_UNIT {
358 yyRelSeconds += $1;
360 | tSEC_UNIT {
361 yyRelSeconds++;
363 | tSNUMBER tMONTH_UNIT {
364 yyRelMonth += $1 * $2;
366 | tUNUMBER tMONTH_UNIT {
367 yyRelMonth += $1 * $2;
369 | tMONTH_UNIT {
370 yyRelMonth += $1;
374 number : tUNUMBER {
375 if (yyHaveTime && yyHaveDate && !yyHaveRel)
376 yyYear = $1;
377 else {
378 if($1>10000) {
379 time_t date_part;
381 date_part= $1/10000;
382 yyHaveDate++;
383 yyDay= (date_part)%100;
384 yyMonth= (date_part/100)%100;
385 yyYear = date_part/10000;
387 yyHaveTime++;
388 if ($1 < 100) {
389 yyHour = $1;
390 yyMinutes = 0;
392 else {
393 yyHour = $1 / 100;
394 yyMinutes = $1 % 100;
396 yySeconds = 0;
397 yyMeridian = MER24;
402 o_merid : /* NULL */ {
403 $$ = MER24;
405 | tMERIDIAN {
406 $$ = $1;
412 /* Month and day table. */
413 static TABLE const MonthDayTable[] = {
414 { "january", tMONTH, 1 },
415 { "february", tMONTH, 2 },
416 { "march", tMONTH, 3 },
417 { "april", tMONTH, 4 },
418 { "may", tMONTH, 5 },
419 { "june", tMONTH, 6 },
420 { "july", tMONTH, 7 },
421 { "august", tMONTH, 8 },
422 { "september", tMONTH, 9 },
423 { "sept", tMONTH, 9 },
424 { "october", tMONTH, 10 },
425 { "november", tMONTH, 11 },
426 { "december", tMONTH, 12 },
427 { "sunday", tDAY, 0 },
428 { "monday", tDAY, 1 },
429 { "tuesday", tDAY, 2 },
430 { "tues", tDAY, 2 },
431 { "wednesday", tDAY, 3 },
432 { "wednes", tDAY, 3 },
433 { "thursday", tDAY, 4 },
434 { "thur", tDAY, 4 },
435 { "thurs", tDAY, 4 },
436 { "friday", tDAY, 5 },
437 { "saturday", tDAY, 6 },
438 { NULL }
441 /* Time units table. */
442 static TABLE const UnitsTable[] = {
443 { "year", tMONTH_UNIT, 12 },
444 { "month", tMONTH_UNIT, 1 },
445 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
446 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
447 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
448 { "hour", tMINUTE_UNIT, 60 },
449 { "minute", tMINUTE_UNIT, 1 },
450 { "min", tMINUTE_UNIT, 1 },
451 { "second", tSEC_UNIT, 1 },
452 { "sec", tSEC_UNIT, 1 },
453 { NULL }
456 /* Assorted relative-time words. */
457 static TABLE const OtherTable[] = {
458 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
459 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
460 { "today", tMINUTE_UNIT, 0 },
461 { "now", tMINUTE_UNIT, 0 },
462 { "last", tUNUMBER, -1 },
463 { "this", tMINUTE_UNIT, 0 },
464 { "next", tUNUMBER, 2 },
465 { "first", tUNUMBER, 1 },
466 /* { "second", tUNUMBER, 2 }, */
467 { "third", tUNUMBER, 3 },
468 { "fourth", tUNUMBER, 4 },
469 { "fifth", tUNUMBER, 5 },
470 { "sixth", tUNUMBER, 6 },
471 { "seventh", tUNUMBER, 7 },
472 { "eighth", tUNUMBER, 8 },
473 { "ninth", tUNUMBER, 9 },
474 { "tenth", tUNUMBER, 10 },
475 { "eleventh", tUNUMBER, 11 },
476 { "twelfth", tUNUMBER, 12 },
477 { "ago", tAGO, 1 },
478 { NULL }
481 /* The timezone table. */
482 /* Some of these are commented out because a time_t can't store a float. */
483 static TABLE const TimezoneTable[] = {
484 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
485 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
486 { "utc", tZONE, HOUR( 0) },
487 { "wet", tZONE, HOUR( 0) }, /* Western European */
488 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
489 { "wat", tZONE, HOUR( 1) }, /* West Africa */
490 { "at", tZONE, HOUR( 2) }, /* Azores */
491 #if 0
492 /* For completeness. BST is also British Summer, and GST is
493 * also Guam Standard. */
494 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
495 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
496 #endif
497 #if 0
498 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
499 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
500 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
501 #endif
502 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
503 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
504 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
505 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
506 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
507 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
508 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
509 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
510 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
511 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
512 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
513 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
514 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
515 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
516 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
517 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
518 { "nt", tZONE, HOUR(11) }, /* Nome */
519 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
520 { "cet", tZONE, -HOUR(1) }, /* Central European */
521 { "met", tZONE, -HOUR(1) }, /* Middle European */
522 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
523 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
524 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
525 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
526 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
527 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
528 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
529 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
530 #if 0
531 { "it", tZONE, -HOUR(3.5) },/* Iran */
532 #endif
533 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
534 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
535 #if 0
536 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
537 #endif
538 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
539 #if 0
540 /* For completeness. NST is also Newfoundland Stanard, and SST is
541 * also Swedish Summer. */
542 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
543 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
544 #endif /* 0 */
545 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
546 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
547 #if 0
548 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
549 #endif
550 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
551 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
552 #if 0
553 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
554 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
555 #endif
556 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
557 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
558 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
559 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
560 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
561 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
562 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
563 { NULL }
566 /* Military timezone table. */
567 static TABLE const MilitaryTable[] = {
568 { "a", tZONE, HOUR( 1) },
569 { "b", tZONE, HOUR( 2) },
570 { "c", tZONE, HOUR( 3) },
571 { "d", tZONE, HOUR( 4) },
572 { "e", tZONE, HOUR( 5) },
573 { "f", tZONE, HOUR( 6) },
574 { "g", tZONE, HOUR( 7) },
575 { "h", tZONE, HOUR( 8) },
576 { "i", tZONE, HOUR( 9) },
577 { "k", tZONE, HOUR( 10) },
578 { "l", tZONE, HOUR( 11) },
579 { "m", tZONE, HOUR( 12) },
580 { "n", tZONE, HOUR(- 1) },
581 { "o", tZONE, HOUR(- 2) },
582 { "p", tZONE, HOUR(- 3) },
583 { "q", tZONE, HOUR(- 4) },
584 { "r", tZONE, HOUR(- 5) },
585 { "s", tZONE, HOUR(- 6) },
586 { "t", tZONE, HOUR(- 7) },
587 { "u", tZONE, HOUR(- 8) },
588 { "v", tZONE, HOUR(- 9) },
589 { "w", tZONE, HOUR(-10) },
590 { "x", tZONE, HOUR(-11) },
591 { "y", tZONE, HOUR(-12) },
592 { "z", tZONE, HOUR( 0) },
593 { NULL }
599 /* ARGSUSED */
600 static int
601 yyerror(s)
602 char *s;
604 return 0;
608 static time_t
609 ToSeconds(Hours, Minutes, Seconds, Meridian)
610 time_t Hours;
611 time_t Minutes;
612 time_t Seconds;
613 MERIDIAN Meridian;
615 if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
616 return -1;
617 switch (Meridian) {
618 case MER24:
619 if (Hours < 0 || Hours > 23)
620 return -1;
621 return (Hours * 60L + Minutes) * 60L + Seconds;
622 case MERam:
623 if (Hours < 1 || Hours > 12)
624 return -1;
625 return (Hours * 60L + Minutes) * 60L + Seconds;
626 case MERpm:
627 if (Hours < 1 || Hours > 12)
628 return -1;
629 return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
631 /* NOTREACHED */
635 static time_t
636 Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
637 time_t Month;
638 time_t Day;
639 time_t Year;
640 time_t Hours;
641 time_t Minutes;
642 time_t Seconds;
643 MERIDIAN Meridian;
644 DSTMODE DSTmode;
646 static int DaysInMonth[12] = {
647 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
649 time_t tod;
650 time_t Julian;
651 int i;
653 if (Year < 0)
654 Year = -Year;
655 if (Year < 100)
656 Year += 1900;
657 DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
658 ? 29 : 28;
659 if (Year < EPOCH || Year > 1999
660 || Month < 1 || Month > 12
661 /* Lint fluff: "conversion from long may lose accuracy" */
662 || Day < 1 || Day > DaysInMonth[(int)--Month])
663 return -1;
665 for (Julian = Day - 1, i = 0; i < Month; i++)
666 Julian += DaysInMonth[i];
667 for (i = EPOCH; i < Year; i++)
668 Julian += 365 + (i % 4 == 0);
669 Julian *= SECSPERDAY;
670 Julian += yyTimezone * 60L;
671 if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
672 return -1;
673 Julian += tod;
674 if (DSTmode == DSTon
675 || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
676 Julian -= 60 * 60;
677 return Julian;
681 static time_t
682 DSTcorrect(Start, Future)
683 time_t Start;
684 time_t Future;
686 time_t StartDay;
687 time_t FutureDay;
689 StartDay = (localtime(&Start)->tm_hour + 1) % 24;
690 FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
691 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
695 static time_t
696 RelativeDate(Start, DayOrdinal, DayNumber)
697 time_t Start;
698 time_t DayOrdinal;
699 time_t DayNumber;
701 struct tm *tm;
702 time_t now;
704 now = Start;
705 tm = localtime(&now);
706 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
707 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
708 return DSTcorrect(Start, now);
712 static time_t
713 RelativeMonth(Start, RelMonth)
714 time_t Start;
715 time_t RelMonth;
717 struct tm *tm;
718 time_t Month;
719 time_t Year;
721 if (RelMonth == 0)
722 return 0;
723 tm = localtime(&Start);
724 Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
725 Year = Month / 12;
726 Month = Month % 12 + 1;
727 return DSTcorrect(Start,
728 Convert(Month, (time_t)tm->tm_mday, Year,
729 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
730 MER24, DSTmaybe));
734 static int
735 LookupWord(buff)
736 char *buff;
738 register char *p;
739 register char *q;
740 register const TABLE *tp;
741 int i;
742 int abbrev;
744 /* Make it lowercase. */
745 for (p = buff; *p; p++)
746 if (isupper(*p))
747 *p = tolower(*p);
749 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
750 yylval.Meridian = MERam;
751 return tMERIDIAN;
753 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
754 yylval.Meridian = MERpm;
755 return tMERIDIAN;
758 /* See if we have an abbreviation for a month. */
759 if (strlen(buff) == 3)
760 abbrev = 1;
761 else if (strlen(buff) == 4 && buff[3] == '.') {
762 abbrev = 1;
763 buff[3] = '\0';
765 else
766 abbrev = 0;
768 for (tp = MonthDayTable; tp->name; tp++) {
769 if (abbrev) {
770 if (strncmp(buff, tp->name, 3) == 0) {
771 yylval.Number = tp->value;
772 return tp->type;
775 else if (strcmp(buff, tp->name) == 0) {
776 yylval.Number = tp->value;
777 return tp->type;
781 for (tp = TimezoneTable; tp->name; tp++)
782 if (strcmp(buff, tp->name) == 0) {
783 yylval.Number = tp->value;
784 return tp->type;
787 if (strcmp(buff, "dst") == 0)
788 return tDST;
790 for (tp = UnitsTable; tp->name; tp++)
791 if (strcmp(buff, tp->name) == 0) {
792 yylval.Number = tp->value;
793 return tp->type;
796 /* Strip off any plural and try the units table again. */
797 i = strlen(buff) - 1;
798 if (buff[i] == 's') {
799 buff[i] = '\0';
800 for (tp = UnitsTable; tp->name; tp++)
801 if (strcmp(buff, tp->name) == 0) {
802 yylval.Number = tp->value;
803 return tp->type;
805 buff[i] = 's'; /* Put back for "this" in OtherTable. */
808 for (tp = OtherTable; tp->name; tp++)
809 if (strcmp(buff, tp->name) == 0) {
810 yylval.Number = tp->value;
811 return tp->type;
814 /* Military timezones. */
815 if (buff[1] == '\0' && isalpha(*buff)) {
816 for (tp = MilitaryTable; tp->name; tp++)
817 if (strcmp(buff, tp->name) == 0) {
818 yylval.Number = tp->value;
819 return tp->type;
823 /* Drop out any periods and try the timezone table again. */
824 for (i = 0, p = q = buff; *q; q++)
825 if (*q != '.')
826 *p++ = *q;
827 else
828 i++;
829 *p = '\0';
830 if (i)
831 for (tp = TimezoneTable; tp->name; tp++)
832 if (strcmp(buff, tp->name) == 0) {
833 yylval.Number = tp->value;
834 return tp->type;
837 return tID;
841 static int
842 yylex()
844 register char c;
845 register char *p;
846 char buff[20];
847 int Count;
848 int sign;
850 for ( ; ; ) {
851 while (isspace(*yyInput))
852 yyInput++;
854 if (isdigit(c = *yyInput) || c == '-' || c == '+') {
855 if (c == '-' || c == '+') {
856 sign = c == '-' ? -1 : 1;
857 if (!isdigit(*++yyInput))
858 /* skip the '-' sign */
859 continue;
861 else
862 sign = 0;
863 for (yylval.Number = 0; isdigit(c = *yyInput++); )
864 yylval.Number = 10 * yylval.Number + c - '0';
865 yyInput--;
866 if (sign < 0)
867 yylval.Number = -yylval.Number;
868 return sign ? tSNUMBER : tUNUMBER;
870 if (isalpha(c)) {
871 for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
872 if (p < &buff[sizeof buff - 1])
873 *p++ = c;
874 *p = '\0';
875 yyInput--;
876 return LookupWord(buff);
878 if (c != '(')
879 return *yyInput++;
880 Count = 0;
881 do {
882 c = *yyInput++;
883 if (c == '\0')
884 return c;
885 if (c == '(')
886 Count++;
887 else if (c == ')')
888 Count--;
889 } while (Count > 0);
894 #define TM_YEAR_ORIGIN 1900
896 /* Yield A - B, measured in seconds. */
897 static time_t
898 difftm(a, b)
899 struct tm *a, *b;
901 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
902 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
903 return
907 /* difference in day of year */
908 a->tm_yday - b->tm_yday
909 /* + intervening leap days */
910 + ((ay >> 2) - (by >> 2))
911 - (ay/100 - by/100)
912 + ((ay/100 >> 2) - (by/100 >> 2))
913 /* + difference in years * 365 */
914 + (time_t)(ay-by) * 365
915 )*24 + (a->tm_hour - b->tm_hour)
916 )*60 + (a->tm_min - b->tm_min)
917 )*60 + (a->tm_sec - b->tm_sec);
920 time_t
921 get_date(p, now)
922 char *p;
923 struct timeb *now;
925 struct tm *tm, gmt;
926 struct timeb ftz;
927 time_t Start;
928 time_t tod;
930 yyInput = p;
931 if (now == NULL) {
932 now = &ftz;
933 (void)time(&ftz.time);
935 if (! (tm = gmtime (&ftz.time)))
936 return -1;
937 gmt = *tm; /* Make a copy, in case localtime modifies *tm. */
938 ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60;
941 tm = localtime(&now->time);
942 yyYear = tm->tm_year;
943 yyMonth = tm->tm_mon + 1;
944 yyDay = tm->tm_mday;
945 yyTimezone = now->timezone;
946 yyDSTmode = DSTmaybe;
947 yyHour = 0;
948 yyMinutes = 0;
949 yySeconds = 0;
950 yyMeridian = MER24;
951 yyRelSeconds = 0;
952 yyRelMonth = 0;
953 yyHaveDate = 0;
954 yyHaveDay = 0;
955 yyHaveRel = 0;
956 yyHaveTime = 0;
957 yyHaveZone = 0;
959 if (yyparse()
960 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
961 return -1;
963 if (yyHaveDate || yyHaveTime || yyHaveDay) {
964 Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
965 yyMeridian, yyDSTmode);
966 if (Start < 0)
967 return -1;
969 else {
970 Start = now->time;
971 if (!yyHaveRel)
972 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
975 Start += yyRelSeconds;
976 Start += RelativeMonth(Start, yyRelMonth);
978 if (yyHaveDay && !yyHaveDate) {
979 tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
980 Start += tod;
983 /* Have to do *something* with a legitimate -1 so it's distinguishable
984 * from the error return value. (Alternately could set errno on error.) */
985 return Start == -1 ? 0 : Start;
989 #if defined(TEST)
991 /* ARGSUSED */
992 main(ac, av)
993 int ac;
994 char *av[];
996 char buff[128];
997 time_t d;
999 (void)printf("Enter date, or blank line to exit.\n\t> ");
1000 (void)fflush(stdout);
1001 while (gets(buff) && buff[0]) {
1002 d = get_date(buff, (struct timeb *)NULL);
1003 if (d == -1)
1004 (void)printf("Bad format - couldn't convert.\n");
1005 else
1006 (void)printf("%s", ctime(&d));
1007 (void)printf("\t> ");
1008 (void)fflush(stdout);
1010 exit(0);
1011 /* NOTREACHED */
1013 #endif /* defined(TEST) */