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.16 2013/06/12 01:46:07 yamt 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)
47 ** An entry in the lexical lookup table.
49 typedef
struct _TABLE
{
57 ** Daylight-savings mode: on, off, or not yet known.
59 typedef
enum _DSTMODE
{
60 DSTon
, DSToff
, DSTmaybe
64 ** Meridian: am, pm, or 24-hour style.
66 typedef
enum _MERIDIAN
{
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
}
144 cvsstamp: tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
{
146 if
(param
->yyYear
< 100) param
->yyYear
+= 1900;
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;
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
;
169 param
->yyYear
= EPOCH
;
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
{
186 param
->yyMinutes
= 0;
187 param
->yySeconds
= 0;
188 param
->yyMeridian
= $2;
190 | tUNUMBER
':' tUNUMBER o_merid
{
192 param
->yyMinutes
= $3;
193 param
->yySeconds
= 0;
194 param
->yyMeridian
= $4;
196 | tUNUMBER
':' tUNUMBER tSNUMBER
{
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
{
205 param
->yyMinutes
= $3;
206 param
->yySeconds
= $5;
207 param
->yyMeridian
= $6;
209 | tUNUMBER
':' tUNUMBER
':' tUNUMBER tSNUMBER
{
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
{
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); */
229 param
->yyTimezone
= $1;
230 param
->yyDSTmode
= DSToff
;
233 param
->yyTimezone
= $1;
234 param
->yyDSTmode
= DSTon
;
238 param
->yyTimezone
= $1;
239 param
->yyDSTmode
= DSTon
;
244 param
->yyDayOrdinal
= 1;
245 param
->yyDayNumber
= $1;
248 param
->yyDayOrdinal
= 1;
249 param
->yyDayNumber
= $1;
252 param
->yyDayOrdinal
= $1;
253 param
->yyDayNumber
= $2;
257 date
: tUNUMBER
'/' tUNUMBER
{
261 | tUNUMBER
'/' tUNUMBER
'/' tUNUMBER
{
272 | tUNUMBER tSNUMBER tSNUMBER
{
273 /* ISO 8601 format. yyyy-mm-dd. */
275 param
->yyMonth
= -$2;
278 | tUNUMBER tMONTH tSNUMBER
{
279 /* e.g. 17-JUN-1992. */
288 | tMONTH tUNUMBER
',' tUNUMBER
{
297 | tUNUMBER tMONTH tUNUMBER
{
305 param
->yyRelSeconds
= -param
->yyRelSeconds
;
306 param
->yyRelMonth
= -param
->yyRelMonth
;
311 relunit
: tUNUMBER tMINUTE_UNIT
{
312 param
->yyRelSeconds
+= $1 * $2 * 60L;
314 | tSNUMBER tMINUTE_UNIT
{
315 param
->yyRelSeconds
+= $1 * $2 * 60L;
318 param
->yyRelSeconds
+= $1 * 60L;
320 | tSNUMBER tSEC_UNIT
{
321 param
->yyRelSeconds
+= $1;
323 | tUNUMBER tSEC_UNIT
{
324 param
->yyRelSeconds
+= $1;
327 param
->yyRelSeconds
++;
329 | tSNUMBER tMONTH_UNIT
{
330 param
->yyRelMonth
+= $1 * $2;
332 | tUNUMBER tMONTH_UNIT
{
333 param
->yyRelMonth
+= $1 * $2;
336 param
->yyRelMonth
+= $1;
341 if
(param
->yyHaveTime
&& param
->yyHaveDate
&& !param
->yyHaveRel
)
346 param
->yyDay
= ($1)%
100;
347 param
->yyMonth
= ($1/100)%
100;
348 param
->yyYear
= $1/10000;
354 param
->yyMinutes
= 0;
357 param
->yyHour
= $1 / 100;
358 param
->yyMinutes
= $1 %
100;
360 param
->yySeconds
= 0;
361 param
->yyMeridian
= MER24
;
367 o_merid
: /* NULL */ {
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 },
396 { "wednesday", tDAY
, 3 },
397 { "wednes", tDAY
, 3 },
398 { "thursday", tDAY
, 4 },
400 { "thurs", tDAY
, 4 },
401 { "friday", tDAY
, 5 },
402 { "saturday", tDAY
, 6 },
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 },
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 },
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 */
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 */
475 { "nft", tZONE
, HOUR
(3.5) }, /* Newfoundland */
476 { "nst", tZONE
, HOUR
(3.5) }, /* Newfoundland Standard */
477 { "ndt", tDAYZONE
, HOUR
(3.5) }, /* Newfoundland Daylight */
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 */
508 { "it", tZONE
, -HOUR
(3.5) },/* Iran */
510 { "zp4", tZONE
, -HOUR
(4) }, /* USSR Zone 3 */
511 { "zp5", tZONE
, -HOUR
(5) }, /* USSR Zone 4 */
513 { "ist", tZONE
, -HOUR
(5.5) },/* Indian Standard */
515 { "zp6", tZONE
, -HOUR
(6) }, /* USSR Zone 5 */
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 */
522 { "wast", tZONE
, -HOUR
(7) }, /* West Australian Standard */
523 { "wadt", tDAYZONE
, -HOUR
(7) }, /* West Australian Daylight */
525 { "jt", tZONE
, -HOUR
(7.5) },/* Java (3pm in Cronusland!) */
527 { "cct", tZONE
, -HOUR
(8) }, /* China Coast, USSR Zone 7 */
528 { "jst", tZONE
, -HOUR
(9) }, /* Japan Standard, USSR Zone 8 */
530 { "cast", tZONE
, -HOUR
(9.5) },/* Central Australian Standard */
531 { "cadt", tDAYZONE
, -HOUR
(9.5) },/* Central Australian Daylight */
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 */
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) },
578 yyerror(struct dateinfo
*param
, const char **inp
, const char *s __unused
)
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). */
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};
614 tm.tm_hour
= Hours
+ (Meridian
== MERpm ?
12 : 0);
616 tm.tm_mon
= Month
- 1;
617 tm.tm_year
= Year
- 1900;
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;
641 if
((tm
= localtime
(&Start
)) == NULL
)
643 StartDay
= (tm
->tm_hour
+ 1) %
24;
645 if
((tm
= localtime
(&Future
)) == NULL
)
647 FutureDay
= (tm
->tm_hour
+ 1) %
24;
649 return
(Future
- Start
) + (StartDay
- FutureDay
) * 60L * 60L;
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
);
684 tm
= localtime
(&Start
);
687 Month
= 12 * (tm
->tm_year
+ 1900) + tm
->tm_mon
+ RelMonth
;
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
));
698 LookupWord
(YYSTYPE *yylval, char *buff
)
702 register
const TABLE
*tp
;
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
;
715 if
(strcmp
(buff
, "pm") == 0 || strcmp
(buff
, "p.m.") == 0) {
716 yylval->Meridian
= MERpm
;
720 /* See if we have an abbreviation for a month. */
721 if
(strlen
(buff
) == 3)
723 else if
(strlen
(buff
) == 4 && buff
[3] == '.') {
730 for
(tp
= MonthDayTable
; tp
->name
; tp
++) {
732 if
(strncmp
(buff
, tp
->name
, 3) == 0) {
733 yylval->Number
= tp
->value
;
737 else if
(strcmp
(buff
, tp
->name
) == 0) {
738 yylval->Number
= tp
->value
;
743 for
(tp
= TimezoneTable
; tp
->name
; tp
++)
744 if
(strcmp
(buff
, tp
->name
) == 0) {
745 yylval->Number
= tp
->value
;
749 if
(strcmp
(buff
, "dst") == 0)
752 for
(tp
= UnitsTable
; tp
->name
; tp
++)
753 if
(strcmp
(buff
, tp
->name
) == 0) {
754 yylval->Number
= tp
->value
;
758 /* Strip off any plural and try the units table again. */
759 i
= strlen
(buff
) - 1;
760 if
(buff
[i
] == 's') {
762 for
(tp
= UnitsTable
; tp
->name
; tp
++)
763 if
(strcmp
(buff
, tp
->name
) == 0) {
764 yylval->Number
= tp
->value
;
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
;
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
;
785 /* Drop out any periods and try the timezone table again. */
786 for
(i
= 0, p
= q
= buff
; *q
; q
++)
793 for
(tp
= TimezoneTable
; tp
->name
; tp
++)
794 if
(strcmp
(buff
, tp
->name
) == 0) {
795 yylval->Number
= tp
->value
;
804 yylex(YYSTYPE *yylval, const char **yyInput
)
811 const char *inp
= *yyInput
;
814 while
(isspace
((unsigned char)*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 */
826 for
(yylval->Number
= 0; isdigit
((unsigned char)(c
= *inp
++)); )
827 yylval->Number
= 10 * yylval->Number
+ c
- '0';
829 yylval->Number
= -yylval->Number
;
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])
839 return LookupWord
(yylval, buff
);
862 #define TM_YEAR_ORIGIN 1900
864 /* Yield A - B, measured in seconds. */
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);
871 /* difference in day of year */
872 a
->tm_yday
- b
->tm_yday
873 /* + intervening leap days */
874 + ((ay
>> 2) - (by
>> 2))
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
));
886 parsedate
(const char *p
, const time_t *now
, const int *zone
)
888 struct tm gmt
, local
, *gmt_ptr
, *tm
;
893 struct dateinfo param
;
899 if
(now
== NULL || zone
== NULL
) {
904 gmt_ptr
= gmtime_r
(now
, &gmt
);
905 if
((tm
= localtime_r
(now
, &local
)) == NULL
)
909 zonet
= difftm
(&gmt
, &local
) / 60;
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
921 if
((tm
= localtime_r
(now
, &local
)) == NULL
)
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
;
932 param.yyMeridian
= MER24
;
933 param.yyRelSeconds
= 0;
934 param.yyRelMonth
= 0;
935 param.yyHaveDate
= 0;
938 param.yyHaveTime
= 0;
939 param.yyHaveZone
= 0;
941 if
(yyparse(¶m
, &p
) || param.yyHaveTime
> 1 || param.yyHaveZone
> 1 ||
942 param.yyHaveDate
> 1 || param.yyHaveDay
> 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)
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)
966 if
(param.yyHaveDay
&& !param.yyHaveDate
) {
967 tod
= RelativeDate
(Start
, param.yyDayOrdinal
, param.yyDayNumber
);
981 main
(int ac
, char *av
[])
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') {
990 d
= parsedate
(buff
, NULL
, NULL
);
991 if
(d
== -1 && errno
!= 0)
992 (void)printf
("Bad format - couldn't convert: %s\n",
995 (void)printf
("%jd\t%s", (intmax_t)d
, ctime
(&d
));
996 (void)printf
("\t> ");
997 (void)fflush
(stdout
);
1002 #endif /* defined(TEST) */