Date performance fixes, bug 578259. r=waldo

This commit is contained in:
Brian Hackett
2010-08-17 10:42:57 -07:00
parent 2c0d58fd7d
commit 1a16221ebd
6 changed files with 277 additions and 296 deletions

View File

@@ -98,16 +98,12 @@ extern int gettimeofday(struct timeval *tv);
#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS)
#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
/* function prototypes */
static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm);
/*
* get the difference in seconds between this time zone and UTC (GMT)
*/
JSInt32
PRMJ_LocalGMTDifference()
{
struct tm ltime;
#if defined(XP_WIN) && !defined(WINCE)
/* Windows does not follow POSIX. Updates to the
* TZ environment variable are not reflected
@@ -116,11 +112,31 @@ PRMJ_LocalGMTDifference()
*/
_tzset();
#endif
/* get the difference between this time zone and GMT */
memset((char *)&ltime,0,sizeof(ltime));
ltime.tm_mday = 2;
ltime.tm_year = 70;
return (JSInt32)mktime(&ltime) - (24L * 3600L);
/*
* Get the difference between this time zone and GMT, by checking the local
* time at the epoch.
*/
time_t local = 0;
struct tm tm;
#ifndef HAVE_LOCALTIME_R
struct tm *ptm = localtime(&local);
if (!ptm)
return 0;
tm = *ptm;
#else
localtime_r(&local, &tm);
#endif
JSInt32 time = (tm.tm_hour * 3600)
+ (tm.tm_min * 60)
+ tm.tm_sec;
time = (24 * 3600) - time;
if (time >= (12 * 3600))
time -= (24 * 3600);
return time;
}
/* Constants for GMT offset from 1970 */
@@ -689,170 +705,6 @@ static int mtab[] = {
31,31,30,31,30,31
};
/*
* basic time calculation functionality for localtime and gmtime
* setups up prtm argument with correct values based upon input number
* of seconds.
*/
static void
PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)
{
/* convert tsecs back to year,month,day,hour,secs */
JSInt32 year = 0;
JSInt32 month = 0;
JSInt32 yday = 0;
JSInt32 mday = 0;
JSInt32 wday = 6; /* start on a Sunday */
JSInt32 days = 0;
JSInt32 seconds = 0;
JSInt32 minutes = 0;
JSInt32 hours = 0;
JSInt32 isleap = 0;
/* Temporaries used for various computations */
JSInt64 result;
JSInt64 result1;
JSInt64 result2;
JSInt64 base;
/* Some variables for intermediate result storage to make computing isleap
easier/faster */
JSInt32 fourCenturyBlocks;
JSInt32 centuriesLeft;
JSInt32 fourYearBlocksLeft;
JSInt32 yearsLeft;
/* Since leap years work by 400/100/4 year intervals, precompute the length
of those in seconds if they start at the beginning of year 1. */
JSInt64 fourYears;
JSInt64 century;
JSInt64 fourCenturies;
JSLL_UI2L(result, PRMJ_DAY_SECONDS);
JSLL_I2L(fourYears, PRMJ_FOUR_YEARS_DAYS);
JSLL_MUL(fourYears, fourYears, result);
JSLL_I2L(century, PRMJ_CENTURY_DAYS);
JSLL_MUL(century, century, result);
JSLL_I2L(fourCenturies, PRMJ_FOUR_CENTURIES_DAYS);
JSLL_MUL(fourCenturies, fourCenturies, result);
/* get the base time via UTC */
base = PRMJ_ToExtendedTime(0);
JSLL_UI2L(result, PRMJ_USEC_PER_SEC);
JSLL_DIV(base,base,result);
JSLL_ADD(tsecs,tsecs,base);
/* Compute our |year|, |isleap|, and part of |days|. When this part is
done, |year| should hold the year our date falls in (number of whole
years elapsed before our date), isleap should hold 1 if the year the
date falls in is a leap year and 0 otherwise. */
/* First do year 0; it's special and nonleap. */
JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
if (!JSLL_CMP(tsecs,<,result)) {
days = PRMJ_YEAR_DAYS;
year = 1;
JSLL_SUB(tsecs, tsecs, result);
}
/* Now use those constants we computed above */
JSLL_UDIVMOD(&result1, &result2, tsecs, fourCenturies);
JSLL_L2I(fourCenturyBlocks, result1);
year += fourCenturyBlocks * 400;
days += fourCenturyBlocks * PRMJ_FOUR_CENTURIES_DAYS;
tsecs = result2;
JSLL_UDIVMOD(&result1, &result2, tsecs, century);
JSLL_L2I(centuriesLeft, result1);
year += centuriesLeft * 100;
days += centuriesLeft * PRMJ_CENTURY_DAYS;
tsecs = result2;
JSLL_UDIVMOD(&result1, &result2, tsecs, fourYears);
JSLL_L2I(fourYearBlocksLeft, result1);
year += fourYearBlocksLeft * 4;
days += fourYearBlocksLeft * PRMJ_FOUR_YEARS_DAYS;
tsecs = result2;
/* Recall that |result| holds PRMJ_YEAR_SECONDS */
JSLL_UDIVMOD(&result1, &result2, tsecs, result);
JSLL_L2I(yearsLeft, result1);
year += yearsLeft;
days += yearsLeft * PRMJ_YEAR_DAYS;
tsecs = result2;
/* now compute isleap. Note that we don't have to use %, since we've
already computed those remainders. Also note that they're all offset by
1 because of the 1 for year 0. */
isleap =
(yearsLeft == 3) && (fourYearBlocksLeft != 24 || centuriesLeft == 3);
JS_ASSERT(isleap ==
((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)));
JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
JSLL_DIV(result,tsecs,result1);
JSLL_L2I(mday,result);
/* let's find the month */
while(((month == 1 && isleap) ?
(mday >= mtab[month] + 1) :
(mday >= mtab[month]))){
yday += mtab[month];
days += mtab[month];
mday -= mtab[month];
/* it's a Feb, check if this is a leap year */
if(month == 1 && isleap != 0){
yday++;
days++;
mday--;
}
month++;
}
/* now adjust tsecs */
JSLL_MUL(result,result,result1);
JSLL_SUB(tsecs,tsecs,result);
mday++; /* day of month always start with 1 */
days += mday;
wday = (days + wday) % 7;
yday += mday;
/* get the hours */
JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);
JSLL_DIV(result,tsecs,result1);
JSLL_L2I(hours,result);
JSLL_MUL(result,result,result1);
JSLL_SUB(tsecs,tsecs,result);
/* get minutes */
JSLL_UI2L(result1,60);
JSLL_DIV(result,tsecs,result1);
JSLL_L2I(minutes,result);
JSLL_MUL(result,result,result1);
JSLL_SUB(tsecs,tsecs,result);
JSLL_L2I(seconds,tsecs);
prtm->tm_usec = 0L;
prtm->tm_sec = (JSInt8)seconds;
prtm->tm_min = (JSInt8)minutes;
prtm->tm_hour = (JSInt8)hours;
prtm->tm_mday = (JSInt8)mday;
prtm->tm_mon = (JSInt8)month;
prtm->tm_wday = (JSInt8)wday;
prtm->tm_year = (JSInt16)year;
prtm->tm_yday = (JSInt16)yday;
}
JSInt64
DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds)
{
@@ -869,9 +721,7 @@ DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds)
#endif
time_t local = static_cast<time_t>(localTimeSeconds);
PRMJTime prtm;
struct tm tm;
PRMJ_basetime(localTimeSeconds, &prtm);
#ifndef HAVE_LOCALTIME_R
struct tm *ptm = localtime(&local);
if (!ptm)
@@ -881,8 +731,13 @@ DSTOffsetCache::computeDSTOffsetMilliseconds(int64 localTimeSeconds)
localtime_r(&local, &tm); /* get dst information */
#endif
JSInt32 diff = ((tm.tm_hour - prtm.tm_hour) * SECONDS_PER_HOUR) +
((tm.tm_min - prtm.tm_min) * SECONDS_PER_MINUTE);
JSInt32 base = PRMJ_LocalGMTDifference();
int32 dayoff = int32((localTimeSeconds - base) % (SECONDS_PER_HOUR * 24));
int32 tmoff = tm.tm_sec + (tm.tm_min * SECONDS_PER_MINUTE) +
(tm.tm_hour * SECONDS_PER_HOUR);
JSInt32 diff = tmoff - dayoff;
if (diff < 0)
diff += SECONDS_PER_DAY;
@@ -911,12 +766,23 @@ DSTOffsetCache::getDSTOffsetMilliseconds(JSInt64 localTimeMilliseconds, JSContex
* values, must result in a cache miss.
*/
if (rangeStartSeconds <= localTimeSeconds) {
if (localTimeSeconds <= rangeEndSeconds) {
noteCacheHit();
return offsetMilliseconds;
}
if (rangeStartSeconds <= localTimeSeconds &&
localTimeSeconds <= rangeEndSeconds) {
noteCacheHit();
return offsetMilliseconds;
}
if (oldRangeStartSeconds <= localTimeSeconds &&
localTimeSeconds <= oldRangeEndSeconds) {
noteCacheHit();
return oldOffsetMilliseconds;
}
oldOffsetMilliseconds = offsetMilliseconds;
oldRangeStartSeconds = rangeStartSeconds;
oldRangeEndSeconds = rangeEndSeconds;
if (rangeStartSeconds <= localTimeSeconds) {
JSInt64 newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET);
if (newEndSeconds >= localTimeSeconds) {
JSInt64 endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds);