diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e396fb7ab2b9..39959ff7a476 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6409,6 +6409,14 @@ JS_ObjectIsDate(JSContext *cx, JSObject *obj) return obj->isDate(); } +JS_PUBLIC_API(void) +JS_ClearDateCaches(JSContext *cx) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + js_ClearDateCaches(); +} + /************************************************************************/ /* diff --git a/js/src/jsapi.h b/js/src/jsapi.h index da1031b247ac..a3777804b5c2 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5728,6 +5728,13 @@ JS_NewDateObjectMsec(JSContext *cx, double msec); extern JS_PUBLIC_API(JSBool) JS_ObjectIsDate(JSContext *cx, JSObject *obj); +/* + * Clears the cache of calculated local time from each Date object. + * Call to propagate a system timezone change. + */ +extern JS_PUBLIC_API(void) +JS_ClearDateCaches(JSContext *cx); + /************************************************************************/ /* diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 2636f5d4fa68..423527e52022 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -128,6 +128,14 @@ using namespace js::types; * general going-over. */ +/* Constants defined by 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; + /* ES5 15.9.1.2. */ const double msPerDay = 86400000; @@ -310,8 +318,14 @@ WeekDay(double t) return result; } -/* ES5 15.9.1.7. */ -static double LocalTZA; // set by js_InitDateClass +/* ES5 15.9.1.7. Set by UpdateLocalTZA(). */ +static double LocalTZA; + +inline void +UpdateLocalTZA() +{ + LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); +} inline int DayFromMonth(int month, bool isLeapYear) @@ -450,13 +464,6 @@ UTC(double t, JSContext *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 double HourFromTime(double t) { @@ -1288,10 +1295,20 @@ SetDateToNaN(JSContext *cx, JSObject *obj, Value *vp = NULL) * slots will be set to the UTC time without conversion. */ static bool -FillLocalTimes(JSContext *cx, JSObject *obj) +CacheLocalTime(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isDate()); + /* Check if the cache is already populated. */ + if (!obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).isUndefined() && + obj->getSlot(JSObject::JSSLOT_DATE_TZA).toDouble() == LocalTZA) + { + return true; + } + + /* Remember timezone used to generate the local cache. */ + obj->setSlot(JSObject::JSSLOT_DATE_TZA, DoubleValue(LocalTZA)); + double utcTime = obj->getDateUTCTime().toNumber(); if (!MOZ_DOUBLE_IS_FINITE(utcTime)) { @@ -1400,42 +1417,24 @@ FillLocalTimes(JSContext *cx, JSObject *obj) obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DATE, Int32Value(day - step)); int weekday = WeekDay(localTime); - obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DAY, Int32Value(weekday)); int seconds = yearSeconds % 60; - obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS, Int32Value(seconds)); int minutes = (yearSeconds / 60) % 60; - obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES, Int32Value(minutes)); int hours = (yearSeconds / (60 * 60)) % 24; - obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS, Int32Value(hours)); return true; } -/* Cache the local times in obj, if necessary. */ -static inline bool -GetAndCacheLocalTime(JSContext *cx, JSObject *obj) +inline bool +GetCachedLocalTime(JSContext *cx, JSObject *obj, double *time) { - JS_ASSERT(obj->isDate()); - - /* If the local time is undefined, we need to fill in the cached values. */ - if (obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).isUndefined()) { - if (!FillLocalTimes(cx, obj)) - return false; - } - return true; -} - -static inline bool -GetAndCacheLocalTime(JSContext *cx, JSObject *obj, double *time) -{ - if (!obj || !GetAndCacheLocalTime(cx, obj)) + if (!obj || !CacheLocalTime(cx, obj)) return false; *time = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).toDouble(); @@ -1471,7 +1470,7 @@ date_getYear(JSContext *cx, unsigned argc, Value *vp) if (!thisObj) return true; - if (!GetAndCacheLocalTime(cx, thisObj)) + if (!CacheLocalTime(cx, thisObj)) return false; Value yearVal = thisObj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR); @@ -1497,7 +1496,7 @@ date_getFullYear(JSContext *cx, unsigned argc, Value *vp) if (!thisObj) return true; - if (!GetAndCacheLocalTime(cx, thisObj)) + if (!CacheLocalTime(cx, thisObj)) return false; args.rval() = thisObj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR); @@ -1534,7 +1533,7 @@ date_getMonth(JSContext *cx, unsigned argc, Value *vp) if (!thisObj) return true; - if (!GetAndCacheLocalTime(cx, thisObj)) + if (!CacheLocalTime(cx, thisObj)) return false; args.rval() = thisObj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH); @@ -1568,7 +1567,7 @@ date_getDate(JSContext *cx, unsigned argc, Value *vp) if (!thisObj) return true; - if (!GetAndCacheLocalTime(cx, thisObj)) + if (!CacheLocalTime(cx, thisObj)) return false; args.rval() = thisObj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE); @@ -1605,7 +1604,7 @@ date_getDay(JSContext *cx, unsigned argc, Value *vp) if (!thisObj) return true; - if (!GetAndCacheLocalTime(cx, thisObj)) + if (!CacheLocalTime(cx, thisObj)) return false; args.rval() = thisObj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY); @@ -1642,7 +1641,7 @@ date_getHours(JSContext *cx, unsigned argc, Value *vp) if (!thisObj) return true; - if (!GetAndCacheLocalTime(cx, thisObj)) + if (!CacheLocalTime(cx, thisObj)) return false; args.rval() = thisObj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS); @@ -1679,7 +1678,7 @@ date_getMinutes(JSContext *cx, unsigned argc, Value *vp) if (!thisObj) return true; - if (!GetAndCacheLocalTime(cx, thisObj)) + if (!CacheLocalTime(cx, thisObj)) return false; args.rval() = thisObj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES); @@ -1718,7 +1717,7 @@ date_getUTCSeconds(JSContext *cx, unsigned argc, Value *vp) if (!thisObj) return true; - if (!GetAndCacheLocalTime(cx, thisObj)) + if (!CacheLocalTime(cx, thisObj)) return false; args.rval() = thisObj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS); @@ -1760,7 +1759,7 @@ date_getTimezoneOffset(JSContext *cx, unsigned argc, Value *vp) double utctime = thisObj->getDateUTCTime().toNumber(); double localtime; - if (!GetAndCacheLocalTime(cx, thisObj, &localtime)) + if (!GetCachedLocalTime(cx, thisObj, &localtime)) return false; /* @@ -3069,8 +3068,7 @@ js_InitDateClass(JSContext *cx, JSObject *obj) { JS_ASSERT(obj->isNative()); - /* Set the static LocalTZA. */ - LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); + UpdateLocalTZA(); Rooted global(cx, &obj->asGlobal()); @@ -3136,6 +3134,12 @@ js_NewDateObject(JSContext* cx, int year, int mon, int mday, return obj; } +void +js_ClearDateCaches() +{ + UpdateLocalTZA(); +} + JS_FRIEND_API(JSBool) js_DateIsValid(JSContext *cx, JSObject* obj) { @@ -3148,7 +3152,7 @@ js_DateGetYear(JSContext *cx, JSObject* obj) double localtime; /* Preserve legacy API behavior of returning 0 for invalid dates. */ - if (!GetAndCacheLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) + if (!GetCachedLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) return 0; return (int) YearFromTime(localtime); @@ -3159,7 +3163,7 @@ js_DateGetMonth(JSContext *cx, JSObject* obj) { double localtime; - if (!GetAndCacheLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) + if (!GetCachedLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) return 0; return (int) MonthFromTime(localtime); @@ -3170,7 +3174,7 @@ js_DateGetDate(JSContext *cx, JSObject* obj) { double localtime; - if (!GetAndCacheLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) + if (!GetCachedLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) return 0; return (int) DateFromTime(localtime); @@ -3181,7 +3185,7 @@ js_DateGetHours(JSContext *cx, JSObject* obj) { double localtime; - if (!GetAndCacheLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) + if (!GetCachedLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) return 0; return (int) HourFromTime(localtime); @@ -3192,7 +3196,7 @@ js_DateGetMinutes(JSContext *cx, JSObject* obj) { double localtime; - if (!GetAndCacheLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) + if (!GetCachedLocalTime(cx, obj, &localtime) || MOZ_DOUBLE_IS_NaN(localtime)) return 0; return (int) MinFromTime(localtime); diff --git a/js/src/jsdate.h b/js/src/jsdate.h index cf0f35e804c2..3c0740b63ac5 100644 --- a/js/src/jsdate.h +++ b/js/src/jsdate.h @@ -65,6 +65,9 @@ extern JS_FRIEND_API(JSObject*) js_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec); +extern void +js_ClearDateCaches(); + extern JS_FRIEND_API(int) js_DateGetYear(JSContext *cx, JSObject* obj); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index f7083652e3ac..83866d8fb89d 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -616,24 +616,25 @@ struct JSObject : public js::ObjectImpl */ static const uint32_t JSSLOT_DATE_UTC_TIME = 0; + static const uint32_t JSSLOT_DATE_TZA = 1; /* * Cached slots holding local properties of the date. * These are undefined until the first actual lookup occurs * and are reset to undefined whenever the date's time is modified. */ - static const uint32_t JSSLOT_DATE_COMPONENTS_START = 1; + static const uint32_t JSSLOT_DATE_COMPONENTS_START = 2; - static const uint32_t JSSLOT_DATE_LOCAL_TIME = 1; - static const uint32_t JSSLOT_DATE_LOCAL_YEAR = 2; - static const uint32_t JSSLOT_DATE_LOCAL_MONTH = 3; - static const uint32_t JSSLOT_DATE_LOCAL_DATE = 4; - static const uint32_t JSSLOT_DATE_LOCAL_DAY = 5; - static const uint32_t JSSLOT_DATE_LOCAL_HOURS = 6; - static const uint32_t JSSLOT_DATE_LOCAL_MINUTES = 7; - static const uint32_t JSSLOT_DATE_LOCAL_SECONDS = 8; + static const uint32_t JSSLOT_DATE_LOCAL_TIME = JSSLOT_DATE_COMPONENTS_START + 0; + static const uint32_t JSSLOT_DATE_LOCAL_YEAR = JSSLOT_DATE_COMPONENTS_START + 1; + static const uint32_t JSSLOT_DATE_LOCAL_MONTH = JSSLOT_DATE_COMPONENTS_START + 2; + static const uint32_t JSSLOT_DATE_LOCAL_DATE = JSSLOT_DATE_COMPONENTS_START + 3; + static const uint32_t JSSLOT_DATE_LOCAL_DAY = JSSLOT_DATE_COMPONENTS_START + 4; + static const uint32_t JSSLOT_DATE_LOCAL_HOURS = JSSLOT_DATE_COMPONENTS_START + 5; + static const uint32_t JSSLOT_DATE_LOCAL_MINUTES = JSSLOT_DATE_COMPONENTS_START + 6; + static const uint32_t JSSLOT_DATE_LOCAL_SECONDS = JSSLOT_DATE_COMPONENTS_START + 7; - static const uint32_t DATE_CLASS_RESERVED_SLOTS = 9; + static const uint32_t DATE_CLASS_RESERVED_SLOTS = JSSLOT_DATE_LOCAL_SECONDS + 1; inline const js::Value &getDateUTCTime() const; inline void setDateUTCTime(const js::Value &pthis);