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 */
22 /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
25 We don't want to mess with all the portability hassles of alloca.
26 In particular, most (all?) versions of bison will use alloca in
27 their parser. If bison works on your system (e.g. it should work
28 with gcc), then go ahead and use it, but the more general solution
29 is to use byacc instead of bison, which should generate a portable
30 parser. I played with adding "#define alloca dont_use_alloca", to
31 give an error if the parser generator uses alloca (and thus detect
32 unportable parsedate.c's), but that seems to cause as many problems
36 #define HOUR(x) ((time_t)(x) * 60)
37 #define SECSPERDAY (24L * 60L * 60L)
41 ** An entry in the lexical lookup table.
43 typedef
struct _TABLE
{
51 ** Daylight-savings mode: on, off, or not yet known.
53 typedef
enum _DSTMODE
{
54 DSTon
, DSToff
, DSTmaybe
58 ** Meridian: am, pm, or 24-hour style.
60 typedef
enum _MERIDIAN
{
66 ** Global variables. We could get rid of most of these by using a good
67 ** union as the yacc stack. (This routine was originally written before
68 ** yacc had the %union construct.) Maybe someday; right now we only use
69 ** the %union very rarely.
71 static const char *yyInput
;
72 static DSTMODE yyDSTmode
;
73 static time_t yyDayOrdinal
;
74 static time_t yyDayNumber
;
75 static int yyHaveDate
;
78 static int yyHaveTime
;
79 static int yyHaveZone
;
80 static time_t yyTimezone
;
83 static time_t yyMinutes
;
84 static time_t yyMonth
;
85 static time_t yySeconds
;
87 static MERIDIAN yyMeridian
;
88 static time_t yyRelMonth
;
89 static time_t yyRelSeconds
;
95 enum _MERIDIAN Meridian
;
98 %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
99 %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
101 %type
<Number
> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
102 %type
<Number
> tSEC_UNIT tSNUMBER tUNUMBER tZONE
103 %type
<Meridian
> tMERIDIAN o_merid
134 cvsstamp: tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
'.' tUNUMBER
{
136 if
(yyYear
< 100) yyYear
+= 1900;
147 time
: tUNUMBER tMERIDIAN
{
153 | tUNUMBER
':' tUNUMBER o_merid
{
159 | tUNUMBER
':' tUNUMBER tSNUMBER
{
164 yyTimezone
= - ($4 %
100 + ($4 / 100) * 60);
166 | tUNUMBER
':' tUNUMBER
':' tUNUMBER o_merid
{
172 | tUNUMBER
':' tUNUMBER
':' tUNUMBER tSNUMBER
{
178 yyTimezone
= - ($6 %
100 + ($6 / 100) * 60);
211 date
: tUNUMBER
'/' tUNUMBER
{
215 | tUNUMBER
'/' tUNUMBER
'/' tUNUMBER
{
226 | tUNUMBER tSNUMBER tSNUMBER
{
227 /* ISO 8601 format. yyyy-mm-dd. */
232 | tUNUMBER tMONTH tSNUMBER
{
233 /* e.g. 17-JUN-1992. */
242 | tMONTH tUNUMBER
',' tUNUMBER
{
251 | tUNUMBER tMONTH tUNUMBER
{
259 yyRelSeconds
= -yyRelSeconds
;
260 yyRelMonth
= -yyRelMonth
;
265 relunit
: tUNUMBER tMINUTE_UNIT
{
266 yyRelSeconds
+= $1 * $2 * 60L;
268 | tSNUMBER tMINUTE_UNIT
{
269 yyRelSeconds
+= $1 * $2 * 60L;
272 yyRelSeconds
+= $1 * 60L;
274 | tSNUMBER tSEC_UNIT
{
277 | tUNUMBER tSEC_UNIT
{
283 | tSNUMBER tMONTH_UNIT
{
284 yyRelMonth
+= $1 * $2;
286 | tUNUMBER tMONTH_UNIT
{
287 yyRelMonth
+= $1 * $2;
295 if
(yyHaveTime
&& yyHaveDate
&& !yyHaveRel
)
301 yyMonth
= ($1/100)%
100;
312 yyMinutes
= $1 %
100;
321 o_merid
: /* NULL */ {
331 /* Month and day table. */
332 static TABLE
const MonthDayTable
[] = {
333 { "january", tMONTH
, 1 },
334 { "february", tMONTH
, 2 },
335 { "march", tMONTH
, 3 },
336 { "april", tMONTH
, 4 },
337 { "may", tMONTH
, 5 },
338 { "june", tMONTH
, 6 },
339 { "july", tMONTH
, 7 },
340 { "august", tMONTH
, 8 },
341 { "september", tMONTH
, 9 },
342 { "sept", tMONTH
, 9 },
343 { "october", tMONTH
, 10 },
344 { "november", tMONTH
, 11 },
345 { "december", tMONTH
, 12 },
346 { "sunday", tDAY
, 0 },
347 { "monday", tDAY
, 1 },
348 { "tuesday", tDAY
, 2 },
350 { "wednesday", tDAY
, 3 },
351 { "wednes", tDAY
, 3 },
352 { "thursday", tDAY
, 4 },
354 { "thurs", tDAY
, 4 },
355 { "friday", tDAY
, 5 },
356 { "saturday", tDAY
, 6 },
360 /* Time units table. */
361 static TABLE
const UnitsTable
[] = {
362 { "year", tMONTH_UNIT
, 12 },
363 { "month", tMONTH_UNIT
, 1 },
364 { "fortnight", tMINUTE_UNIT
, 14 * 24 * 60 },
365 { "week", tMINUTE_UNIT
, 7 * 24 * 60 },
366 { "day", tMINUTE_UNIT
, 1 * 24 * 60 },
367 { "hour", tMINUTE_UNIT
, 60 },
368 { "minute", tMINUTE_UNIT
, 1 },
369 { "min", tMINUTE_UNIT
, 1 },
370 { "second", tSEC_UNIT
, 1 },
371 { "sec", tSEC_UNIT
, 1 },
375 /* Assorted relative-time words. */
376 static TABLE
const OtherTable
[] = {
377 { "tomorrow", tMINUTE_UNIT
, 1 * 24 * 60 },
378 { "yesterday", tMINUTE_UNIT
, -1 * 24 * 60 },
379 { "today", tMINUTE_UNIT
, 0 },
380 { "now", tMINUTE_UNIT
, 0 },
381 { "last", tUNUMBER
, -1 },
382 { "this", tMINUTE_UNIT
, 0 },
383 { "next", tUNUMBER
, 2 },
384 { "first", tUNUMBER
, 1 },
385 /* { "second", tUNUMBER, 2 }, */
386 { "third", tUNUMBER
, 3 },
387 { "fourth", tUNUMBER
, 4 },
388 { "fifth", tUNUMBER
, 5 },
389 { "sixth", tUNUMBER
, 6 },
390 { "seventh", tUNUMBER
, 7 },
391 { "eighth", tUNUMBER
, 8 },
392 { "ninth", tUNUMBER
, 9 },
393 { "tenth", tUNUMBER
, 10 },
394 { "eleventh", tUNUMBER
, 11 },
395 { "twelfth", tUNUMBER
, 12 },
400 /* The timezone table. */
401 /* Some of these are commented out because a time_t can't store a float. */
402 static TABLE
const TimezoneTable
[] = {
403 { "gmt", tZONE
, HOUR
( 0) }, /* Greenwich Mean */
404 { "ut", tZONE
, HOUR
( 0) }, /* Universal (Coordinated) */
405 { "utc", tZONE
, HOUR
( 0) },
406 { "wet", tZONE
, HOUR
( 0) }, /* Western European */
407 { "bst", tDAYZONE
, HOUR
( 0) }, /* British Summer */
408 { "wat", tZONE
, HOUR
( 1) }, /* West Africa */
409 { "at", tZONE
, HOUR
( 2) }, /* Azores */
411 /* For completeness. BST is also British Summer, and GST is
412 * also Guam Standard. */
413 { "bst", tZONE
, HOUR
( 3) }, /* Brazil Standard */
414 { "gst", tZONE
, HOUR
( 3) }, /* Greenland Standard */
417 { "nft", tZONE
, HOUR
(3.5) }, /* Newfoundland */
418 { "nst", tZONE
, HOUR
(3.5) }, /* Newfoundland Standard */
419 { "ndt", tDAYZONE
, HOUR
(3.5) }, /* Newfoundland Daylight */
421 { "ast", tZONE
, HOUR
( 4) }, /* Atlantic Standard */
422 { "adt", tDAYZONE
, HOUR
( 4) }, /* Atlantic Daylight */
423 { "est", tZONE
, HOUR
( 5) }, /* Eastern Standard */
424 { "edt", tDAYZONE
, HOUR
( 5) }, /* Eastern Daylight */
425 { "cst", tZONE
, HOUR
( 6) }, /* Central Standard */
426 { "cdt", tDAYZONE
, HOUR
( 6) }, /* Central Daylight */
427 { "mst", tZONE
, HOUR
( 7) }, /* Mountain Standard */
428 { "mdt", tDAYZONE
, HOUR
( 7) }, /* Mountain Daylight */
429 { "pst", tZONE
, HOUR
( 8) }, /* Pacific Standard */
430 { "pdt", tDAYZONE
, HOUR
( 8) }, /* Pacific Daylight */
431 { "yst", tZONE
, HOUR
( 9) }, /* Yukon Standard */
432 { "ydt", tDAYZONE
, HOUR
( 9) }, /* Yukon Daylight */
433 { "hst", tZONE
, HOUR
(10) }, /* Hawaii Standard */
434 { "hdt", tDAYZONE
, HOUR
(10) }, /* Hawaii Daylight */
435 { "cat", tZONE
, HOUR
(10) }, /* Central Alaska */
436 { "ahst", tZONE
, HOUR
(10) }, /* Alaska-Hawaii Standard */
437 { "nt", tZONE
, HOUR
(11) }, /* Nome */
438 { "idlw", tZONE
, HOUR
(12) }, /* International Date Line West */
439 { "cet", tZONE
, -HOUR
(1) }, /* Central European */
440 { "met", tZONE
, -HOUR
(1) }, /* Middle European */
441 { "mewt", tZONE
, -HOUR
(1) }, /* Middle European Winter */
442 { "mest", tDAYZONE
, -HOUR
(1) }, /* Middle European Summer */
443 { "swt", tZONE
, -HOUR
(1) }, /* Swedish Winter */
444 { "sst", tDAYZONE
, -HOUR
(1) }, /* Swedish Summer */
445 { "fwt", tZONE
, -HOUR
(1) }, /* French Winter */
446 { "fst", tDAYZONE
, -HOUR
(1) }, /* French Summer */
447 { "eet", tZONE
, -HOUR
(2) }, /* Eastern Europe, USSR Zone 1 */
448 { "bt", tZONE
, -HOUR
(3) }, /* Baghdad, USSR Zone 2 */
450 { "it", tZONE
, -HOUR
(3.5) },/* Iran */
452 { "zp4", tZONE
, -HOUR
(4) }, /* USSR Zone 3 */
453 { "zp5", tZONE
, -HOUR
(5) }, /* USSR Zone 4 */
455 { "ist", tZONE
, -HOUR
(5.5) },/* Indian Standard */
457 { "zp6", tZONE
, -HOUR
(6) }, /* USSR Zone 5 */
459 /* For completeness. NST is also Newfoundland Stanard, and SST is
460 * also Swedish Summer. */
461 { "nst", tZONE
, -HOUR
(6.5) },/* North Sumatra */
462 { "sst", tZONE
, -HOUR
(7) }, /* South Sumatra, USSR Zone 6 */
464 { "wast", tZONE
, -HOUR
(7) }, /* West Australian Standard */
465 { "wadt", tDAYZONE
, -HOUR
(7) }, /* West Australian Daylight */
467 { "jt", tZONE
, -HOUR
(7.5) },/* Java (3pm in Cronusland!) */
469 { "cct", tZONE
, -HOUR
(8) }, /* China Coast, USSR Zone 7 */
470 { "jst", tZONE
, -HOUR
(9) }, /* Japan Standard, USSR Zone 8 */
472 { "cast", tZONE
, -HOUR
(9.5) },/* Central Australian Standard */
473 { "cadt", tDAYZONE
, -HOUR
(9.5) },/* Central Australian Daylight */
475 { "east", tZONE
, -HOUR
(10) }, /* Eastern Australian Standard */
476 { "eadt", tDAYZONE
, -HOUR
(10) }, /* Eastern Australian Daylight */
477 { "gst", tZONE
, -HOUR
(10) }, /* Guam Standard, USSR Zone 9 */
478 { "nzt", tZONE
, -HOUR
(12) }, /* New Zealand */
479 { "nzst", tZONE
, -HOUR
(12) }, /* New Zealand Standard */
480 { "nzdt", tDAYZONE
, -HOUR
(12) }, /* New Zealand Daylight */
481 { "idle", tZONE
, -HOUR
(12) }, /* International Date Line East */
485 /* Military timezone table. */
486 static TABLE
const MilitaryTable
[] = {
487 { "a", tZONE
, HOUR
( 1) },
488 { "b", tZONE
, HOUR
( 2) },
489 { "c", tZONE
, HOUR
( 3) },
490 { "d", tZONE
, HOUR
( 4) },
491 { "e", tZONE
, HOUR
( 5) },
492 { "f", tZONE
, HOUR
( 6) },
493 { "g", tZONE
, HOUR
( 7) },
494 { "h", tZONE
, HOUR
( 8) },
495 { "i", tZONE
, HOUR
( 9) },
496 { "k", tZONE
, HOUR
( 10) },
497 { "l", tZONE
, HOUR
( 11) },
498 { "m", tZONE
, HOUR
( 12) },
499 { "n", tZONE
, HOUR
(- 1) },
500 { "o", tZONE
, HOUR
(- 2) },
501 { "p", tZONE
, HOUR
(- 3) },
502 { "q", tZONE
, HOUR
(- 4) },
503 { "r", tZONE
, HOUR
(- 5) },
504 { "s", tZONE
, HOUR
(- 6) },
505 { "t", tZONE
, HOUR
(- 7) },
506 { "u", tZONE
, HOUR
(- 8) },
507 { "v", tZONE
, HOUR
(- 9) },
508 { "w", tZONE
, HOUR
(-10) },
509 { "x", tZONE
, HOUR
(-11) },
510 { "y", tZONE
, HOUR
(-12) },
511 { "z", tZONE
, HOUR
( 0) },
520 yyerror(const char *s __unused
)
534 if
(Minutes
< 0 || Minutes
> 59 || Seconds
< 0 || Seconds
> 59)
538 if
(Hours
< 0 || Hours
> 23)
540 return
(Hours
* 60L + Minutes
) * 60L + Seconds
;
542 if
(Hours
< 1 || Hours
> 12)
546 return
(Hours
* 60L + Minutes
) * 60L + Seconds
;
548 if
(Hours
< 1 || Hours
> 12)
552 return
((Hours
+ 12) * 60L + Minutes
) * 60L + Seconds
;
561 * A negative number, which means to use its absolute value (why?)
562 * A number from 0 to 99, which means a year from 1900 to 1999, or
563 * The actual year (>=100). */
576 static int DaysInMonth
[12] = {
577 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
589 DaysInMonth
[1] = Year %
4 == 0 && (Year %
100 != 0 || Year %
400 == 0)
591 /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
592 I'm too lazy to try to check for time_t overflow in another way. */
593 if
(Year
< EPOCH || Year
> 2038
594 || Month
< 1 || Month
> 12
595 /* Lint fluff: "conversion from long may lose accuracy" */
596 || Day
< 1 || Day
> DaysInMonth
[(int)--Month
])
598 * It would be nice to set a global error string here.
599 * "February 30 is not a valid date" is much more informative than
600 * "Can't parse date/time: 100 months" when the user input was
601 * "100 months" and addition resolved that to February 30, for
602 * example. See rcs2-7 in src/sanity.sh for more. */
605 for
(Julian
= Day
- 1, i
= 0; i
< Month
; i
++)
606 Julian
+= DaysInMonth
[i
];
607 for
(i
= EPOCH
; i
< Year
; i
++)
608 Julian
+= 365 + (i %
4 == 0);
609 Julian
*= SECSPERDAY
;
610 Julian
+= yyTimezone
* 60L;
611 if
((tod
= ToSeconds
(Hours
, Minutes
, Seconds
, Meridian
)) < 0)
615 ||
(DSTmode
== DSTmaybe
&& localtime
(&Julian
)->tm_isdst
))
630 StartDay
= (localtime
(&Start
)->tm_hour
+ 1) %
24;
631 FutureDay
= (localtime
(&Future
)->tm_hour
+ 1) %
24;
632 return
(Future
- Start
) + (StartDay
- FutureDay
) * 60L * 60L;
647 tm
= localtime
(&now
);
648 now
+= SECSPERDAY
* ((DayNumber
- tm
->tm_wday
+ 7) %
7);
649 now
+= 7 * SECSPERDAY
* (DayOrdinal
<= 0 ? DayOrdinal
: DayOrdinal
- 1);
650 return DSTcorrect
(Start
, now
);
666 tm
= localtime
(&Start
);
667 Month
= 12 * (tm
->tm_year
+ 1900) + tm
->tm_mon
+ RelMonth
;
669 Month
= Month %
12 + 1;
670 return DSTcorrect
(Start
,
671 Convert
(Month
, (time_t)tm
->tm_mday
, Year
,
672 (time_t)tm
->tm_hour
, (time_t)tm
->tm_min
, (time_t)tm
->tm_sec
,
678 LookupWord
(char *buff
)
682 register
const TABLE
*tp
;
686 /* Make it lowercase. */
687 for
(p
= buff
; *p
; p
++)
688 if
(isupper
((unsigned char)*p
))
689 *p
= tolower
((unsigned char)*p
);
691 if
(strcmp
(buff
, "am") == 0 || strcmp
(buff
, "a.m.") == 0) {
692 yylval.Meridian
= MERam
;
695 if
(strcmp
(buff
, "pm") == 0 || strcmp
(buff
, "p.m.") == 0) {
696 yylval.Meridian
= MERpm
;
700 /* See if we have an abbreviation for a month. */
701 if
(strlen
(buff
) == 3)
703 else if
(strlen
(buff
) == 4 && buff
[3] == '.') {
710 for
(tp
= MonthDayTable
; tp
->name
; tp
++) {
712 if
(strncmp
(buff
, tp
->name
, 3) == 0) {
713 yylval.Number
= tp
->value
;
717 else if
(strcmp
(buff
, tp
->name
) == 0) {
718 yylval.Number
= tp
->value
;
723 for
(tp
= TimezoneTable
; tp
->name
; tp
++)
724 if
(strcmp
(buff
, tp
->name
) == 0) {
725 yylval.Number
= tp
->value
;
729 if
(strcmp
(buff
, "dst") == 0)
732 for
(tp
= UnitsTable
; tp
->name
; tp
++)
733 if
(strcmp
(buff
, tp
->name
) == 0) {
734 yylval.Number
= tp
->value
;
738 /* Strip off any plural and try the units table again. */
739 i
= strlen
(buff
) - 1;
740 if
(buff
[i
] == 's') {
742 for
(tp
= UnitsTable
; tp
->name
; tp
++)
743 if
(strcmp
(buff
, tp
->name
) == 0) {
744 yylval.Number
= tp
->value
;
747 buff
[i
] = 's'; /* Put back for "this" in OtherTable. */
750 for
(tp
= OtherTable
; tp
->name
; tp
++)
751 if
(strcmp
(buff
, tp
->name
) == 0) {
752 yylval.Number
= tp
->value
;
756 /* Military timezones. */
757 if
(buff
[1] == '\0' && isalpha
((unsigned char)*buff
)) {
758 for
(tp
= MilitaryTable
; tp
->name
; tp
++)
759 if
(strcmp
(buff
, tp
->name
) == 0) {
760 yylval.Number
= tp
->value
;
765 /* Drop out any periods and try the timezone table again. */
766 for
(i
= 0, p
= q
= buff
; *q
; q
++)
773 for
(tp
= TimezoneTable
; tp
->name
; tp
++)
774 if
(strcmp
(buff
, tp
->name
) == 0) {
775 yylval.Number
= tp
->value
;
793 while
(isspace
((unsigned char)*yyInput
))
796 if
(isdigit
((unsigned char)(c
= *yyInput
)) || c
== '-' || c
== '+') {
797 if
(c
== '-' || c
== '+') {
798 sign
= c
== '-' ?
-1 : 1;
799 if
(!isdigit
((unsigned char)*++yyInput
))
800 /* skip the '-' sign */
805 for
(yylval.Number
= 0; isdigit
((unsigned char)(c
= *yyInput
++)); )
806 yylval.Number
= 10 * yylval.Number
+ c
- '0';
809 yylval.Number
= -yylval.Number
;
810 return sign ? tSNUMBER
: tUNUMBER
;
812 if
(isalpha
((unsigned char)c
)) {
813 for
(p
= buff
; isalpha
((unsigned char)(c
= *yyInput
++)) || c
== '.'; )
814 if
(p
< &buff
[sizeof buff
- 1])
818 return LookupWord
(buff
);
835 #define TM_YEAR_ORIGIN 1900
837 /* Yield A - B, measured in seconds. */
839 difftm
(struct tm
*a
, struct tm
*b
)
841 int ay
= a
->tm_year
+ (TM_YEAR_ORIGIN
- 1);
842 int by
= b
->tm_year
+ (TM_YEAR_ORIGIN
- 1);
844 /* difference in day of year */
845 a
->tm_yday
- b
->tm_yday
846 /* + intervening leap days */
847 + ((ay
>> 2) - (by
>> 2))
849 + ((ay
/100 >> 2) - (by
/100 >> 2))
850 /* + difference in years * 365 */
851 + (long)(ay
-by
) * 365
853 return
(60*(60*(24*days
+ (a
->tm_hour
- b
->tm_hour
))
854 + (a
->tm_min
- b
->tm_min
))
855 + (a
->tm_sec
- b
->tm_sec
));
859 parsedate
(const char *p
, const time_t *now
, const int *zone
)
861 struct tm gmt
, local
, *gmt_ptr
, *tm
;
868 if
(now
== NULL || zone
== NULL
) {
873 gmt_ptr
= gmtime_r
(now
, &gmt
);
874 if
((tm
= localtime_r
(now
, &local
)) == NULL
)
878 zonet
= difftm
(&gmt
, &local
) / 60;
880 /* We are on a system like VMS, where the system clock is
881 in local time and the system has no concept of timezones.
882 Hopefully we can fake this out (for the case in which the
883 user specifies no timezone) by just saying the timezone
890 if
((tm
= localtime_r
(now
, &local
)) == NULL
)
893 yyYear
= tm
->tm_year
+ 1900;
894 yyMonth
= tm
->tm_mon
+ 1;
897 yyDSTmode
= DSTmaybe
;
911 || yyHaveTime
> 1 || yyHaveZone
> 1 || yyHaveDate
> 1 || yyHaveDay
> 1)
914 if
(yyHaveDate || yyHaveTime || yyHaveDay
) {
915 Start
= Convert
(yyMonth
, yyDay
, yyYear
, yyHour
, yyMinutes
, yySeconds
,
916 yyMeridian
, yyDSTmode
);
923 Start
-= ((tm
->tm_hour
* 60L + tm
->tm_min
) * 60L) + tm
->tm_sec
;
926 Start
+= yyRelSeconds
;
927 Start
+= RelativeMonth
(Start
, yyRelMonth
);
929 if
(yyHaveDay
&& !yyHaveDate
) {
930 tod
= RelativeDate
(Start
, yyDayOrdinal
, yyDayNumber
);
934 /* Have to do *something* with a legitimate -1 so it's distinguishable
935 * from the error return value. (Alternately could set errno on error.) */
936 return Start
== -1 ?
0 : Start
;
951 (void)printf
("Enter date, or blank line to exit.\n\t> ");
952 (void)fflush
(stdout
);
953 while
(gets
(buff
) && buff
[0]) {
954 d
= parsedate
(buff
, NULL
, NULL
);
956 (void)printf
("Bad format - couldn't convert.\n");
958 (void)printf
("%s", ctime
(&d
));
959 (void)printf
("\t> ");
960 (void)fflush
(stdout
);
965 #endif /* defined(TEST) */