Pick up the current system timezone when a Date object is created (492056, r=mrbkap).
☠☠ backed out by 4548b8be232f ☠ ☠
authorMarco Pesenti Gritti <marco@marcopg.org>
Wed, 03 Jun 2009 11:16:57 -0400
changeset 29783 4ceba37b3b22
parent 29782 6107ad66e56d
child 29784 ca23d3b5a999
child 29785 4548b8be232f
push id106
push userhsivonen@iki.fi
push dateSun, 28 Jun 2009 17:44:42 +0000
reviewersmrbkap
bugs492056
milestone1.9.2a1pre
Pick up the current system timezone when a Date object is created (492056, r=mrbkap).
js/src/jsdate.cpp
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -371,19 +371,16 @@ EquivalentYearForDST(jsint year)
     if (day < 0)
         day += 7;
 
     isLeapYear = (DaysInYear(year) == 366);
 
     return yearStartingWith[isLeapYear][day];
 }
 
-/* LocalTZA gets set by js_InitDateClass() */
-static jsdouble LocalTZA;
-
 static jsdouble
 DaylightSavingTA(jsdouble t)
 {
     volatile int64 PR_t;
     int64 ms2us;
     int64 offset;
     jsdouble result;
 
@@ -411,32 +408,16 @@ DaylightSavingTA(jsdouble t)
 
     offset = PRMJ_DSTOffset(PR_t);
 
     JSLL_DIV(offset, offset, ms2us);
     JSLL_L2D(result, offset);
     return result;
 }
 
-static jsdouble
-AdjustTime(jsdouble date)
-{
-    jsdouble t = DaylightSavingTA(date) + LocalTZA;
-    t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
-    return t;
-}
-
-#define LocalTime(t)    ((t) + AdjustTime(t))
-
-static jsdouble
-UTC(jsdouble t)
-{
-    return t - AdjustTime(t - LocalTZA);
-}
-
 static intN
 HourFromTime(jsdouble t)
 {
     intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
     if (result < 0)
         result += (intN)HoursPerDay;
     return result;
 }
@@ -481,18 +462,19 @@ msFromTime(jsdouble t)
  */
 
 /*
  * We use the first reseved slot to store UTC time, and the second for caching
  * the local time. The initial value of the cache entry is NaN.
  */
 const uint32 JSSLOT_UTC_TIME    = JSSLOT_PRIVATE;
 const uint32 JSSLOT_LOCAL_TIME  = JSSLOT_PRIVATE + 1;
