Use mktime(3) instead of hand-coded equivalent.

Don't treat negative results as errors (we should be able
to handle years before 1970).

XXX: This still fails on 1969-12-31 23:59:59.
This commit is contained in:
apb 2011-12-17 19:14:10 +00:00
parent 7cf05991c0
commit f93996848b
1 changed files with 23 additions and 100 deletions

View File

@ -573,69 +573,24 @@ yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
}
static time_t
ToSeconds(
time_t Hours,
time_t Minutes,
time_t Seconds,
MERIDIAN Meridian
)
{
if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
return -1;
switch (Meridian) {
case MER24:
if (Hours < 0 || Hours > 23)
return -1;
return (Hours * 60L + Minutes) * 60L + Seconds;
case MERam:
if (Hours < 1 || Hours > 12)
return -1;
if (Hours == 12)
Hours = 0;
return (Hours * 60L + Minutes) * 60L + Seconds;
case MERpm:
if (Hours < 1 || Hours > 12)
return -1;
if (Hours == 12)
Hours = 0;
return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
default:
abort ();
}
/* NOTREACHED */
}
static int
isLeap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
/* Year is either
* A negative number, which means to use its absolute value (why?)
* A number from 0 to 99, which means a year from 1900 to 1999, or
* The actual year (>=100). */
static time_t
Convert(
time_t Month,
time_t Day,
time_t Year,
time_t Hours,
time_t Minutes,
time_t Seconds,
time_t Timezone,
MERIDIAN Meridian,
DSTMODE DSTmode
time_t Month, /* month of year [1-12] */
time_t Day, /* day of month [1-31] */
time_t Year, /* year; see above comment */
time_t Hours, /* Hour of day [0-24] */
time_t Minutes, /* Minute of hour [0-59] */
time_t Seconds, /* Second of minute [0-60] */
time_t Timezone, /* Timezone as seconds west of UTC */
MERIDIAN Meridian, /* Hours are am/pm/24 hour clock */
DSTMODE DSTmode /* DST on/off/maybe */
)
{
static int DaysInMonth[12] = {
31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
time_t tod;
time_t Julian, oJulian;
int i;
struct tm tm;
/* XXX Y2K */
if (Year < 0)
@ -644,53 +599,21 @@ Convert(
Year += 2000;
else if (Year < 100)
Year += 1900;
DaysInMonth[1] = isLeap(Year) ? 29 : 28;
if (Year < EPOCH || Month < 1 || Month > 12
/* Lint fluff: "conversion from long may lose accuracy" */
|| Day < 1 || Day > DaysInMonth[(int)--Month])
/* FIXME:
* It would be nice to set a global error string here.
* "February 30 is not a valid date" is much more informative than
* "Can't parse date/time: 100 months" when the user input was
* "100 months" and addition resolved that to February 30, for
* example. See rcs2-7 in src/sanity.sh for more. */
return -1;
for (Julian = Day - 1, i = 0; i < Month; i++)
Julian += DaysInMonth[i];
oJulian = Julian;
for (i = EPOCH; i < Year; i++) {
Julian += 365 + isLeap(i);
if (oJulian > Julian)
return -1;
oJulian = Julian;
tm.tm_sec = Seconds;
tm.tm_min = Minutes;
tm.tm_hour = Hours + (Meridian == MERpm ? 12 : 0);
tm.tm_mday = Day;
tm.tm_mon = Month - 1;
tm.tm_year = Year - 1900;
switch (DSTmode) {
case DSTon: tm.tm_isdst = 1; break;
case DSToff: tm.tm_isdst = 0; break;
default: tm.tm_isdst = -1; break;
}
tm.tm_gmtoff = -Timezone;
Julian *= SECSPERDAY;
if (oJulian > Julian)
return -1;
oJulian = Julian;
Julian += Timezone * 60L;
if (Timezone > 0 && oJulian > Julian)
return -1;
oJulian = Julian;
if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
return -1;
Julian += tod;
if (oJulian > Julian)
return -1;
if (DSTmode == DSTon || (DSTmode == DSTmaybe)) {
struct tm *tm;
if ((tm = localtime(&Julian)) == NULL)
return -1;
if (tm->tm_isdst)
Julian -= 60 * 60;
}
return Julian;
return mktime(&tm);
}
@ -1008,7 +931,7 @@ parsedate(const char *p, const time_t *now, const int *zone)
Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
param.yyMinutes, param.yySeconds, param.yyTimezone,
param.yyMeridian, param.yyDSTmode);
if (Start < 0)
if (Start == -1)
return -1;
}
else {