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;
8 ** This grammar has 10 shift/reduce conflicts.
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>
17 __RCSID
("$NetBSD: parsedate.y,v 1.20 2014/10/08 17:38:28 apb Exp $");
28 /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
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
42 #define HOUR(x) ((time_t)(x) * 60)
43 #define SECSPERDAY (24L * 60L * 60L)
45 #define USE_LOCAL_TIME 99999 /* special case for Convert() and yyTimezone */
48 ** An entry in the lexical lookup table.
50 typedef
struct _TABLE
{
58 ** Daylight-savings mode: on, off, or not yet known.
60 typedef
enum _DSTMODE
{
61 DSTon
, DSToff
, DSTmaybe
65 ** Meridian: am, pm, or 24-hour style.
67 typedef
enum _MERIDIAN
{
73 DSTMODE yyDSTmode
; /* DST on/off/maybe */
77 int yyHaveFullYear
; /* if true, year is not abbreviated. */
78 /* if false, need to call AdjustYear(). */
83 time_t yyTimezone
; /* Timezone as minutes ahead/east of UTC */
84 time_t yyDay
; /* Day of month [1-31] */
85 time_t yyHour
; /* Hour of day [0-24] or [1-12] */
86 time_t yyMinutes
; /* Minute of hour [0-59] */
87 time_t yyMonth
; /* Month of year [1-12] */
88 time_t yySeconds
; /* Second of minute [0-60] */
89 time_t yyYear
; /* Year, see also yyHaveFullYear */
90 MERIDIAN yyMeridian
; /* Interpret yyHour as AM/PM/24 hour clock */
98 enum _MERIDIAN Meridian
;
101 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
102 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN
104 %type
<Number
> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
105 %type
<Number
> tSEC_UNIT tSNUMBER tUNUMBER tZONE
106 %type
<Meridian
> tMERIDIAN o_merid
108 %parse
-param
{ struct dateinfo
*param
}
109 %parse
-param
{ const char **yyInput
}
110 %lex
-param
{ const char **yyInput
}
151 cvsstamp: tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
{
153 if
(param
->yyYear
< 100) param
->yyYear
+= 1900;
154 param
->yyHaveFullYear
= 1;
158 param
->yyMinutes
= $9;
159 param
->yySeconds
= $11;
160 param
->yyDSTmode
= DSToff
;
161 param
->yyTimezone
= 0;
165 epochdate: AT_SIGN at_number
{
166 time_t when
= $
<Number
>2;
168 if
(gmtime_r
(&when
, &tmbuf
) != NULL
) {
169 param
->yyYear
= tmbuf.tm_year
+ 1900;
170 param
->yyMonth
= tmbuf.tm_mon
+ 1;
171 param
->yyDay
= tmbuf.tm_mday
;
173 param
->yyHour
= tmbuf.tm_hour
;
174 param
->yyMinutes
= tmbuf.tm_min
;
175 param
->yySeconds
= tmbuf.tm_sec
;
177 param
->yyYear
= EPOCH
;
182 param
->yyMinutes
= 0;
183 param
->yySeconds
= 0;
185 param
->yyHaveFullYear
= 1;
186 param
->yyDSTmode
= DSToff
;
187 param
->yyTimezone
= 0;
191 at_number
: tUNUMBER | tSNUMBER
;
193 time
: tUNUMBER tMERIDIAN
{
195 param
->yyMinutes
= 0;
196 param
->yySeconds
= 0;
197 param
->yyMeridian
= $2;
199 | tUNUMBER
':' tUNUMBER o_merid
{
201 param
->yyMinutes
= $3;
202 param
->yySeconds
= 0;
203 param
->yyMeridian
= $4;
205 | tUNUMBER
':' tUNUMBER
':' tUNUMBER o_merid
{
207 param
->yyMinutes
= $3;
208 param
->yySeconds
= $5;
209 param
->yyMeridian
= $6;
211 | tUNUMBER
':' tUNUMBER
':' tUNUMBER
'.' tUNUMBER
{
213 param
->yyMinutes
= $3;
214 param
->yySeconds
= $5;
215 param
->yyMeridian
= MER24
;
216 /* XXX: Do nothing with millis */
220 time_numericzone
: tUNUMBER
':' tUNUMBER tSNUMBER
{
222 param
->yyMinutes
= $3;
223 param
->yyMeridian
= MER24
;
224 param
->yyDSTmode
= DSToff
;
225 param
->yyTimezone
= - ($4 %
100 + ($4 / 100) * 60);
227 | tUNUMBER
':' tUNUMBER
':' tUNUMBER tSNUMBER
{
229 param
->yyMinutes
= $3;
230 param
->yySeconds
= $5;
231 param
->yyMeridian
= MER24
;
232 param
->yyDSTmode
= DSToff
;
233 param
->yyTimezone
= - ($6 %
100 + ($6 / 100) * 60);
238 param
->yyTimezone
= $1;
239 param
->yyDSTmode
= DSToff
;
242 param
->yyTimezone
= $1;
243 param
->yyDSTmode
= DSTon
;
247 param
->yyTimezone
= $1;
248 param
->yyDSTmode
= DSTon
;
253 param
->yyDayOrdinal
= 1;
254 param
->yyDayNumber
= $1;
257 param
->yyDayOrdinal
= 1;
258 param
->yyDayNumber
= $1;
261 param
->yyDayOrdinal
= $1;
262 param
->yyDayNumber
= $2;
266 date
: tUNUMBER
'/' tUNUMBER
{
270 | tUNUMBER
'/' tUNUMBER
'/' tUNUMBER
{
281 | tUNUMBER tSNUMBER tSNUMBER
{
282 /* ISO 8601 format. yyyy-mm-dd. */
284 param
->yyHaveFullYear
= 1;
285 param
->yyMonth
= -$2;
288 | tUNUMBER tMONTH tSNUMBER
{
289 /* e.g. 17-JUN-1992. */
298 | tMONTH tUNUMBER
',' tUNUMBER
{
307 | tUNUMBER tMONTH tUNUMBER
{
315 param
->yyRelSeconds
= -param
->yyRelSeconds
;
316 param
->yyRelMonth
= -param
->yyRelMonth
;
321 relunit
: tUNUMBER tMINUTE_UNIT
{
322 param
->yyRelSeconds
+= $1 * $2 * 60L;
324 | tSNUMBER tMINUTE_UNIT
{
325 param
->yyRelSeconds
+= $1 * $2 * 60L;
328 param
->yyRelSeconds
+= $1 * 60L;
330 | tSNUMBER tSEC_UNIT
{
331 param
->yyRelSeconds
+= $1;
333 | tUNUMBER tSEC_UNIT
{
334 param
->yyRelSeconds
+= $1;
337 param
->yyRelSeconds
++;
339 | tSNUMBER tMONTH_UNIT
{
340 param
->yyRelMonth
+= $1 * $2;
342 | tUNUMBER tMONTH_UNIT
{
343 param
->yyRelMonth
+= $1 * $2;
346 param
->yyRelMonth
+= $1;
351 if
(param
->yyHaveTime
&& param
->yyHaveDate
&& !param
->yyHaveRel
)
356 param
->yyDay
= ($1)%
100;
357 param
->yyMonth
= ($1/100)%
100;
358 param
->yyYear
= $1/10000;
364 param
->yyMinutes
= 0;
367 param
->yyHour
= $1 / 100;
368 param
->yyMinutes
= $1 %
100;
370 param
->yySeconds
= 0;
371 param
->yyMeridian
= MER24
;
377 o_merid
: /* NULL */ {
387 /* Month and day table. */
388 static const TABLE MonthDayTable
[] = {
389 { "january", tMONTH
, 1 },
390 { "february", tMONTH
, 2 },
391 { "march", tMONTH
, 3 },
392 { "april", tMONTH
, 4 },
393 { "may", tMONTH
, 5 },
394 { "june", tMONTH
, 6 },
395 { "july", tMONTH
, 7 },
396 { "august", tMONTH
, 8 },
397 { "september", tMONTH
, 9 },
398 { "sept", tMONTH
, 9 },
399 { "october", tMONTH
, 10 },
400 { "november", tMONTH
, 11 },
401 { "december", tMONTH
, 12 },
402 { "sunday", tDAY
, 0 },
403 { "monday", tDAY
, 1 },
404 { "tuesday", tDAY
, 2 },
406 { "wednesday", tDAY
, 3 },
407 { "wednes", tDAY
, 3 },
408 { "thursday", tDAY
, 4 },
410 { "thurs", tDAY
, 4 },
411 { "friday", tDAY
, 5 },
412 { "saturday", tDAY
, 6 },
416 /* Time units table. */
417 static const TABLE UnitsTable
[] = {
418 { "year", tMONTH_UNIT
, 12 },
419 { "month", tMONTH_UNIT
, 1 },
420 { "fortnight", tMINUTE_UNIT
, 14 * 24 * 60 },
421 { "week", tMINUTE_UNIT
, 7 * 24 * 60 },
422 { "day", tMINUTE_UNIT
, 1 * 24 * 60 },
423 { "hour", tMINUTE_UNIT
, 60 },
424 { "minute", tMINUTE_UNIT
, 1 },
425 { "min", tMINUTE_UNIT
, 1 },
426 { "second", tSEC_UNIT
, 1 },
427 { "sec", tSEC_UNIT
, 1 },
431 /* Assorted relative-time words. */
432 static const TABLE OtherTable
[] = {
433 { "tomorrow", tMINUTE_UNIT
, 1 * 24 * 60 },
434 { "yesterday", tMINUTE_UNIT
, -1 * 24 * 60 },
435 { "today", tMINUTE_UNIT
, 0 },
436 { "now", tMINUTE_UNIT
, 0 },
437 { "last", tUNUMBER
, -1 },
438 { "this", tMINUTE_UNIT
, 0 },
439 { "next", tUNUMBER
, 2 },
440 { "first", tUNUMBER
, 1 },
441 { "one", tUNUMBER
, 1 },
442 /* { "second", tUNUMBER, 2 }, */
443 { "two", tUNUMBER
, 2 },
444 { "third", tUNUMBER
, 3 },
445 { "three", tUNUMBER
, 3 },
446 { "fourth", tUNUMBER
, 4 },
447 { "four", tUNUMBER
, 4 },
448 { "fifth", tUNUMBER
, 5 },
449 { "five", tUNUMBER
, 5 },
450 { "sixth", tUNUMBER
, 6 },
451 { "six", tUNUMBER
, 6 },
452 { "seventh", tUNUMBER
, 7 },
453 { "seven", tUNUMBER
, 7 },
454 { "eighth", tUNUMBER
, 8 },
455 { "eight", tUNUMBER
, 8 },
456 { "ninth", tUNUMBER
, 9 },
457 { "nine", tUNUMBER
, 9 },
458 { "tenth", tUNUMBER
, 10 },
459 { "ten", tUNUMBER
, 10 },
460 { "eleventh", tUNUMBER
, 11 },
461 { "eleven", tUNUMBER
, 11 },
462 { "twelfth", tUNUMBER
, 12 },
463 { "twelve", tUNUMBER
, 12 },
468 /* The timezone table. */
469 /* Some of these are commented out because a time_t can't store a float. */
470 static const TABLE TimezoneTable
[] = {
471 { "gmt", tZONE
, HOUR
( 0) }, /* Greenwich Mean */
472 { "ut", tZONE
, HOUR
( 0) }, /* Universal (Coordinated) */
473 { "utc", tZONE
, HOUR
( 0) },
474 { "wet", tZONE
, HOUR
( 0) }, /* Western European */
475 { "bst", tDAYZONE
, HOUR
( 0) }, /* British Summer */
476 { "wat", tZONE
, HOUR
( 1) }, /* West Africa */
477 { "at", tZONE
, HOUR
( 2) }, /* Azores */
479 /* For completeness. BST is also British Summer, and GST is
480 * also Guam Standard. */
481 { "bst", tZONE
, HOUR
( 3) }, /* Brazil Standard */
482 { "gst", tZONE
, HOUR
( 3) }, /* Greenland Standard */
485 { "nft", tZONE
, HOUR
(3.5) }, /* Newfoundland */
486 { "nst", tZONE
, HOUR
(3.5) }, /* Newfoundland Standard */
487 { "ndt", tDAYZONE
, HOUR
(3.5) }, /* Newfoundland Daylight */
489 { "ast", tZONE
, HOUR
( 4) }, /* Atlantic Standard */
490 { "adt", tDAYZONE
, HOUR
( 4) }, /* Atlantic Daylight */
491 { "est", tZONE
, HOUR
( 5) }, /* Eastern Standard */
492 { "edt", tDAYZONE
, HOUR
( 5) }, /* Eastern Daylight */
493 { "cst", tZONE
, HOUR
( 6) }, /* Central Standard */
494 { "cdt", tDAYZONE
, HOUR
( 6) }, /* Central Daylight */
495 { "mst", tZONE
, HOUR
( 7) }, /* Mountain Standard */
496 { "mdt", tDAYZONE
, HOUR
( 7) }, /* Mountain Daylight */
497 { "pst", tZONE
, HOUR
( 8) }, /* Pacific Standard */
498 { "pdt", tDAYZONE
, HOUR
( 8) }, /* Pacific Daylight */
499 { "yst", tZONE
, HOUR
( 9) }, /* Yukon Standard */
500 { "ydt", tDAYZONE
, HOUR
( 9) }, /* Yukon Daylight */
501 { "hst", tZONE
, HOUR
(10) }, /* Hawaii Standard */
502 { "hdt", tDAYZONE
, HOUR
(10) }, /* Hawaii Daylight */
503 { "cat", tZONE
, HOUR
(10) }, /* Central Alaska */
504 { "ahst", tZONE
, HOUR
(10) }, /* Alaska-Hawaii Standard */
505 { "nt", tZONE
, HOUR
(11) }, /* Nome */
506 { "idlw", tZONE
, HOUR
(12) }, /* International Date Line West */
507 { "cet", tZONE
, -HOUR
(1) }, /* Central European */
508 { "met", tZONE
, -HOUR
(1) }, /* Middle European */
509 { "mewt", tZONE
, -HOUR
(1) }, /* Middle European Winter */
510 { "mest", tDAYZONE
, -HOUR
(1) }, /* Middle European Summer */
511 { "swt", tZONE
, -HOUR
(1) }, /* Swedish Winter */
512 { "sst", tDAYZONE
, -HOUR
(1) }, /* Swedish Summer */
513 { "fwt", tZONE
, -HOUR
(1) }, /* French Winter */
514 { "fst", tDAYZONE
, -HOUR
(1) }, /* French Summer */
515 { "eet", tZONE
, -HOUR
(2) }, /* Eastern Europe, USSR Zone 1 */
516 { "bt", tZONE
, -HOUR
(3) }, /* Baghdad, USSR Zone 2 */
518 { "it", tZONE
, -HOUR
(3.5) },/* Iran */
520 { "zp4", tZONE
, -HOUR
(4) }, /* USSR Zone 3 */
521 { "zp5", tZONE
, -HOUR
(5) }, /* USSR Zone 4 */
523 { "ist", tZONE
, -HOUR
(5.5) },/* Indian Standard */
525 { "zp6", tZONE
, -HOUR
(6) }, /* USSR Zone 5 */
527 /* For completeness. NST is also Newfoundland Stanard, and SST is
528 * also Swedish Summer. */
529 { "nst", tZONE
, -HOUR
(6.5) },/* North Sumatra */
530 { "sst", tZONE
, -HOUR
(7) }, /* South Sumatra, USSR Zone 6 */
532 { "wast", tZONE
, -HOUR
(7) }, /* West Australian Standard */
533 { "wadt", tDAYZONE
, -HOUR
(7) }, /* West Australian Daylight */
535 { "jt", tZONE
, -HOUR
(7.5) },/* Java (3pm in Cronusland!) */
537 { "cct", tZONE
, -HOUR
(8) }, /* China Coast, USSR Zone 7 */
538 { "jst", tZONE
, -HOUR
(9) }, /* Japan Standard, USSR Zone 8 */
540 { "cast", tZONE
, -HOUR
(9.5) },/* Central Australian Standard */
541 { "cadt", tDAYZONE
, -HOUR
(9.5) },/* Central Australian Daylight */
543 { "east", tZONE
, -HOUR
(10) }, /* Eastern Australian Standard */
544 { "eadt", tDAYZONE
, -HOUR
(10) }, /* Eastern Australian Daylight */
545 { "gst", tZONE
, -HOUR
(10) }, /* Guam Standard, USSR Zone 9 */
546 { "nzt", tZONE
, -HOUR
(12) }, /* New Zealand */
547 { "nzst", tZONE
, -HOUR
(12) }, /* New Zealand Standard */
548 { "nzdt", tDAYZONE
, -HOUR
(12) }, /* New Zealand Daylight */
549 { "idle", tZONE
, -HOUR
(12) }, /* International Date Line East */
553 /* Military timezone table. */
554 static const TABLE MilitaryTable
[] = {
555 { "a", tZONE
, HOUR
( 1) },
556 { "b", tZONE
, HOUR
( 2) },
557 { "c", tZONE
, HOUR
( 3) },
558 { "d", tZONE
, HOUR
( 4) },
559 { "e", tZONE
, HOUR
( 5) },
560 { "f", tZONE
, HOUR
( 6) },
561 { "g", tZONE
, HOUR
( 7) },
562 { "h", tZONE
, HOUR
( 8) },
563 { "i", tZONE
, HOUR
( 9) },
564 { "k", tZONE
, HOUR
( 10) },
565 { "l", tZONE
, HOUR
( 11) },
566 { "m", tZONE
, HOUR
( 12) },
567 { "n", tZONE
, HOUR
(- 1) },
568 { "o", tZONE
, HOUR
(- 2) },
569 { "p", tZONE
, HOUR
(- 3) },
570 { "q", tZONE
, HOUR
(- 4) },
571 { "r", tZONE
, HOUR
(- 5) },
572 { "s", tZONE
, HOUR
(- 6) },
573 { "t", tZONE
, HOUR
(- 7) },
574 { "u", tZONE
, HOUR
(- 8) },
575 { "v", tZONE
, HOUR
(- 9) },
576 { "w", tZONE
, HOUR
(-10) },
577 { "x", tZONE
, HOUR
(-11) },
578 { "y", tZONE
, HOUR
(-12) },
579 { "z", tZONE
, HOUR
( 0) },
588 yyerror(struct dateinfo
*param
, const char **inp
, const char *s __unused
)
594 /* Adjust year from a value that might be abbreviated, to a full value.
595 * e.g. convert 70 to 1970.
596 * Input Year is either:
597 * - A negative number, which means to use its absolute value (why?)
598 * - A number from 0 to 99, which means a year from 1900 to 1999, or
599 * - The actual year (>=100).
600 * Returns the full year. */
602 AdjustYear
(time_t Year
)
616 time_t Month
, /* month of year [1-12] */
617 time_t Day
, /* day of month [1-31] */
618 time_t Year
, /* year, not abbreviated in any way */
619 time_t Hours
, /* Hour of day [0-24] */
620 time_t Minutes
, /* Minute of hour [0-59] */
621 time_t Seconds
, /* Second of minute [0-60] */
622 time_t Timezone
, /* Timezone as minutes east of UTC,
623 * or USE_LOCAL_TIME special case */
624 MERIDIAN Meridian
, /* Hours are am/pm/24 hour clock */
625 DSTMODE DSTmode
/* DST on/off/maybe */
628 struct tm tm
= {.tm_sec
= 0};
633 tm.tm_hour
= Hours
+ (Meridian
== MERpm ?
12 : 0);
635 tm.tm_mon
= Month
- 1;
636 tm.tm_year
= Year
- 1900;
638 case DSTon
: tm.tm_isdst
= 1; break
;
639 case DSToff
: tm.tm_isdst
= 0; break
;
640 default
: tm.tm_isdst
= -1; break
;
643 if
(Timezone
== USE_LOCAL_TIME
) {
644 result
= mktime
(&tm
);
646 /* We rely on mktime_z(NULL, ...) working in UTC */
647 result
= mktime_z
(NULL
, &tm
);
648 result
+= Timezone
* 60;
652 fprintf
(stderr
, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd"
655 (intmax_t)Month
, (intmax_t)Day
, (intmax_t)Year
,
656 (intmax_t)Hours
, (intmax_t)Minutes
, (intmax_t)Seconds
,
657 (intmax_t)Timezone
, (int)Meridian
, (int)DSTmode
);
658 fprintf
(stderr
, " -> %jd", (intmax_t)result
);
659 fprintf
(stderr
, " %s", ctime
(&result
));
676 if
((tm
= localtime
(&Start
)) == NULL
)
678 StartDay
= (tm
->tm_hour
+ 1) %
24;
680 if
((tm
= localtime
(&Future
)) == NULL
)
682 FutureDay
= (tm
->tm_hour
+ 1) %
24;
684 return
(Future
- Start
) + (StartDay
- FutureDay
) * 60L * 60L;
699 tm
= localtime
(&now
);
700 now
+= SECSPERDAY
* ((DayNumber
- tm
->tm_wday
+ 7) %
7);
701 now
+= 7 * SECSPERDAY
* (DayOrdinal
<= 0 ? DayOrdinal
: DayOrdinal
- 1);
702 return DSTcorrect
(Start
, now
);
719 tm
= localtime
(&Start
);
722 Month
= 12 * (tm
->tm_year
+ 1900) + tm
->tm_mon
+ RelMonth
;
724 Month
= Month %
12 + 1;
725 return DSTcorrect
(Start
,
726 Convert
(Month
, (time_t)tm
->tm_mday
, Year
,
727 (time_t)tm
->tm_hour
, (time_t)tm
->tm_min
, (time_t)tm
->tm_sec
,
728 Timezone
, MER24
, DSTmaybe
));
733 LookupWord
(YYSTYPE *yylval, char *buff
)
737 register
const TABLE
*tp
;
741 /* Make it lowercase. */
742 for
(p
= buff
; *p
; p
++)
743 if
(isupper
((unsigned char)*p
))
744 *p
= tolower
((unsigned char)*p
);
746 if
(strcmp
(buff
, "am") == 0 || strcmp
(buff
, "a.m.") == 0) {
747 yylval->Meridian
= MERam
;
750 if
(strcmp
(buff
, "pm") == 0 || strcmp
(buff
, "p.m.") == 0) {
751 yylval->Meridian
= MERpm
;
755 /* See if we have an abbreviation for a month. */
756 if
(strlen
(buff
) == 3)
758 else if
(strlen
(buff
) == 4 && buff
[3] == '.') {
765 for
(tp
= MonthDayTable
; tp
->name
; tp
++) {
767 if
(strncmp
(buff
, tp
->name
, 3) == 0) {
768 yylval->Number
= tp
->value
;
772 else if
(strcmp
(buff
, tp
->name
) == 0) {
773 yylval->Number
= tp
->value
;
778 for
(tp
= TimezoneTable
; tp
->name
; tp
++)
779 if
(strcmp
(buff
, tp
->name
) == 0) {
780 yylval->Number
= tp
->value
;
784 if
(strcmp
(buff
, "dst") == 0)
787 for
(tp
= UnitsTable
; tp
->name
; tp
++)
788 if
(strcmp
(buff
, tp
->name
) == 0) {
789 yylval->Number
= tp
->value
;
793 /* Strip off any plural and try the units table again. */
794 i
= strlen
(buff
) - 1;
795 if
(buff
[i
] == 's') {
797 for
(tp
= UnitsTable
; tp
->name
; tp
++)
798 if
(strcmp
(buff
, tp
->name
) == 0) {
799 yylval->Number
= tp
->value
;
802 buff
[i
] = 's'; /* Put back for "this" in OtherTable. */
805 for
(tp
= OtherTable
; tp
->name
; tp
++)
806 if
(strcmp
(buff
, tp
->name
) == 0) {
807 yylval->Number
= tp
->value
;
811 /* Military timezones. */
812 if
(buff
[1] == '\0' && isalpha
((unsigned char)*buff
)) {
813 for
(tp
= MilitaryTable
; tp
->name
; tp
++)
814 if
(strcmp
(buff
, tp
->name
) == 0) {
815 yylval->Number
= tp
->value
;
820 /* Drop out any periods and try the timezone table again. */
821 for
(i
= 0, p
= q
= buff
; *q
; q
++)
828 for
(tp
= TimezoneTable
; tp
->name
; tp
++)
829 if
(strcmp
(buff
, tp
->name
) == 0) {
830 yylval->Number
= tp
->value
;
839 yylex(YYSTYPE *yylval, const char **yyInput
)
846 const char *inp
= *yyInput
;
849 while
(isspace
((unsigned char)*inp
))
852 if
(isdigit
((unsigned char)(c
= *inp
)) || c
== '-' || c
== '+') {
853 if
(c
== '-' || c
== '+') {
854 sign
= c
== '-' ?
-1 : 1;
855 if
(!isdigit
((unsigned char)*++inp
))
856 /* skip the '-' sign */
861 for
(yylval->Number
= 0; isdigit
((unsigned char)(c
= *inp
++)); )
862 yylval->Number
= 10 * yylval->Number
+ c
- '0';
864 yylval->Number
= -yylval->Number
;
866 return sign ? tSNUMBER
: tUNUMBER
;
868 if
(isalpha
((unsigned char)c
)) {
869 for
(p
= buff
; isalpha
((unsigned char)(c
= *inp
++)) || c
== '.'; )
870 if
(p
< &buff
[sizeof buff
- 1])
874 return LookupWord
(yylval, buff
);
897 #define TM_YEAR_ORIGIN 1900
900 parsedate
(const char *p
, const time_t *now
, const int *zone
)
902 struct tm local
, *tm
;
907 struct dateinfo param
;
919 zonet
= USE_LOCAL_TIME
;
920 if
((tm
= localtime_r
(now
, &local
)) == NULL
)
924 * Should use the specified zone, not localtime.
925 * Fake it using gmtime and arithmetic.
926 * This is good enough because we use only the year/month/day,
927 * not other fields of struct tm.
929 time_t fake
= *now
+ (*zone
* 60);
930 if
((tm
= gmtime_r
(&fake
, &local
)) == NULL
)
933 param.yyYear
= tm
->tm_year
+ 1900;
934 param.yyMonth
= tm
->tm_mon
+ 1;
935 param.yyDay
= tm
->tm_mday
;
936 param.yyTimezone
= *zone
;
937 param.yyDSTmode
= DSTmaybe
;
941 param.yyMeridian
= MER24
;
942 param.yyRelSeconds
= 0;
943 param.yyRelMonth
= 0;
944 param.yyHaveDate
= 0;
945 param.yyHaveFullYear
= 0;
948 param.yyHaveTime
= 0;
949 param.yyHaveZone
= 0;
951 if
(yyparse(¶m
, &p
) || param.yyHaveTime
> 1 || param.yyHaveZone
> 1 ||
952 param.yyHaveDate
> 1 || param.yyHaveDay
> 1) {
957 if
(param.yyHaveDate || param.yyHaveTime || param.yyHaveDay
) {
958 if
(! param.yyHaveFullYear
) {
959 param.yyYear
= AdjustYear
(param.yyYear
);
960 param.yyHaveFullYear
= 1;
962 Start
= Convert
(param.yyMonth
, param.yyDay
, param.yyYear
, param.yyHour
,
963 param.yyMinutes
, param.yySeconds
, param.yyTimezone
,
964 param.yyMeridian
, param.yyDSTmode
);
965 if
(Start
== -1 && errno
!= 0)
970 if
(!param.yyHaveRel
)
971 Start
-= ((tm
->tm_hour
* 60L + tm
->tm_min
) * 60L) + tm
->tm_sec
;
974 Start
+= param.yyRelSeconds
;
975 rm
= RelativeMonth
(Start
, param.yyRelMonth
, param.yyTimezone
);
976 if
(rm
== -1 && errno
!= 0)
980 if
(param.yyHaveDay
&& !param.yyHaveDate
) {
981 tod
= RelativeDate
(Start
, param.yyDayOrdinal
, param.yyDayNumber
);
995 main
(int ac
, char *av
[])
1000 (void)printf
("Enter date, or blank line to exit.\n\t> ");
1001 (void)fflush
(stdout
);
1002 while
(fgets
(buff
, sizeof
(buff
), stdin
) && buff
[0] != '\n') {
1004 d
= parsedate
(buff
, NULL
, NULL
);
1005 if
(d
== -1 && errno
!= 0)
1006 (void)printf
("Bad format - couldn't convert: %s\n",
1009 (void)printf
("%jd\t%s", (intmax_t)d
, ctime
(&d
));
1010 (void)printf
("\t> ");
1011 (void)fflush
(stdout
);
1016 #endif /* defined(TEST) */