-
-const uint32 DATE_RESERVED_SLOTS = 2;
+const uint32 JSSLOT_LOCAL_TZA   = JSSLOT_PRIVATE + 2;
+
+const uint32 DATE_RESERVED_SLOTS = 3;
 
 JSClass js_DateClass = {
     js_Date_str,
     JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
@@ -519,16 +501,100 @@ static int ttb[] = {
     2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
     10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */
     10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */
     10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */
     10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */
     10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */
 };
 
+static jsdouble
+LocalTZA()
+{
+    return -(PRMJ_LocalGMTDifference() * msPerSecond);
+}
+
+static JSBool
+GetAndCacheLocalTZA(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
+{
+    jsval v;
+    jsdouble result;
+    jsdouble *cached;
+
+    v = obj->fslots[JSSLOT_LOCAL_TZA];
+
+    result = *JSVAL_TO_DOUBLE(v);
+
+    if (JSDOUBLE_IS_NaN(result)) {
+        result = LocalTZA();
+
+        cached = js_NewWeaklyRootedDouble(cx, result);
+        if (!cached)
+            return JS_FALSE;
+
+        obj->fslots[JSSLOT_LOCAL_TZA] = DOUBLE_TO_JSVAL(cached);
+    }
+
+    *dp = result;
+    return JS_TRUE;
+}
+
+static jsdouble
+AdjustTime(jsdouble date, jsdouble localTZA)
+{
+    jsdouble t = DaylightSavingTA(date) + localTZA;
+    t = (localTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
+    return t;
+}
+
+static JSBool
+GetAdjustTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble t, jsdouble *dp)
+{
+    if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
+        return JS_FALSE;
+
+    jsdouble localTZA;
+    if (!GetAndCacheLocalTZA(cx, obj, vp, &localTZA))
+        return JS_FALSE;
+
+    *dp = AdjustTime(t, localTZA);
+
+    return JS_TRUE;
+}
+
+static JSBool
+ConvertToUTC(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
+{
+    if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
+        return JS_FALSE;
+
+    jsdouble localTZA;
+    if (!GetAndCacheLocalTZA(cx, obj, vp, &localTZA))
+        return JS_FALSE;
+
+    *dp = *dp - AdjustTime(*dp - localTZA, localTZA);
+
+    return JS_TRUE;
+}
+
+static JSBool
+ConvertToLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
+{
+    if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
+        return JS_FALSE;
+
+    jsdouble localTZA;
+    if (!GetAndCacheLocalTZA(cx, obj, vp, &localTZA))
+        return JS_FALSE;
+
+    *dp = *dp + AdjustTime(*dp, localTZA);
+
+    return JS_TRUE;
+}
+
 /* helper for date_parse */
 static JSBool
 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
                    int count, int ignoreCase)
 {
     JSBool result = JS_FALSE;
     /* return true if matches, otherwise, false */
 
@@ -622,17 +688,17 @@ date_UTC(JSContext *cx, uintN argc, jsva
         return JS_FALSE;
 
     msec_time = TIMECLIP(msec_time);
 
     return js_NewNumberInRootedValue(cx, msec_time, vp);
 }
 
 static JSBool
-date_parseString(JSString *str, jsdouble *result)
+date_parseString(JSContext *cx, JSObject *obj, JSString *str, jsdouble *result)
 {
     jsdouble msec;
 
     const jschar *s;
     size_t limit;
     size_t i = 0;
     int year = -1;
     int mon = -1;
@@ -872,20 +938,23 @@ date_parseString(JSString *str, jsdouble
     mon -= 1; /* convert month to 0-based */
     if (sec < 0)
         sec = 0;
     if (min < 0)
         min = 0;
     if (hour < 0)
         hour = 0;
     if (tzoffset == -1) { /* no time zone specified, have to use local */
-        jsdouble msec_time;
-        msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
-
-        *result = UTC(msec_time);
+        *result = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
+        if (obj) {
+            return ConvertToUTC(cx, obj, NULL, result);
+        }
+
+        jsdouble localTZA = LocalTZA();
+        *result = *result - AdjustTime(*result - localTZA, localTZA);
         return JS_TRUE;
     }
 
     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
     msec += tzoffset * msPerMinute;
     *result = msec;
     return JS_TRUE;
 
@@ -904,17 +973,17 @@ date_parse(JSContext *cx, uintN argc, js
     if (argc == 0) {
         *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
         return JS_TRUE;
     }
     str = js_ValueToString(cx, vp[2]);
     if (!str)
         return JS_FALSE;
     vp[2] = STRING_TO_JSVAL(str);
-    if (!date_parseString(str, &result)) {
+    if (!date_parseString(cx, NULL, str, &result)) {
         *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
         return JS_TRUE;
     }
 
     result = TIMECLIP(result);
     return js_NewNumberInRootedValue(cx, result, vp);
 }
 
@@ -1000,17 +1069,18 @@ GetAndCacheLocalTime(JSContext *cx, JSOb
     result = *JSVAL_TO_DOUBLE(v);
 
     if (JSDOUBLE_IS_NaN(result)) {
         if (!GetUTCTime(cx, obj, vp, &result))
             return JS_FALSE;
 
         /* if result is NaN, it couldn't be finite. */
         if (JSDOUBLE_IS_FINITE(result))
-            result = LocalTime(result);
+            if (!ConvertToLocalTime(cx, obj, vp, &result))
+                return JS_FALSE;
 
         cached = js_NewWeaklyRootedDouble(cx, result);
         if (!cached)
             return JS_FALSE;
 
         obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cached);
     }
 
@@ -1340,20 +1410,19 @@ date_makeTime(JSContext *cx, uintN maxar
         args[i] = js_ValueToNumber(cx, &argv[i]);
         if (JSVAL_IS_NULL(argv[i]))
             return JS_FALSE;
         if (!JSDOUBLE_IS_FINITE(args[i]))
             return SetDateToNaN(cx, vp);
         args[i] = js_DoubleToInteger(args[i]);
     }
 
-    if (local)
-        lorutime = LocalTime(result);
-    else
-        lorutime = result;
+    lorutime = result;
+    if (local && !ConvertToLocalTime(cx, obj, vp, &lorutime))
+        return JS_FALSE;
 
     argp = args;
     stop = argp + argc;
     if (maxargs >= 4 && argp < stop)
         hour = *argp++;
     else
         hour = HourFromTime(lorutime);
 
@@ -1370,22 +1439,18 @@ date_makeTime(JSContext *cx, uintN maxar
     if (maxargs >= 1 && argp < stop)
         msec = *argp;
     else
         msec = msFromTime(lorutime);
 
     msec_time = MakeTime(hour, min, sec, msec);
     result = MakeDate(Day(lorutime), msec_time);
 
-/*     fprintf(stderr, "%f\n", result); */
-
-    if (local)
-        result = UTC(result);
-
-/*     fprintf(stderr, "%f\n", result); */
+    if (local && !ConvertToUTC(cx, obj, vp, &result))
+        return JS_FALSE;
 
     result = TIMECLIP(result);
     if (!SetUTCTime(cx, obj, NULL, result))
         return JS_FALSE;
 
     return js_NewNumberInRootedValue(cx, result, vp);
 }
 
@@ -1471,17 +1536,20 @@ date_makeDate(JSContext *cx, uintN maxar
 
     /* return NaN if date is NaN and we're not setting the year,
      * If we are, use 0 as the time. */
     if (!(JSDOUBLE_IS_FINITE(result))) {
         if (maxargs < 3)
             return js_NewNumberInRootedValue(cx, result, vp);
         lorutime = +0.;
     } else {
-        lorutime = local ? LocalTime(result) : result;
+        lorutime = result;
+
+        if (local && ConvertToLocalTime(cx, obj, vp, &lorutime))
+            return JS_FALSE;
     }
 
     argp = args;
     stop = argp + argc;
     if (maxargs >= 3 && argp < stop)
         year = *argp++;
     else
         year = YearFromTime(lorutime);
@@ -1494,18 +1562,18 @@ date_makeDate(JSContext *cx, uintN maxar
     if (maxargs >= 1 && argp < stop)
         day = *argp++;
     else
         day = DateFromTime(lorutime);
 
     day = MakeDay(year, month, day); /* day within year */
     result = MakeDate(day, TimeWithinDay(lorutime));
 
-    if (local)
-        result = UTC(result);
+    if (local && !ConvertToUTC(cx, obj, vp, &result))
+        return JS_FALSE;
 
     result = TIMECLIP(result);
     if (!SetUTCTime(cx, obj, NULL, result))
         return JS_FALSE;
 
     return js_NewNumberInRootedValue(cx, result, vp);
 }
 
@@ -1566,25 +1634,30 @@ date_setYear(JSContext *cx, uintN argc, 
     if (!JSDOUBLE_IS_FINITE(year))
         return SetDateToNaN(cx, vp);
 
     year = js_DoubleToInteger(year);
 
     if (!JSDOUBLE_IS_FINITE(result)) {
         t = +0.0;
     } else {
-        t = LocalTime(result);
+        t = result;
+
+        if (!ConvertToLocalTime(cx, obj, vp, &t))
+            return JS_FALSE;
     }
 
     if (year >= 0 && year <= 99)
         year += 1900;
 
     day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
     result = MakeDate(day, TimeWithinDay(t));
-    result = UTC(result);
+
+    if (!ConvertToUTC(cx, obj, vp, &result))
+        return JS_FALSE;
 
     result = TIMECLIP(result);
     if (!SetUTCTime(cx, obj, NULL, result))
         return JS_FALSE;
 
     return js_NewNumberInRootedValue(cx, result, vp);
 }
 
@@ -1686,33 +1759,46 @@ new_explode(jsdouble timeval, PRMJTime *
 }
 
 typedef enum formatspec {
     FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
 } formatspec;
 
 /* helper function */
 static JSBool
-date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
+date_format(JSContext *cx, JSObject *obj, jsdouble date, formatspec format, jsval *rval)
 {
     char buf[100];
     JSString *str;
     char tzbuf[100];
     JSBool usetz;
     size_t i, tzlen;
     PRMJTime split;
 
     if (!JSDOUBLE_IS_FINITE(date)) {
         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
     } else {
-        jsdouble local = LocalTime(date);
+        jsdouble local;
+        jsdouble adjustTime;
+
+        if (obj) {
+            local = date;
+            if (!ConvertToLocalTime(cx, obj, NULL, &local))
+                return JS_FALSE;
+
+            if (!GetAdjustTime(cx, obj, NULL, date, &adjustTime))
+                return JS_FALSE;
+        } else {
+            adjustTime = AdjustTime(date, LocalTZA());
+            local = date + adjustTime;
+        }
 
         /* offset from GMT in minutes.  The offset includes daylight savings,
            if it applies. */
-        jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
+        jsint minutes = (jsint) floor(adjustTime / msPerMinute);
 
         /* map 510 minutes to 0830 hours */
         intN offset = (minutes / 60) * 100 + minutes % 60;
 
         /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
          * printed as 'GMT-0800' rather than as 'PST' to avoid
          * operating-system dependence on strftime (which
          * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
@@ -1815,25 +1901,29 @@ date_toLocaleHelper(JSContext *cx, const
     obj = JS_THIS_OBJECT(cx, vp);
     if (!GetUTCTime(cx, obj, vp, &utctime))
         return JS_FALSE;
 
     if (!JSDOUBLE_IS_FINITE(utctime)) {
         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
     } else {
         intN result_len;
-        jsdouble local = LocalTime(utctime);
+
+        jsdouble local = utctime;
+        if (!ConvertToLocalTime(cx, obj, vp, &local))
+            return JS_FALSE;
+
         new_explode(local, &split);
 
         /* let PRMJTime format it.       */
         result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
 
         /* If it failed, default to toString. */
         if (result_len == 0)
-            return date_format(cx, utctime, FORMATSPEC_FULL, vp);
+            return date_format(cx, obj, utctime, FORMATSPEC_FULL, vp);
 
         /* Hacked check against undesired 2-digit year 00/00/00 form. */
         if (strcmp(format, "%x") == 0 && result_len >= 6 &&
             /* Format %x means use OS settings, which may have 2-digit yr, so
                hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
             !isdigit(buf[result_len - 3]) &&
             isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
             /* ...but not if starts with 4-digit year, like 2022/3/11. */
@@ -1911,31 +2001,37 @@ date_toLocaleFormat(JSContext *cx, uintN
         return JS_FALSE;
 
     return date_toLocaleHelper(cx, fmtbytes, vp);
 }
 
 static JSBool
 date_toTimeString(JSContext *cx, uintN argc, jsval *vp)
 {
+    JSObject *obj;
     jsdouble utctime;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
+    obj = JS_THIS_OBJECT(cx, vp);
+
+    if (!GetUTCTime(cx, obj, vp, &utctime))
         return JS_FALSE;
-    return date_format(cx, utctime, FORMATSPEC_TIME, vp);
+    return date_format(cx, obj, utctime, FORMATSPEC_TIME, vp);
 }
 
 static JSBool
 date_toDateString(JSContext *cx, uintN argc, jsval *vp)
 {
+    JSObject *obj;
     jsdouble utctime;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
+    obj = JS_THIS_OBJECT(cx, vp);
+
+    if (!GetUTCTime(cx, obj, vp, &utctime))
         return JS_FALSE;
-    return date_format(cx, utctime, FORMATSPEC_DATE, vp);
+    return date_format(cx, obj, utctime, FORMATSPEC_DATE, vp);
 }
 
 #if JS_HAS_TOSOURCE
 #include <string.h>
 #include "jsdtoa.h"
 
 static JSBool
 date_toSource(JSContext *cx, uintN argc, jsval *vp)
@@ -1967,37 +2063,40 @@ date_toSource(JSContext *cx, uintN argc,
     *vp = STRING_TO_JSVAL(str);
     return JS_TRUE;
 }
 #endif
 
 static JSBool
 date_toString(JSContext *cx, uintN argc, jsval *vp)
 {
+    JSObject *obj;
     jsdouble utctime;
 
-    if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
+    obj = JS_THIS_OBJECT(cx, vp);
+
+    if (!GetUTCTime(cx, obj, vp, &utctime))
         return JS_FALSE;
-    return date_format(cx, utctime, FORMATSPEC_FULL, vp);
+    return date_format(cx, obj, utctime, FORMATSPEC_FULL, vp);
 }
 
 #ifdef JS_TRACER
 static jsval FASTCALL
 date_valueOf_tn(JSContext* cx, JSObject* obj, JSString* str)
 {
     JS_ASSERT(JS_InstanceOf(cx, obj, &js_DateClass, NULL));
     jsdouble t = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
 
     JSString* number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
     jsval v;
     if (js_EqualStrings(str, number_str)) {
         if (!js_NewNumberInRootedValue(cx, t, &v))
             return JSVAL_ERROR_COOKIE;
     } else {
-        if (!date_format(cx, t, FORMATSPEC_FULL, &v))
+        if (!date_format(cx, obj, t, FORMATSPEC_FULL, &v))
             return JSVAL_ERROR_COOKIE;
     }
     return v;
 }
 #endif
 
 static JSBool
 date_valueOf(JSContext *cx, uintN argc, jsval *vp)
@@ -2097,29 +2196,31 @@ date_constructor(JSContext *cx, JSObject
     jsdouble *date;
 
     date = js_NewWeaklyRootedDouble(cx, 0.0);
     if (!date)
         return NULL;
 
     obj->fslots[JSSLOT_UTC_TIME] = DOUBLE_TO_JSVAL(date);
     obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
+    obj->fslots[JSSLOT_LOCAL_TZA] = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
+
     return date;
 }
 
 JSBool
 js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
     jsdouble *date;
     JSString *str;
     jsdouble d;
 
     /* Date called as function. */
     if (!JS_IsConstructing(cx))
-        return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, rval);
+        return date_format(cx, NULL, NowAsMillis(), FORMATSPEC_FULL, rval);
 
     /* Date called as constructor. */
     if (argc == 0) {
         date = date_constructor(cx, obj);
         if (!date)
             return JS_FALSE;
         *date = NowAsMillis();
     } else if (argc == 1) {
@@ -2137,49 +2238,49 @@ js_Date(JSContext *cx, JSObject *obj, ui
             date = date_constructor(cx, obj);
             if (!date)
                 return JS_FALSE;
 
             str = js_ValueToString(cx, argv[0]);
             if (!str)
                 return JS_FALSE;
 
-            if (!date_parseString(str, date))
+            if (!date_parseString(cx, obj, str, date))
                 *date = *cx->runtime->jsNaN;
             *date = TIMECLIP(*date);
         }
     } else {
         jsdouble *date;
         jsdouble msec_time;
 
         if (!date_msecFromArgs(cx, argc, argv, &msec_time))
             return JS_FALSE;
 
         date = date_constructor(cx, obj);
         if (!date)
             return JS_FALSE;
 
         if (JSDOUBLE_IS_FINITE(msec_time)) {
-            msec_time = UTC(msec_time);
+            if (!ConvertToUTC(cx, obj, NULL, &msec_time))
+                return JS_FALSE;
+
             msec_time = TIMECLIP(msec_time);
         }
 
         *date = msec_time;
     }
     return JS_TRUE;
 }
 
 JSObject *
 js_InitDateClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto;
     jsdouble *proto_date;
 
-    /* set static LocalTZA */
-    LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
     proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS,
                          NULL, date_methods, NULL, date_static_methods);
     if (!proto)
         return NULL;
 
     /* Alias toUTCString with toGMTString.  (ECMA B.2.6) */
     if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
         return NULL;
@@ -2211,21 +2312,35 @@ js_NewDateObjectMsec(JSContext *cx, jsdo
     return obj;
 }
 
 JS_FRIEND_API(JSObject *)
 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
                  int hour, int min, int sec)
 {
     JSObject *obj;
+    jsdouble *date;
     jsdouble msec_time;
 
     JS_ASSERT(mon < 12);
     msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
-    obj = js_NewDateObjectMsec(cx, UTC(msec_time));
+
+    obj = js_NewObject(cx, &js_DateClass, NULL, NULL, 0);
+    if (!obj)
+        return NULL;
+
+    date = date_constructor(cx, obj);
+    if (!date)
+        return NULL;
+
+    if (!ConvertToUTC(cx, obj, NULL, &msec_time))
+        return NULL;
+
+    *date = msec_time;
+
     return obj;
 }
 
 JS_FRIEND_API(JSBool)
 js_DateIsValid(JSContext *cx, JSObject* obj)
 {
     jsdouble utctime;
     return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime);
@@ -2307,40 +2422,46 @@ js_DateGetSeconds(JSContext *cx, JSObjec
 
     return (int) SecFromTime(utctime);
 }
 
 JS_FRIEND_API(void)
 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
 {
     jsdouble local;
+    jsdouble utc;
 
     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
         return;
 
     /* reset date if it was NaN */
     if (JSDOUBLE_IS_NaN(local))
         local = 0;
 
     local = date_msecFromDate(year,
                               MonthFromTime(local),
                               DateFromTime(local),
                               HourFromTime(local),
                               MinFromTime(local),
                               SecFromTime(local),
                               msFromTime(local));
 
+    utc = local;
+    if (!ConvertToUTC(cx, obj, NULL, &utc))
+        return;
+
     /* SetUTCTime also invalidates local time cache. */
-    SetUTCTime(cx, obj, NULL, UTC(local));
+    SetUTCTime(cx, obj, NULL, utc);
 }
 
 JS_FRIEND_API(void)
 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
 {
     jsdouble local;
+    jsdouble utc;
 
     JS_ASSERT(month < 12);
 
     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
         return;
 
     /* bail if date was NaN */
     if (JSDOUBLE_IS_NaN(local))
@@ -2348,98 +2469,130 @@ js_DateSetMonth(JSContext *cx, JSObject 
 
     local = date_msecFromDate(YearFromTime(local),
                               month,
                               DateFromTime(local),
                               HourFromTime(local),
                               MinFromTime(local),
                               SecFromTime(local),
                               msFromTime(local));
-    SetUTCTime(cx, obj, NULL, UTC(local));
+
+    utc = local;
+    if (!ConvertToUTC(cx, obj, NULL, &utc))
+        return;
+
+    SetUTCTime(cx, obj, NULL, utc);
 }
 
 JS_FRIEND_API(void)
 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
 {
     jsdouble local;
+    jsdouble utc;
 
     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
         return;
 
     if (JSDOUBLE_IS_NaN(local))
         return;
 
     local = date_msecFromDate(YearFromTime(local),
                               MonthFromTime(local),
                               date,
                               HourFromTime(local),
                               MinFromTime(local),
                               SecFromTime(local),
                               msFromTime(local));
-    SetUTCTime(cx, obj, NULL, UTC(local));
+
+    utc = local;
+    if (!ConvertToUTC(cx, obj, NULL, &utc))
+        return;
+
+    SetUTCTime(cx, obj, NULL, utc);
 }
 
 JS_FRIEND_API(void)
 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
 {
     jsdouble local;
+    jsdouble utc;
 
     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
         return;
 
     if (JSDOUBLE_IS_NaN(local))
         return;
+
     local = date_msecFromDate(YearFromTime(local),
                               MonthFromTime(local),
                               DateFromTime(local),
                               hours,
                               MinFromTime(local),
                               SecFromTime(local),
                               msFromTime(local));
-    SetUTCTime(cx, obj, NULL, UTC(local));
+
+    utc = local;
+    if (!ConvertToUTC(cx, obj, NULL, &utc))
+        return;
+
+    SetUTCTime(cx, obj, NULL, utc);
 }
 
 JS_FRIEND_API(void)
 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
 {
     jsdouble local;
+    jsdouble utc;
 
     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
         return;
 
     if (JSDOUBLE_IS_NaN(local))
         return;
+
     local = date_msecFromDate(YearFromTime(local),
                               MonthFromTime(local),
                               DateFromTime(local),
                               HourFromTime(local),
                               minutes,
                               SecFromTime(local),
                               msFromTime(local));
-    SetUTCTime(cx, obj, NULL, UTC(local));
+
+    utc = local;
+    if (!ConvertToUTC(cx, obj, NULL, &utc))
+        return;
+
+    SetUTCTime(cx, obj, NULL, utc);
 }
 
 JS_FRIEND_API(void)
 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
 {
     jsdouble local;
+    jsdouble utc;
 
     if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
         return;
 
     if (JSDOUBLE_IS_NaN(local))
         return;
+
     local = date_msecFromDate(YearFromTime(local),
                               MonthFromTime(local),
                               DateFromTime(local),
                               HourFromTime(local),
                               MinFromTime(local),
                               seconds,
                               msFromTime(local));
-    SetUTCTime(cx, obj, NULL, UTC(local));
+
+    utc = local;
+    if (!ConvertToUTC(cx, obj, NULL, &utc))
+            return;
+
+    SetUTCTime(cx, obj, NULL, utc);
 }
 
 JS_FRIEND_API(jsdouble)
 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
 {
     jsdouble utctime;
     if (!GetUTCTime(cx, obj, NULL, &utctime))
         return 0;