tools/llvm: Do not build with symbols
[minix3.git] / lib / libutil / parsedate.y
blob11af4ed375d132413f9baa0dee7a1e225fd4addd
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 #include <sys/cdefs.h>
16 #ifdef __RCSID
17 __RCSID("$NetBSD: parsedate.y,v 1.16 2013/06/12 01:46:07 yamt Exp $");
18 #endif
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <time.h>
25 #include <util.h>
26 #include <stdlib.h>
28 /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
29 releases):
31 We don't want to mess with all the portability hassles of alloca.
32 In particular, most (all?) versions of bison will use alloca in
33 their parser. If bison works on your system (e.g. it should work
34 with gcc), then go ahead and use it, but the more general solution
35 is to use byacc instead of bison, which should generate a portable
36 parser. I played with adding "#define alloca dont_use_alloca", to
37 give an error if the parser generator uses alloca (and thus detect
38 unportable parsedate.c's), but that seems to cause as many problems
39 as it solves. */
41 #define EPOCH 1970
42 #define HOUR(x) ((time_t)(x) * 60)
43 #define SECSPERDAY (24L * 60L * 60L)
47 ** An entry in the lexical lookup table.
49 typedef struct _TABLE {
50 const char *name;
51 int type;
52 time_t value;
53 } TABLE;
57 ** Daylight-savings mode: on, off, or not yet known.
59 typedef enum _DSTMODE {
60 DSTon, DSToff, DSTmaybe
61 } DSTMODE;
64 ** Meridian: am, pm, or 24-hour style.
66 typedef enum _MERIDIAN {
67 MERam, MERpm, MER24
68 } MERIDIAN;
71 struct dateinfo {
72 DSTMODE yyDSTmode;
73 time_t yyDayOrdinal;
74 time_t yyDayNumber;
75 int yyHaveDate;
76 int yyHaveDay;
77 int yyHaveRel;
78 int yyHaveTime;
79 int yyHaveZone;
80 time_t yyTimezone;
81 time_t yyDay;
82 time_t yyHour;
83 time_t yyMinutes;
84 time_t yyMonth;
85 time_t yySeconds;
86 time_t yyYear;
87 MERIDIAN yyMeridian;
88 time_t yyRelMonth;
89 time_t yyRelSeconds;
93 %union {
94 time_t Number;
95 enum _MERIDIAN Meridian;
98 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
99 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN
101 %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
102 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
103 %type <Meridian> tMERIDIAN o_merid
105 %parse-param { struct dateinfo *param }
106 %parse-param { const char **yyInput }
107 %lex-param { const char **yyInput }
108 %pure-parser
112 spec : /* NULL */
113 | spec item
116 item : time {
117 param->yyHaveTime++;
119 | zone {
120 param->yyHaveZone++;
122 | date {
123 param->yyHaveDate++;
125 | day {
126 param->yyHaveDay++;
128 | rel {
129 param->yyHaveRel++;
131 | cvsstamp {
132 param->yyHaveTime++;
133 param->yyHaveDate++;
134 param->yyHaveZone++;
136 | epochdate {
137 param->yyHaveTime++;
138 param->yyHaveDate++;
139 param->yyHaveZone++;
141 | number
144 cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
145 param->yyYear = $1;
146 if (param->yyYear < 100) param->yyYear += 1900;
147 param->yyMonth = $3;
148 param->yyDay = $5;
149 param->yyHour = $7;
150 param->yyMinutes = $9;
151 param->yySeconds = $11;
152 param->yyDSTmode = DSToff;
153 param->yyTimezone = 0;
157 epochdate: AT_SIGN at_number {
158 time_t when = $<Number>2;
159 struct tm tmbuf;
160 if (gmtime_r(&when, &tmbuf) != NULL) {
161 param->yyYear = tmbuf.tm_year + 1900;
162 param->yyMonth = tmbuf.tm_mon + 1;
163 param->yyDay = tmbuf.tm_mday;
165 param->yyHour = tmbuf.tm_hour;
166 param->yyMinutes = tmbuf.tm_min;
167 param->yySeconds = tmbuf.tm_sec;
168 } else {
169 param->yyYear = EPOCH;
170 param->yyMonth = 1;
171 param->yyDay = 1;
173 param->yyHour = 0;
174 param->yyMinutes = 0;
175 param->yySeconds = 0;
177 param->yyDSTmode = DSToff;
178 param->yyTimezone = 0;
182 at_number : tUNUMBER | tSNUMBER ;
184 time : tUNUMBER tMERIDIAN {
185 param->yyHour = $1;
186 param->yyMinutes = 0;
187 param->yySeconds = 0;
188 param->yyMeridian = $2;
190 | tUNUMBER ':' tUNUMBER o_merid {
191 param->yyHour = $1;
192 param->yyMinutes = $3;
193 param->yySeconds = 0;
194 param->yyMeridian = $4;
196 | tUNUMBER ':' tUNUMBER tSNUMBER {
197 param->yyHour = $1;
198 param->yyMinutes = $3;
199 param->yyMeridian = MER24;
200 param->yyDSTmode = DSToff;
201 param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
203 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
204 param->yyHour = $1;
205 param->yyMinutes = $3;
206 param->yySeconds = $5;
207 param->yyMeridian = $6;
209 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
210 param->yyHour = $1;
211 param->yyMinutes = $3;
212 param->yySeconds = $5;
213 param->yyMeridian = MER24;
214 param->yyDSTmode = DSToff;
215 param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
217 | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
218 param->yyHour = $1;
219 param->yyMinutes = $3;
220 param->yySeconds = $5;
221 param->yyMeridian = MER24;
222 param->yyDSTmode = DSToff;
223 /* XXX: Do nothing with millis */
224 /* param->yyTimezone = ($7 % 100 + ($7 / 100) * 60); */
228 zone : tZONE {
229 param->yyTimezone = $1;
230 param->yyDSTmode = DSToff;
232 | tDAYZONE {
233 param->yyTimezone = $1;
234 param->yyDSTmode = DSTon;
237 tZONE tDST {
238 param->yyTimezone = $1;
239 param->yyDSTmode = DSTon;
243 day : tDAY {
244 param->yyDayOrdinal = 1;
245 param->yyDayNumber = $1;
247 | tDAY ',' {
248 param->yyDayOrdinal = 1;
249 param->yyDayNumber = $1;
251 | tUNUMBER tDAY {
252 param->yyDayOrdinal = $1;
253 param->yyDayNumber = $2;
257 date : tUNUMBER '/' tUNUMBER {
258 param->yyMonth = $1;
259 param->yyDay = $3;
261 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
262 if ($1 >= 100) {
263 param->yyYear = $1;
264 param->yyMonth = $3;
265 param->yyDay = $5;
266 } else {
267 param->yyMonth = $1;
268 param->yyDay = $3;
269 param->yyYear = $5;
272 | tUNUMBER tSNUMBER tSNUMBER {
273 /* ISO 8601 format. yyyy-mm-dd. */
274 param->yyYear = $1;
275 param->yyMonth = -$2;
276 param->yyDay = -$3;
278 | tUNUMBER tMONTH tSNUMBER {
279 /* e.g. 17-JUN-1992. */
280 param->yyDay = $1;
281 param->yyMonth = $2;
282 param->yyYear = -$3;
284 | tMONTH tUNUMBER {
285 param->yyMonth = $1;
286 param->yyDay = $2;
288 | tMONTH tUNUMBER ',' tUNUMBER {
289 param->yyMonth = $1;
290 param->yyDay = $2;
291 param->yyYear = $4;
293 | tUNUMBER tMONTH {
294 param->yyMonth = $2;
295 param->yyDay = $1;
297 | tUNUMBER tMONTH tUNUMBER {
298 param->yyMonth = $2;
299 param->yyDay = $1;
300 param->yyYear = $3;
304 rel : relunit tAGO {
305 param->yyRelSeconds = -param->yyRelSeconds;
306 param->yyRelMonth = -param->yyRelMonth;
308 | relunit
311 relunit : tUNUMBER tMINUTE_UNIT {
312 param->yyRelSeconds += $1 * $2 * 60L;
314 | tSNUMBER tMINUTE_UNIT {
315 param->yyRelSeconds += $1 * $2 * 60L;
317 | tMINUTE_UNIT {
318 param->yyRelSeconds += $1 * 60L;
320 | tSNUMBER tSEC_UNIT {
321 param->yyRelSeconds += $1;
323 | tUNUMBER tSEC_UNIT {
324 param->yyRelSeconds += $1;
326 | tSEC_UNIT {
327 param->yyRelSeconds++;
329 | tSNUMBER tMONTH_UNIT {
330 param->yyRelMonth += $1 * $2;
332 | tUNUMBER tMONTH_UNIT {
333 param->yyRelMonth += $1 * $2;
335 | tMONTH_UNIT {
336 param->yyRelMonth += $1;
340 number : tUNUMBER {
341 if (param->yyHaveTime && param->yyHaveDate && !param->yyHaveRel)
342 param->yyYear = $1;
343 else {
344 if($1>10000) {
345 param->yyHaveDate++;
346 param->yyDay= ($1)%100;
347 param->yyMonth= ($1/100)%100;
348 param->yyYear = $1/10000;
350 else {
351 param->yyHaveTime++;
352 if ($1 < 100) {
353 param->yyHour = $1;
354 param->yyMinutes = 0;
356 else {
357 param->yyHour = $1 / 100;
358 param->yyMinutes = $1 % 100;
360 param->yySeconds = 0;
361 param->yyMeridian = MER24;
367 o_merid : /* NULL */ {
368 $$ = MER24;
370 | tMERIDIAN {
371 $$ = $1;
377 /* Month and day table. */
378 static const TABLE MonthDayTable[] = {
379 { "january", tMONTH, 1 },
380 { "february", tMONTH, 2 },
381 { "march", tMONTH, 3 },
382 { "april", tMONTH, 4 },
383 { "may", tMONTH, 5 },
384 { "june", tMONTH, 6 },
385 { "july", tMONTH, 7 },
386 { "august", tMONTH, 8 },
387 { "september", tMONTH, 9 },
388 { "sept", tMONTH, 9 },
389 { "october", tMONTH, 10 },
390 { "november", tMONTH, 11 },
391 { "december", tMONTH, 12 },
392 { "sunday", tDAY, 0 },
393 { "monday", tDAY, 1 },
394 { "tuesday", tDAY, 2 },
395 { "tues", tDAY, 2 },
396 { "wednesday", tDAY, 3 },
397 { "wednes", tDAY, 3 },
398 { "thursday", tDAY, 4 },
399 { "thur", tDAY, 4 },
400 { "thurs", tDAY, 4 },
401 { "friday", tDAY, 5 },
402 { "saturday", tDAY, 6 },
403 { NULL, 0, 0 }
406 /* Time units table. */
407 static const TABLE UnitsTable[] = {
408 { "year", tMONTH_UNIT, 12 },
409 { "month", tMONTH_UNIT, 1 },
410 { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
411 { "week", tMINUTE_UNIT, 7 * 24 * 60 },
412 { "day", tMINUTE_UNIT, 1 * 24 * 60 },
413 { "hour", tMINUTE_UNIT, 60 },
414 { "minute", tMINUTE_UNIT, 1 },
415 { "min", tMINUTE_UNIT, 1 },
416 { "second", tSEC_UNIT, 1 },
417 { "sec", tSEC_UNIT, 1 },
418 { NULL, 0, 0 }
421 /* Assorted relative-time words. */
422 static const TABLE OtherTable[] = {
423 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
424 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
425 { "today", tMINUTE_UNIT, 0 },
426 { "now", tMINUTE_UNIT, 0 },
427 { "last", tUNUMBER, -1 },
428 { "this", tMINUTE_UNIT, 0 },
429 { "next", tUNUMBER, 2 },
430 { "first", tUNUMBER, 1 },
431 { "one", tUNUMBER, 1 },
432 /* { "second", tUNUMBER, 2 }, */
433 { "two", tUNUMBER, 2 },
434 { "third", tUNUMBER, 3 },
435 { "three", tUNUMBER, 3 },
436 { "fourth", tUNUMBER, 4 },
437 { "four", tUNUMBER, 4 },
438 { "fifth", tUNUMBER, 5 },
439 { "five", tUNUMBER, 5 },
440 { "sixth", tUNUMBER, 6 },
441 { "six", tUNUMBER, 6 },
442 { "seventh", tUNUMBER, 7 },
443 { "seven", tUNUMBER, 7 },
444 { "eighth", tUNUMBER, 8 },
445 { "eight", tUNUMBER, 8 },
446 { "ninth", tUNUMBER, 9 },
447 { "nine", tUNUMBER, 9 },
448 { "tenth", tUNUMBER, 10 },
449 { "ten", tUNUMBER, 10 },
450 { "eleventh", tUNUMBER, 11 },
451 { "eleven", tUNUMBER, 11 },
452 { "twelfth", tUNUMBER, 12 },
453 { "twelve", tUNUMBER, 12 },
454 { "ago", tAGO, 1 },
455 { NULL, 0, 0 }
458 /* The timezone table. */
459 /* Some of these are commented out because a time_t can't store a float. */
460 static const TABLE TimezoneTable[] = {
461 { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
462 { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
463 { "utc", tZONE, HOUR( 0) },
464 { "wet", tZONE, HOUR( 0) }, /* Western European */
465 { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
466 { "wat", tZONE, HOUR( 1) }, /* West Africa */
467 { "at", tZONE, HOUR( 2) }, /* Azores */
468 #if 0
469 /* For completeness. BST is also British Summer, and GST is
470 * also Guam Standard. */
471 { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
472 { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
473 #endif
474 #if 0
475 { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
476 { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
477 { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
478 #endif
479 { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
480 { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
481 { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
482 { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
483 { "cst", tZONE, HOUR( 6) }, /* Central Standard */
484 { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
485 { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
486 { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
487 { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
488 { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
489 { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
490 { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
491 { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
492 { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
493 { "cat", tZONE, HOUR(10) }, /* Central Alaska */
494 { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
495 { "nt", tZONE, HOUR(11) }, /* Nome */
496 { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
497 { "cet", tZONE, -HOUR(1) }, /* Central European */
498 { "met", tZONE, -HOUR(1) }, /* Middle European */
499 { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
500 { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
501 { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
502 { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
503 { "fwt", tZONE, -HOUR(1) }, /* French Winter */
504 { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
505 { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
506 { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
507 #if 0
508 { "it", tZONE, -HOUR(3.5) },/* Iran */
509 #endif
510 { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
511 { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
512 #if 0
513 { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
514 #endif
515 { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
516 #if 0
517 /* For completeness. NST is also Newfoundland Stanard, and SST is
518 * also Swedish Summer. */
519 { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
520 { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
521 #endif /* 0 */
522 { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
523 { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
524 #if 0
525 { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
526 #endif
527 { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
528 { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
529 #if 0
530 { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
531 { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
532 #endif
533 { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
534 { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
535 { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
536 { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
537 { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
538 { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
539 { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
540 { NULL, 0, 0 }
543 /* Military timezone table. */
544 static const TABLE MilitaryTable[] = {
545 { "a", tZONE, HOUR( 1) },
546 { "b", tZONE, HOUR( 2) },
547 { "c", tZONE, HOUR( 3) },
548 { "d", tZONE, HOUR( 4) },
549 { "e", tZONE, HOUR( 5) },
550 { "f", tZONE, HOUR( 6) },
551 { "g", tZONE, HOUR( 7) },
552 { "h", tZONE, HOUR( 8) },
553 { "i", tZONE, HOUR( 9) },
554 { "k", tZONE, HOUR( 10) },
555 { "l", tZONE, HOUR( 11) },
556 { "m", tZONE, HOUR( 12) },
557 { "n", tZONE, HOUR(- 1) },
558 { "o", tZONE, HOUR(- 2) },
559 { "p", tZONE, HOUR(- 3) },
560 { "q", tZONE, HOUR(- 4) },
561 { "r", tZONE, HOUR(- 5) },
562 { "s", tZONE, HOUR(- 6) },
563 { "t", tZONE, HOUR(- 7) },
564 { "u", tZONE, HOUR(- 8) },
565 { "v", tZONE, HOUR(- 9) },
566 { "w", tZONE, HOUR(-10) },
567 { "x", tZONE, HOUR(-11) },
568 { "y", tZONE, HOUR(-12) },
569 { "z", tZONE, HOUR( 0) },
570 { NULL, 0, 0 }
576 /* ARGSUSED */
577 static int
578 yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
580 return 0;
584 /* Year is either
585 * A negative number, which means to use its absolute value (why?)
586 * A number from 0 to 99, which means a year from 1900 to 1999, or
587 * The actual year (>=100). */
588 static time_t
589 Convert(
590 time_t Month, /* month of year [1-12] */
591 time_t Day, /* day of month [1-31] */
592 time_t Year, /* year; see above comment */
593 time_t Hours, /* Hour of day [0-24] */
594 time_t Minutes, /* Minute of hour [0-59] */
595 time_t Seconds, /* Second of minute [0-60] */
596 time_t Timezone, /* Timezone as minutes east of UTC */
597 MERIDIAN Meridian, /* Hours are am/pm/24 hour clock */
598 DSTMODE DSTmode /* DST on/off/maybe */
601 struct tm tm = {.tm_sec = 0};
602 time_t result;
604 /* XXX Y2K */
605 if (Year < 0)
606 Year = -Year;
607 if (Year < 70)
608 Year += 2000;
609 else if (Year < 100)
610 Year += 1900;
612 tm.tm_sec = Seconds;
613 tm.tm_min = Minutes;
614 tm.tm_hour = Hours + (Meridian == MERpm ? 12 : 0);
615 tm.tm_mday = Day;
616 tm.tm_mon = Month - 1;
617 tm.tm_year = Year - 1900;
618 switch (DSTmode) {
619 case DSTon: tm.tm_isdst = 1; break;
620 case DSToff: tm.tm_isdst = 0; break;
621 default: tm.tm_isdst = -1; break;
624 /* We rely on mktime_z(NULL, ...) working in UTC, not in local time. */
625 result = mktime_z(NULL, &tm);
626 result += Timezone * 60;
627 return result;
631 static time_t
632 DSTcorrect(
633 time_t Start,
634 time_t Future
637 time_t StartDay;
638 time_t FutureDay;
639 struct tm *tm;
641 if ((tm = localtime(&Start)) == NULL)
642 return -1;
643 StartDay = (tm->tm_hour + 1) % 24;
645 if ((tm = localtime(&Future)) == NULL)
646 return -1;
647 FutureDay = (tm->tm_hour + 1) % 24;
649 return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
653 static time_t
654 RelativeDate(
655 time_t Start,
656 time_t DayOrdinal,
657 time_t DayNumber
660 struct tm *tm;
661 time_t now;
663 now = Start;
664 tm = localtime(&now);
665 now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
666 now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
667 return DSTcorrect(Start, now);
671 static time_t
672 RelativeMonth(
673 time_t Start,
674 time_t RelMonth,
675 time_t Timezone
678 struct tm *tm;
679 time_t Month;
680 time_t Year;
682 if (RelMonth == 0)
683 return 0;
684 tm = localtime(&Start);
685 if (tm == NULL)
686 return -1;
687 Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
688 Year = Month / 12;
689 Month = Month % 12 + 1;
690 return DSTcorrect(Start,
691 Convert(Month, (time_t)tm->tm_mday, Year,
692 (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
693 Timezone, MER24, DSTmaybe));
697 static int
698 LookupWord(YYSTYPE *yylval, char *buff)
700 register char *p;
701 register char *q;
702 register const TABLE *tp;
703 int i;
704 int abbrev;
706 /* Make it lowercase. */
707 for (p = buff; *p; p++)
708 if (isupper((unsigned char)*p))
709 *p = tolower((unsigned char)*p);
711 if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
712 yylval->Meridian = MERam;
713 return tMERIDIAN;
715 if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
716 yylval->Meridian = MERpm;
717 return tMERIDIAN;
720 /* See if we have an abbreviation for a month. */
721 if (strlen(buff) == 3)
722 abbrev = 1;
723 else if (strlen(buff) == 4 && buff[3] == '.') {
724 abbrev = 1;
725 buff[3] = '\0';
727 else
728 abbrev = 0;
730 for (tp = MonthDayTable; tp->name; tp++) {
731 if (abbrev) {
732 if (strncmp(buff, tp->name, 3) == 0) {
733 yylval->Number = tp->value;
734 return tp->type;
737 else if (strcmp(buff, tp->name) == 0) {
738 yylval->Number = tp->value;
739 return tp->type;
743 for (tp = TimezoneTable; tp->name; tp++)
744 if (strcmp(buff, tp->name) == 0) {
745 yylval->Number = tp->value;
746 return tp->type;
749 if (strcmp(buff, "dst") == 0)
750 return tDST;
752 for (tp = UnitsTable; tp->name; tp++)
753 if (strcmp(buff, tp->name) == 0) {
754 yylval->Number = tp->value;
755 return tp->type;
758 /* Strip off any plural and try the units table again. */
759 i = strlen(buff) - 1;
760 if (buff[i] == 's') {
761 buff[i] = '\0';
762 for (tp = UnitsTable; tp->name; tp++)
763 if (strcmp(buff, tp->name) == 0) {
764 yylval->Number = tp->value;
765 return tp->type;
767 buff[i] = 's'; /* Put back for "this" in OtherTable. */
770 for (tp = OtherTable; tp->name; tp++)
771 if (strcmp(buff, tp->name) == 0) {
772 yylval->Number = tp->value;
773 return tp->type;
776 /* Military timezones. */
777 if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
778 for (tp = MilitaryTable; tp->name; tp++)
779 if (strcmp(buff, tp->name) == 0) {
780 yylval->Number = tp->value;
781 return tp->type;
785 /* Drop out any periods and try the timezone table again. */
786 for (i = 0, p = q = buff; *q; q++)
787 if (*q != '.')
788 *p++ = *q;
789 else
790 i++;
791 *p = '\0';
792 if (i)
793 for (tp = TimezoneTable; tp->name; tp++)
794 if (strcmp(buff, tp->name) == 0) {
795 yylval->Number = tp->value;
796 return tp->type;
799 return tID;
803 static int
804 yylex(YYSTYPE *yylval, const char **yyInput)
806 register char c;
807 register char *p;
808 char buff[20];
809 int Count;
810 int sign;
811 const char *inp = *yyInput;
813 for ( ; ; ) {
814 while (isspace((unsigned char)*inp))
815 inp++;
817 if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') {
818 if (c == '-' || c == '+') {
819 sign = c == '-' ? -1 : 1;
820 if (!isdigit((unsigned char)*++inp))
821 /* skip the '-' sign */
822 continue;
824 else
825 sign = 0;
826 for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); )
827 yylval->Number = 10 * yylval->Number + c - '0';
828 if (sign < 0)
829 yylval->Number = -yylval->Number;
830 *yyInput = --inp;
831 return sign ? tSNUMBER : tUNUMBER;
833 if (isalpha((unsigned char)c)) {
834 for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; )
835 if (p < &buff[sizeof buff - 1])
836 *p++ = c;
837 *p = '\0';
838 *yyInput = --inp;
839 return LookupWord(yylval, buff);
841 if (c == '@') {
842 *yyInput = ++inp;
843 return AT_SIGN;
845 if (c != '(') {
846 *yyInput = ++inp;
847 return c;
849 Count = 0;
850 do {
851 c = *inp++;
852 if (c == '\0')
853 return c;
854 if (c == '(')
855 Count++;
856 else if (c == ')')
857 Count--;
858 } while (Count > 0);
862 #define TM_YEAR_ORIGIN 1900
864 /* Yield A - B, measured in seconds. */
865 static time_t
866 difftm (struct tm *a, struct tm *b)
868 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
869 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
870 int days = (
871 /* difference in day of year */
872 a->tm_yday - b->tm_yday
873 /* + intervening leap days */
874 + ((ay >> 2) - (by >> 2))
875 - (ay/100 - by/100)
876 + ((ay/100 >> 2) - (by/100 >> 2))
877 /* + difference in years * 365 */
878 + (long)(ay-by) * 365
880 return ((time_t)60*(60*(24*days + (a->tm_hour - b->tm_hour))
881 + (a->tm_min - b->tm_min))
882 + (a->tm_sec - b->tm_sec));
885 time_t
886 parsedate(const char *p, const time_t *now, const int *zone)
888 struct tm gmt, local, *gmt_ptr, *tm;
889 time_t nowt;
890 int zonet;
891 time_t Start;
892 time_t tod, rm;
893 struct dateinfo param;
894 int saved_errno;
896 saved_errno = errno;
897 errno = 0;
899 if (now == NULL || zone == NULL) {
900 now = &nowt;
901 zone = &zonet;
902 (void)time(&nowt);
904 gmt_ptr = gmtime_r(now, &gmt);
905 if ((tm = localtime_r(now, &local)) == NULL)
906 return -1;
908 if (gmt_ptr != NULL)
909 zonet = difftm(&gmt, &local) / 60;
910 else
911 /* We are on a system like VMS, where the system clock is
912 in local time and the system has no concept of timezones.
913 Hopefully we can fake this out (for the case in which the
914 user specifies no timezone) by just saying the timezone
915 is zero. */
916 zonet = 0;
918 if (local.tm_isdst)
919 zonet += 60;
920 } else {
921 if ((tm = localtime_r(now, &local)) == NULL)
922 return -1;
924 param.yyYear = tm->tm_year + 1900;
925 param.yyMonth = tm->tm_mon + 1;
926 param.yyDay = tm->tm_mday;
927 param.yyTimezone = *zone;
928 param.yyDSTmode = DSTmaybe;
929 param.yyHour = 0;
930 param.yyMinutes = 0;
931 param.yySeconds = 0;
932 param.yyMeridian = MER24;
933 param.yyRelSeconds = 0;
934 param.yyRelMonth = 0;
935 param.yyHaveDate = 0;
936 param.yyHaveDay = 0;
937 param.yyHaveRel = 0;
938 param.yyHaveTime = 0;
939 param.yyHaveZone = 0;
941 if (yyparse(&param, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 ||
942 param.yyHaveDate > 1 || param.yyHaveDay > 1) {
943 errno = EINVAL;
944 return -1;
947 if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) {
948 Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
949 param.yyMinutes, param.yySeconds, param.yyTimezone,
950 param.yyMeridian, param.yyDSTmode);
951 if (Start == -1 && errno != 0)
952 return -1;
954 else {
955 Start = *now;
956 if (!param.yyHaveRel)
957 Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
960 Start += param.yyRelSeconds;
961 rm = RelativeMonth(Start, param.yyRelMonth, param.yyTimezone);
962 if (rm == -1 && errno != 0)
963 return -1;
964 Start += rm;
966 if (param.yyHaveDay && !param.yyHaveDate) {
967 tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber);
968 Start += tod;
971 if (errno == 0)
972 errno = saved_errno;
973 return Start;
977 #if defined(TEST)
979 /* ARGSUSED */
981 main(int ac, char *av[])
983 char buff[128];
984 time_t d;
986 (void)printf("Enter date, or blank line to exit.\n\t> ");
987 (void)fflush(stdout);
988 while (fgets(buff, sizeof(buff), stdin) && buff[0] != '\n') {
989 errno = 0;
990 d = parsedate(buff, NULL, NULL);
991 if (d == -1 && errno != 0)
992 (void)printf("Bad format - couldn't convert: %s\n",
993 strerror(errno));
994 else
995 (void)printf("%jd\t%s", (intmax_t)d, ctime(&d));
996 (void)printf("\t> ");
997 (void)fflush(stdout);
999 exit(0);
1000 /* NOTREACHED */
1002 #endif /* defined(TEST) */