PR/42549: Izumi Tsutsui: parsedate does not work after 2038.

Fix multiple issues:
- Remove bogus 2038 check and add overflow checks in the appropriate places.
- Correct incomplete leap year calculation that broke things after 2100.
- Check localtime return values
- Change int calculations to time_t to avoid oveflow.
- Consistently check/return -1 and remove bogus comment about not being
  able to return -1.

Now:
$ date -d 20991201
Tue Dec  1 00:00:00 EST 2099
$ date -d 40991201
Tue Dec  1 00:00:00 EST 4099
$ date -d 10000000991201
Tue Dec  1 00:00:00 EST 1000000099
TIME=0:04.48 CPU=117.8% (5.288u 0.000s) SWAPS=0 (0+95)pf (0i+0o) (0Kc+0Kd)
$ date -d 100000000991201
date: Cannot parse `100000000991201'
TIME=0:53.48 CPU=99.2% (53.086u 0.000s) SWAPS=0 (0+96)pf (0i+0o) (0Kc+0Kd)
Exit 1
This commit is contained in:
christos 2010-12-12 18:39:57 +00:00
parent 4e63731664
commit 49f71b9cd5

View File

@ -586,6 +586,12 @@ ToSeconds(
/* 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?)
@ -607,7 +613,7 @@ Convert(
31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
time_t tod;
time_t Julian;
time_t Julian, oJulian;
int i;
if (Year < 0)
@ -616,12 +622,8 @@ Convert(
Year += 2000;
else if (Year < 100)
Year += 1900;
DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
? 29 : 28;
/* Checking for 2038 bogusly assumes that time_t is 32 bits. But
I'm too lazy to try to check for time_t overflow in another way. */
if (Year < EPOCH || Year > 2038
|| Month < 1 || Month > 12
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:
@ -634,16 +636,39 @@ Convert(
for (Julian = Day - 1, i = 0; i < Month; i++)
Julian += DaysInMonth[i];
for (i = EPOCH; i < Year; i++)
Julian += 365 + (i % 4 == 0);
oJulian = Julian;
for (i = EPOCH; i < Year; i++) {
Julian += 365 + isLeap(i);
if (oJulian > Julian)
return -1;
oJulian = Julian;
}
Julian *= SECSPERDAY;
if (oJulian > Julian)
return -1;
oJulian = Julian;
Julian += yyTimezone * 60L;
if (oJulian > Julian)
return -1;
oJulian = Julian;
if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
return -1;
Julian += tod;
if (DSTmode == DSTon
|| (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
Julian -= 60 * 60;
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;
}
@ -656,9 +681,16 @@ DSTcorrect(
{
time_t StartDay;
time_t FutureDay;
struct tm *tm;
if ((tm = localtime(&Start)) == NULL)
return -1;
StartDay = (tm->tm_hour + 1) % 24;
if ((tm = localtime(&Future)) == NULL)
return -1;
FutureDay = (tm->tm_hour + 1) % 24;
StartDay = (localtime(&Start)->tm_hour + 1) % 24;
FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
}
@ -694,6 +726,8 @@ RelativeMonth(
if (RelMonth == 0)
return 0;
tm = localtime(&Start);
if (tm == NULL)
return -1;
Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
Year = Month / 12;
Month = Month % 12 + 1;
@ -869,7 +903,7 @@ yylex(void)
#define TM_YEAR_ORIGIN 1900
/* Yield A - B, measured in seconds. */
static long
static time_t
difftm (struct tm *a, struct tm *b)
{
int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
@ -884,7 +918,7 @@ difftm (struct tm *a, struct tm *b)
/* + difference in years * 365 */
+ (long)(ay-by) * 365
);
return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
return ((time_t)60*(60*(24*days + (a->tm_hour - b->tm_hour))
+ (a->tm_min - b->tm_min))
+ (a->tm_sec - b->tm_sec));
}
@ -896,7 +930,7 @@ parsedate(const char *p, const time_t *now, const int *zone)
time_t nowt;
int zonet;
time_t Start;
time_t tod;
time_t tod, rm;
yyInput = p;
if (now == NULL || zone == NULL) {
@ -958,16 +992,17 @@ parsedate(const char *p, const time_t *now, const int *zone)
}
Start += yyRelSeconds;
Start += RelativeMonth(Start, yyRelMonth);
rm = RelativeMonth(Start, yyRelMonth);
if (rm == -1)
return -1;
Start += rm;
if (yyHaveDay && !yyHaveDate) {
tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
Start += tod;
}
/* Have to do *something* with a legitimate -1 so it's distinguishable
* from the error return value. (Alternately could set errno on error.) */
return Start == -1 ? 0 : Start;
return Start;
}