Bug 771742 - Reorganize all the date/time spec algorithms and constants to be in spec order (when possible), have proper types, be methods rather than macros, and so on. This will make it easier to refactor some of the Date method implementations to read more like the spec algorithms. r=luke

This commit is contained in:
Jeff Walden
2012-07-06 13:52:53 -07:00
parent 8e7599c3a8
commit ae3ee934cc

View File

@@ -128,76 +128,58 @@ using namespace js::types;
* general going-over.
*/
/*
* Supporting functions - ECMA 15.9.1.*
*/
/* ES5 15.9.1.2. */
const double msPerDay = 86400000;
#define HoursPerDay 24.0
#define MinutesPerDay (HoursPerDay * MinutesPerHour)
#define MinutesPerHour 60.0
#define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
#define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
#define SecondsPerMinute 60.0
#if defined(XP_WIN) || defined(XP_OS2)
/* Work around msvc double optimization bug by making these runtime values; if
* they're available at compile time, msvc optimizes division by them by
* computing the reciprocal and multiplying instead of dividing - this loses
* when the reciprocal isn't representable in a double.
*/
static double msPerSecond = 1000.0;
static double msPerDay = SecondsPerDay * 1000.0;
static double msPerHour = SecondsPerHour * 1000.0;
static double msPerMinute = SecondsPerMinute * 1000.0;
#else
#define msPerDay (SecondsPerDay * msPerSecond)
#define msPerHour (SecondsPerHour * msPerSecond)
#define msPerMinute (SecondsPerMinute * msPerSecond)
#define msPerSecond 1000.0
#endif
#define Day(t) floor((t) / msPerDay)
inline double
Day(double t)
{
return floor(t / msPerDay);
}
static double
TimeWithinDay(double t)
{
double result;
result = fmod(t, msPerDay);
double result = fmod(t, msPerDay);
if (result < 0)
result += msPerDay;
return result;
}
static inline bool
/* ES5 15.9.1.3. */
inline bool
IsLeapYear(int year)
{
return year % 4 == 0 && (year % 100 || (year % 400 == 0));
}
static inline int
inline int
DaysInYear(int year)
{
return IsLeapYear(year) ? 366 : 365;
}
static inline int
DaysInFebruary(int year)
inline int
DayFromYear(int y)
{
return IsLeapYear(year) ? 29 : 28;
/* This is floating-point math so that floor((1968 - 1969) / 4) == -1. */
return 365 * (y - 1970) +
floor((y - 1969) / 4.0) -
floor((y - 1901) / 100.0) +
floor((y - 1601) / 400.0);
}
/* math here has to be f.p, because we need
* floor((1968 - 1969) / 4) == -1
*/
#define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
- floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
#define TimeFromYear(y) (DayFromYear(y) * msPerDay)
inline double
TimeFromYear(int y)
{
return DayFromYear(y) * msPerDay;
}
static int
YearFromTime(double t)
{
int y = (int) floor(t /(msPerDay*365.2425)) + 1970;
double t2 = (double) TimeFromYear(y);
int y = (int) floor(t / (msPerDay * 365.2425)) + 1970;
double t2 = TimeFromYear(y);
/*
* Adjust the year if the approximation was wrong. Since the year was
@@ -213,34 +195,27 @@ YearFromTime(double t)
return y;
}
#define DayWithinYear(t, year) ((int) (Day(t) - DayFromYear(year)))
/*
* The following array contains the day of year for the first day of
* each month, where index 0 is January, and day 0 is January 1.
*/
static double firstDayOfMonth[2][13] = {
{0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0, 365.0},
{0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0, 366.0}
};
#define DayFromMonth(m, leap) firstDayOfMonth[leap][(int)m]
static int
DaysInMonth(int year, int month)
inline int
DaysInFebruary(int year)
{
JSBool leap = IsLeapYear(year);
int result = int(DayFromMonth(month, leap) - DayFromMonth(month-1, leap));
return result;
return IsLeapYear(year) ? 29 : 28;
}
/* ES5 15.9.1.4. */
inline int
DayWithinYear(double t, int year)
{
JS_ASSERT(YearFromTime(t) == year);
return int(Day(t) - DayFromYear(year));
}
static int
MonthFromTime(double t)
{
int d, step;
int year = YearFromTime(t);
d = DayWithinYear(t, year);
int d = DayWithinYear(t, year);
int step;
if (d < (step = 31))
return 0;
if (d < (step += DaysInFebruary(year)))
@@ -266,16 +241,17 @@ MonthFromTime(double t)
return 11;
}
/* ES5 15.9.1.5. */
static int
DateFromTime(double t)
{
int d, step, next;
int year = YearFromTime(t);
d = DayWithinYear(t, year);
int d = DayWithinYear(t, year);
int next;
if (d <= (next = 30))
return d + 1;
step = next;
int step = next;
if (d <= (next += DaysInFebruary(year)))
return d - step;
step = next;
@@ -309,56 +285,18 @@ DateFromTime(double t)
return d - step;
}
/* ES5 15.9.1.6. */
static int
WeekDay(double t)
{
int result;
result = (int) Day(t) + 4;
result = result % 7;
int result = (int(Day(t)) + 4) % 7;
if (result < 0)
result += 7;
return (int) result;
return result;
}
#define MakeTime(hour, min, sec, ms) \
((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
static double
MakeDay(double year, double month, double date)
{
JSBool leap;
double yearday;
double monthday;
year += floor(month / 12);
month = fmod(month, 12.0);
if (month < 0)
month += 12;
leap = IsLeapYear((int) year);
yearday = floor(TimeFromYear(year) / msPerDay);
monthday = DayFromMonth(month, leap);
return yearday + monthday + date - 1;
}
#define MakeDate(day, time) ((day) * msPerDay + (time))
/*
* Years and leap years on which Jan 1 is a Sunday, Monday, etc.
*
* yearStartingWith[0][i] is an example non-leap year where
* Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
*
* yearStartingWith[1][i] is an example leap year where
* Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
*/
static int yearStartingWith[2][7] = {
{1978, 1973, 1974, 1975, 1981, 1971, 1977},
{1984, 1996, 1980, 1992, 1976, 1988, 1972}
};
/* ES5 15.9.1.7. */
static double LocalTZA; // set by js_InitDateClass
/*
* Find a year for which any given date will fall on the same weekday.
@@ -370,25 +308,86 @@ static int yearStartingWith[2][7] = {
static int
EquivalentYearForDST(int year)
{
int day;
/*
* Years and leap years on which Jan 1 is a Sunday, Monday, etc.
*
* yearStartingWith[0][i] is an example non-leap year where
* Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
*
* yearStartingWith[1][i] is an example leap year where
* Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
*/
static const int yearStartingWith[2][7] = {
{1978, 1973, 1974, 1975, 1981, 1971, 1977},
{1984, 1996, 1980, 1992, 1976, 1988, 1972}
};
day = (int) DayFromYear(year) + 4;
day = day % 7;
int day = int(DayFromYear(year) + 4) % 7;
if (day < 0)
day += 7;
return yearStartingWith[IsLeapYear(year)][day];
}
/* LocalTZA gets set by js_InitDateClass() */
static double LocalTZA;
inline int
DayFromMonth(int month, bool isLeapYear)
{
/*
* The following array contains the day of year for the first day of
* each month, where index 0 is January, and day 0 is January 1.
*/
static const int firstDayOfMonth[2][13] = {
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
};
JS_ASSERT(0 <= month && month <= 12);
return firstDayOfMonth[isLeapYear][month];
}
/* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
static double
MakeDay(double year, double month, double date)
{
if (!MOZ_DOUBLE_IS_FINITE(year) || !MOZ_DOUBLE_IS_FINITE(month) || !MOZ_DOUBLE_IS_FINITE(date))
return js_NaN;
JS_ASSERT(ToInteger(year) == year);
JS_ASSERT(ToInteger(month) == month);
JS_ASSERT(ToInteger(date) == date);
year += floor(month / 12);
month = fmod(month, 12.0);
if (month < 0)
month += 12;
bool leap = IsLeapYear((int) year);
double yearday = floor(TimeFromYear(year) / msPerDay);
double monthday = DayFromMonth(month, leap);
return yearday + monthday + date - 1;
}
/* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
inline double
MakeDate(double day, double time)
{
/* Step 1. */
if (!MOZ_DOUBLE_IS_FINITE(day) || !MOZ_DOUBLE_IS_FINITE(time))
return js_NaN;
/* Step 2. */
return day * msPerDay + time;
}
/* ES5 15.9.1.8. */
static double
DaylightSavingTA(double t, JSContext *cx)
{
/* abort if NaN */
if (MOZ_DOUBLE_IS_NaN(t))
return t;
if (!MOZ_DOUBLE_IS_FINITE(t))
return js_NaN;
/*
* If earlier than 1970 or after 2038, potentially beyond the ken of
@@ -413,6 +412,7 @@ AdjustTime(double date, JSContext *cx)
return t;
}
/* ES5 15.9.1.9. */
static double
LocalTime(double t, JSContext *cx)
{
@@ -425,12 +425,20 @@ UTC(double t, JSContext *cx)
return t - AdjustTime(t - LocalTZA, cx);
}
/* ES5 15.9.1.10. */
const double HoursPerDay = 24.0;
const double MinutesPerHour = 60;
const double SecondsPerMinute = 60;
const double msPerSecond = 1000;
const double msPerMinute = msPerSecond * SecondsPerMinute;
const double msPerHour = msPerMinute * MinutesPerHour;
static int
HourFromTime(double t)
{
int result = (int) fmod(floor(t/msPerHour), HoursPerDay);
if (result < 0)
result += (int)HoursPerDay;
result += int(HoursPerDay);
return result;
}
@@ -439,7 +447,7 @@ MinFromTime(double t)
{
int result = (int) fmod(floor(t / msPerMinute), MinutesPerHour);
if (result < 0)
result += (int)MinutesPerHour;
result += int(MinutesPerHour);
return result;
}
@@ -448,7 +456,7 @@ SecFromTime(double t)
{
int result = (int) fmod(floor(t / msPerSecond), SecondsPerMinute);
if (result < 0)
result += (int)SecondsPerMinute;
result += int(SecondsPerMinute);
return result;
}
@@ -457,7 +465,48 @@ msFromTime(double t)
{
int result = (int) fmod(t, msPerSecond);
if (result < 0)
result += (int)msPerSecond;
result += int(msPerSecond);
return result;
}
/* ES5 15.9.1.11. */
static double
MakeTime(double hour, double min, double sec, double ms)
{
/* Step 1. */
if (!MOZ_DOUBLE_IS_FINITE(hour) ||
!MOZ_DOUBLE_IS_FINITE(min) ||
!MOZ_DOUBLE_IS_FINITE(sec) ||
!MOZ_DOUBLE_IS_FINITE(ms))
{
return js_NaN;
}
/* Step 2. */
double h = ToInteger(hour);
/* Step 3. */
double m = ToInteger(min);
/* Step 4. */
double s = ToInteger(sec);
/* Step 5. */
double milli = ToInteger(ms);
/* Steps 6-7. */
return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
}
/* Additional quantities not mentioned in the spec. */
const double SecondsPerDay = SecondsPerMinute * MinutesPerHour * HoursPerDay;
/* Additional methods not mentioned in the spec. */
static int
DaysInMonth(int year, int month)
{
bool leap = IsLeapYear(year);
int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap));
return result;
}