Bug 1346211 - Part 6: Use ICU for all time zone computations when available. r=Waldo
authorAndré Bargull <andre.bargull@gmail.com>
Fri, 17 Aug 2018 03:04:39 -0700
changeset 487327 7b1a3a49547dc9564aaa6ecf90b919b66b7ada8d
parent 487326 a40001643f53cbb214216e615fae913dc4384c5b
child 487328 fd8d17192696250a5396894301478f36c6c75be7
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1346211
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1346211 - Part 6: Use ICU for all time zone computations when available. r=Waldo
browser/components/resistfingerprinting/test/browser/browser_timezone.js
config/check_spidermonkey_style.py
js/src/builtin/TestingFunctions.cpp
js/src/jsdate.cpp
js/src/tests/lib/tests.py
js/src/tests/non262/Date/reset-time-zone-cache-same-offset.js
js/src/tests/non262/Date/shell.js
js/src/tests/non262/Date/time-zone-2038-pst.js
js/src/tests/non262/Date/time-zone-pst.js
js/src/tests/non262/Date/time-zones-historic.js
js/src/tests/non262/Date/time-zones-imported.js
js/src/tests/non262/Date/time-zones-pedantic.js
js/src/tests/non262/Date/time-zones-posix.js
js/src/tests/non262/Date/time-zones.js
js/src/tests/non262/Date/toString-localized-posix.js
js/src/tests/non262/Date/toString-localized.js
js/src/tests/non262/regress/regress-58116.js
js/src/vm/DateObject.h
js/src/vm/DateTime.cpp
js/src/vm/DateTime.h
js/src/vm/MutexIDs.h
--- a/browser/components/resistfingerprinting/test/browser/browser_timezone.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_timezone.js
@@ -17,17 +17,18 @@ add_task(async function test_timezone() 
   let tab = await BrowserTestUtils.openNewForegroundTab(
     gBrowser, TEST_PATH + "file_dummy.html");
 
   await ContentTask.spawn(tab.linkedBrowser, null,
     async function() {
       let dateObj = new Date();
       let dateString = dateObj.toString();
 
-      ok(dateString.endsWith("(UTC)"), "The date string is in UTC timezone.");
+      ok(dateString.endsWith("(Coordinated Universal Time)"),
+           "The date string is in UTC timezone.");
       is(dateObj.getFullYear(), dateObj.getUTCFullYear(),
            "The full year reports in UTC timezone.");
       is(dateObj.getMonth(), dateObj.getUTCMonth(), "The month reports in UTC timezone.");
       is(dateObj.getDate(), dateObj.getUTCDate(), "The month reports in UTC timezone.");
       is(dateObj.getDay(), dateObj.getUTCDay(), "The day reports in UTC timezone.");
       is(dateObj.getHours(), dateObj.getUTCHours(), "The hours reports in UTC timezone.");
       is(dateObj.getTimezoneOffset(), 0, "The difference with UTC timezone is 0.");
 
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -79,18 +79,20 @@ included_inclnames_to_ignore = set([
     'private/pprio.h',          # NSPR
     'prlink.h',                 # NSPR
     'prlock.h',                 # NSPR
     'prprf.h',                  # NSPR
     'prthread.h',               # NSPR
     'prtypes.h',                # NSPR
     'selfhosted.out.h',         # generated in $OBJDIR
     'shellmoduleloader.out.h',  # generated in $OBJDIR
+    'unicode/basictz.h',        # ICU
+    'unicode/locid.h',          # ICU
+    'unicode/plurrule.h',       # ICU
     'unicode/timezone.h',       # ICU
-    'unicode/plurrule.h',       # ICU
     'unicode/ucal.h',           # ICU
     'unicode/uchar.h',          # ICU
     'unicode/uclean.h',         # ICU
     'unicode/ucol.h',           # ICU
     'unicode/udat.h',           # ICU
     'unicode/udatpg.h',         # ICU
     'unicode/udisplaycontext.h',  # ICU
     'unicode/uenum.h',          # ICU
@@ -98,16 +100,17 @@ included_inclnames_to_ignore = set([
     'unicode/unistr.h',         # ICU
     'unicode/unorm2.h',         # ICU
     'unicode/unum.h',           # ICU
     'unicode/unumsys.h',        # ICU
     'unicode/upluralrules.h',   # ICU
     'unicode/ureldatefmt.h',    # ICU
     'unicode/ustring.h',        # ICU
     'unicode/utypes.h',         # ICU
+    'unicode/uversion.h',       # ICU
     'vtune/VTuneWrapper.h'      # VTune
 ])
 
 # These files have additional constraints on where they are #included, so we
 # ignore #includes of them when checking #include ordering.
 oddly_ordered_inclnames = set([
     'ctypes/typedefs.h',        # Included multiple times in the body of ctypes/CTypes.h
     'frontend/BinSource-auto.h',  # Included in the body of frontend/BinSource.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -6,18 +6,20 @@
 
 #include "builtin/TestingFunctions.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/TextUtils.h"
 #include "mozilla/Unused.h"
 
+#include <algorithm>
 #include <cfloat>
 #include <cmath>
 #include <cstdlib>
 #include <ctime>
 
 #if defined(XP_UNIX) && !defined(XP_DARWIN)
 #include <time.h>
 #else
@@ -4948,16 +4950,95 @@ SetTimeZone(JSContext* cx, unsigned argc
 #endif /* _WIN32 */
 
     JS::ResetTimeZone();
 
     args.rval().setUndefined();
     return true;
 }
 
+static bool
+GetDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedObject callee(cx, &args.callee());
+
+    if (args.length() != 0) {
+        ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
+        return false;
+    }
+
+    UniqueChars locale = JS_GetDefaultLocale(cx);
+    if (!locale) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEFAULT_LOCALE_ERROR);
+        return false;
+    }
+
+    return ReturnStringCopy(cx, args, locale.get());
+}
+
+static bool
+SetDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedObject callee(cx, &args.callee());
+
+    if (args.length() != 1) {
+        ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
+        return false;
+    }
+
+    if (!args[0].isString() && !args[0].isUndefined()) {
+        ReportUsageErrorASCII(cx, callee, "First argument should be a string or undefined");
+        return false;
+    }
+
+    if (args[0].isString() && !args[0].toString()->empty()) {
+        auto containsOnlyValidBCP47Characters = [](auto* chars, size_t length) {
+            return mozilla::IsAsciiAlpha(chars[0]) &&
+                   std::all_of(chars, chars + length, [](auto c) {
+                       return mozilla::IsAsciiAlphanumeric(c) || c == '-';
+                   });
+        };
+
+        RootedLinearString str(cx, args[0].toString()->ensureLinear(cx));
+        if (!str)
+            return false;
+
+        bool hasValidChars;
+        {
+            JS::AutoCheckCannotGC nogc;
+
+            size_t length = str->length();
+            hasValidChars = str->hasLatin1Chars()
+                            ? containsOnlyValidBCP47Characters(str->latin1Chars(nogc), length)
+                            : containsOnlyValidBCP47Characters(str->twoByteChars(nogc), length);
+        }
+
+        if (!hasValidChars) {
+            ReportUsageErrorASCII(cx, callee, "First argument should be BCP47 language tag");
+            return false;
+        }
+
+        JSAutoByteString locale;
+        if (!locale.encodeLatin1(cx, str))
+            return false;
+
+        if (!JS_SetDefaultLocale(cx->runtime(), locale.ptr())) {
+            ReportOutOfMemory(cx);
+            return false;
+        }
+    } else {
+        JS_ResetDefaultLocale(cx->runtime());
+    }
+
+    args.rval().setUndefined();
+    return true;
+}
+
 #if defined(FUZZING) && defined(__AFL_COMPILER)
 static bool
 AflLoop(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     uint32_t max_cnt;
     if (!ToUint32(cx, args.get(0), &max_cnt))
@@ -5938,16 +6019,20 @@ gc::ZealModeHelpText),
     JS_FN_HELP("isLegacyIterator", IsLegacyIterator, 1, 0,
 "isLegacyIterator(value)",
 "  Returns whether the value is considered is a legacy iterator.\n"),
 
     JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
 "getTimeZone()",
 "  Get the current time zone.\n"),
 
+    JS_FN_HELP("getDefaultLocale", GetDefaultLocale, 0, 0,
+"getDefaultLocale()",
+"  Get the current default locale.\n"),
+
     JS_FN_HELP("setTimeResolution", SetTimeResolution, 2, 0,
 "setTimeResolution(resolution, jitter)",
 "  Enables time clamping and jittering. Specify a time resolution in\n"
 "  microseconds and whether or not to jitter\n"),
 
     JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal, 0, 0,
 "scriptedCallerGlobal()",
 "  Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"),
@@ -5988,16 +6073,22 @@ static const JSFunctionSpecWithHelp Fuzz
 "  Returns an array of error notes."),
 
     JS_FN_HELP("setTimeZone", SetTimeZone, 1, 0,
 "setTimeZone(tzname)",
 "  Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n"
 "  An empty string or undefined resets the time zone to its default value.\n"
 "  NOTE: The input string is not validated and will be passed verbatim to setenv()."),
 
+JS_FN_HELP("setDefaultLocale", SetDefaultLocale, 1, 0,
+"setDefaultLocale(locale)",
+"  Set the runtime default locale to the given value.\n"
+"  An empty string or undefined resets the runtime locale to its default value.\n"
+"  NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
+
     JS_FS_HELP_END
 };
 
 bool
 js::DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe_,
                            bool disableOOMFunctions_)
 {
     fuzzingSafe = fuzzingSafe_;
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -33,16 +33,17 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/String.h"
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/StringType.h"
 #include "vm/Time.h"
 
@@ -430,16 +431,55 @@ JS::SetReduceMicrosecondTimePrecisionCal
 
 JS_PUBLIC_API(void)
 JS::SetTimeResolutionUsec(uint32_t resolution, bool jitter)
 {
     sResolutionUsec = resolution;
     sJitter = jitter;
 }
 
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+// ES2019 draft rev 0ceb728a1adbffe42b26972a6541fd7f398b1557
+// 20.3.1.7 LocalTZA ( t, isUTC )
+static double
+LocalTZA(double t, DateTimeInfo::TimeZoneOffset offset)
+{
+    MOZ_ASSERT(IsFinite(t));
+
+    int64_t milliseconds = static_cast<int64_t>(t);
+    int32_t offsetMilliseconds = DateTimeInfo::getOffsetMilliseconds(milliseconds, offset);
+    return static_cast<double>(offsetMilliseconds);
+}
+
+// ES2019 draft rev 0ceb728a1adbffe42b26972a6541fd7f398b1557
+// 20.3.1.8 LocalTime ( t )
+static double
+LocalTime(double t)
+{
+    if (!IsFinite(t))
+        return GenericNaN();
+
+    MOZ_ASSERT(StartOfTime <= t && t <= EndOfTime);
+    return t + LocalTZA(t, DateTimeInfo::TimeZoneOffset::UTC);
+}
+
+// ES2019 draft rev 0ceb728a1adbffe42b26972a6541fd7f398b1557
+// 20.3.1.9 UTC ( t )
+static double
+UTC(double t)
+{
+    if (!IsFinite(t))
+        return GenericNaN();
+
+    if (t < (StartOfTime - msPerDay) || t > (EndOfTime + msPerDay))
+        return GenericNaN();
+
+    return t - LocalTZA(t, DateTimeInfo::TimeZoneOffset::Local);
+}
+#else
 /*
  * Find a year for which any given date will fall on the same weekday.
  *
  * This function should be used with caution when used other than
  * for determining DST; it hasn't been proven not to produce an
  * incorrect year for times near year boundaries.
  */
 static int
@@ -527,16 +567,17 @@ UTC(double t)
     // transitions. For example when transitioning from PST to PDT,
     // |new Date(2016,2,13,2,0,0).toTimeString()| returns the string value
     // "01:00:00 GMT-0800 (PST)" instead of "03:00:00 GMT-0700 (PDT)". Follow
     // V8 and subtract one hour before computing the offset.
     // Spec bug: https://bugs.ecmascript.org/show_bug.cgi?id=4007
 
     return t - AdjustTime(t - DateTimeInfo::localTZA() - msPerHour);
 }
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
 
 /* ES5 15.9.1.10. */
 static double
 HourFromTime(double t)
 {
     return PositiveModulo(floor(t/msPerHour), HoursPerDay);
 }
 
@@ -1769,30 +1810,18 @@ DateObject::getUTCMinutes_impl(JSContext
 
 static bool
 date_getUTCMinutes(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsDate, DateObject::getUTCMinutes_impl>(cx, args);
 }
 
-/*
- * Date.getSeconds is mapped to getUTCSeconds. As long as no supported time
- * zone has a fractional-minute component, the differences in their
- * specifications aren't observable.
- *
- * We'll have to split the implementations if a new time zone with a
- * fractional-minute component is introduced or once we implement ES6's
- * 20.3.1.7 Local Time Zone Adjustment: time zones with adjustments like that
- * did historically exist, e.g.
- * https://en.wikipedia.org/wiki/UTC%E2%88%9200:25:21
- */
-
 /* static */ MOZ_ALWAYS_INLINE bool
-DateObject::getUTCSeconds_impl(JSContext* cx, const CallArgs& args)
+DateObject::getSeconds_impl(JSContext* cx, const CallArgs& args)
 {
     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
     dateObj->fillLocalTimeSlots();
 
     // Note: LOCAL_SECONDS_INTO_YEAR_SLOT is guaranteed to contain an
     // int32 or NaN after the call to fillLocalTimeSlots.
     Value yearSeconds = dateObj->getReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT);
     if (yearSeconds.isDouble()) {
@@ -1800,27 +1829,49 @@ DateObject::getUTCSeconds_impl(JSContext
         args.rval().set(yearSeconds);
     } else {
         args.rval().setInt32(yearSeconds.toInt32() % int(SecondsPerMinute));
     }
     return true;
 }
 
 static bool
+date_getSeconds(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsDate, DateObject::getSeconds_impl>(cx, args);
+}
+
+/* static */ MOZ_ALWAYS_INLINE bool
+DateObject::getUTCSeconds_impl(JSContext* cx, const CallArgs& args)
+{
+    double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
+    if (IsFinite(result))
+        result = SecFromTime(result);
+
+    args.rval().setNumber(result);
+    return true;
+}
+
+static bool
 date_getUTCSeconds(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsDate, DateObject::getUTCSeconds_impl>(cx, args);
 }
+
 /*
- * Date.getMilliseconds is mapped to getUTCMilliseconds for the same reasons
- * that getSeconds is mapped to getUTCSeconds (see above).  No known LocalTZA
- * has *ever* included a fractional-second component, however, so we can keep
- * this simplification even if we stop implementing ES5 local-time computation
- * semantics.
+ * Date.getMilliseconds is mapped to getUTCMilliseconds. As long as no
+ * supported time zone has a fractional-second component, the differences in
+ * their specifications aren't observable.
+ *
+ * The 'tz' database explicitly does not support fractional-second time zones.
+ * For example the Netherlands observed Amsterdam Mean Time, estimated to be
+ * UT +00:19:32.13, from 1909 to 1937, but in tzdata AMT is defined as exactly
+ * UT +00:19:32.
  */
 
 /* static */ MOZ_ALWAYS_INLINE bool
 DateObject::getUTCMilliseconds_impl(JSContext* cx, const CallArgs& args)
 {
     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
     if (IsFinite(result))
         result = msFromTime(result);
@@ -2654,16 +2705,51 @@ date_toJSON(JSContext* cx, unsigned argc
         JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_BAD_TOISOSTRING_PROP);
         return false;
     }
 
     /* Step 6. */
     return Call(cx, toISO, obj, args.rval());
 }
 
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+static JSString*
+TimeZoneComment(JSContext* cx, double utcTime, double localTime)
+{
+    const char* locale = cx->runtime()->getDefaultLocale();
+    if (!locale) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEFAULT_LOCALE_ERROR);
+        return nullptr;
+    }
+
+    char16_t tzbuf[100];
+    tzbuf[0] = ' ';
+    tzbuf[1] = '(';
+
+    char16_t* timeZoneStart = tzbuf + 2;
+    constexpr size_t remainingSpace = mozilla::ArrayLength(tzbuf) - 2 - 1; // for the trailing ')'
+
+    int64_t utcMilliseconds = static_cast<int64_t>(utcTime);
+    if (!DateTimeInfo::timeZoneDisplayName(timeZoneStart, remainingSpace, utcMilliseconds, locale))
+    {
+        JS_ReportOutOfMemory(cx);
+        return nullptr;
+    }
+
+    // Reject if the result string is empty.
+    size_t len = js_strlen(timeZoneStart);
+    if (len == 0)
+        return cx->names().empty;
+
+    // Parenthesize the returned display name.
+    timeZoneStart[len] = ')';
+
+    return NewStringCopyN<CanGC>(cx, tzbuf, 2 + len + 1);
+}
+#else
 /* Interface to PRMJTime date struct. */
 static PRMJTime
 ToPRMJTime(double localTime, double utcTime)
 {
     double year = YearFromTime(localTime);
 
     PRMJTime prtm;
     prtm.tm_usec = int32_t(msFromTime(localTime)) * 1000;
@@ -2722,16 +2808,17 @@ TimeZoneComment(JSContext* cx, double ut
             usetz = false;
 
         if (usetz)
             return NewStringCopyN<CanGC>(cx, tzbuf, tzlen);
     }
 
     return cx->names().empty;
 }
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
 
 enum class FormatSpec {
     DateTime,
     Date,
     Time
 };
 
 static bool
@@ -2746,30 +2833,36 @@ FormatDate(JSContext* cx, double utcTime
 
     double localTime = LocalTime(utcTime);
 
     int offset = 0;
     RootedString timeZoneComment(cx);
     if (format == FormatSpec::DateTime || format == FormatSpec::Time) {
         // Offset from GMT in minutes. The offset includes daylight savings,
         // if it applies.
-        int minutes = (int) floor((localTime - utcTime) / msPerMinute);
+        int minutes = (int) trunc((localTime - utcTime) / msPerMinute);
 
         // Map 510 minutes to 0830 hours.
         offset = (minutes / 60) * 100 + minutes % 60;
 
         // Print as "Wed Nov 05 1997 19:38:03 GMT-0800 (PST)".
         //
         // The TZA is printed as 'GMT-0800' rather than as 'PST' to avoid
         // operating-system dependence on strftime (which PRMJ_FormatTime
         // calls, for %Z only.) win32 prints PST as 'Pacific Standard Time.'
         // This way we always know what we're getting, and can parse it if
         // we produce it. The OS time zone string is included as a comment.
-
-        // Get a time zone string from the OS to include as a comment.
+        //
+        // When ICU is used to retrieve the time zone string, the localized
+        // 'long' name format from CLDR is used. For example when the default
+        // locale is "en-US", PST is displayed as 'Pacific Standard Time', but
+        // when it is "ru", 'Тихоокеанское стандартное время' is used. This
+        // also means the time zone string may not fit into Latin-1.
+
+        // Get a time zone string from the OS or ICU to include as a comment.
         timeZoneComment = TimeZoneComment(cx, utcTime, localTime);
         if (!timeZoneComment)
             return false;
     }
 
     char buf[100];
     switch (format) {
       case FormatSpec::DateTime:
@@ -3061,17 +3154,17 @@ static const JSFunctionSpec date_methods
     JS_FN("getDate",             date_getDate,            0,0),
     JS_FN("getUTCDate",          date_getUTCDate,         0,0),
     JS_FN("getDay",              date_getDay,             0,0),
     JS_FN("getUTCDay",           date_getUTCDay,          0,0),
     JS_FN("getHours",            date_getHours,           0,0),
     JS_FN("getUTCHours",         date_getUTCHours,        0,0),
     JS_FN("getMinutes",          date_getMinutes,         0,0),
     JS_FN("getUTCMinutes",       date_getUTCMinutes,      0,0),
-    JS_FN("getSeconds",          date_getUTCSeconds,      0,0),
+    JS_FN("getSeconds",          date_getSeconds,         0,0),
     JS_FN("getUTCSeconds",       date_getUTCSeconds,      0,0),
     JS_FN("getMilliseconds",     date_getUTCMilliseconds, 0,0),
     JS_FN("getUTCMilliseconds",  date_getUTCMilliseconds, 0,0),
     JS_FN("setTime",             date_setTime,            1,0),
     JS_FN("setYear",             date_setYear,            1,0),
     JS_FN("setFullYear",         date_setFullYear,        3,0),
     JS_FN("setUTCFullYear",      date_setUTCFullYear,     3,0),
     JS_FN("setMonth",            date_setMonth,           2,0),
--- a/js/src/tests/lib/tests.py
+++ b/js/src/tests/lib/tests.py
@@ -66,17 +66,17 @@ def get_environment_overlay(js_shell):
     """
     Build a dict of additional environment variables that must be set to run
     tests successfully.
     """
     env = {
         # Force Pacific time zone to avoid failures in Date tests.
         'TZ': 'PST8PDT',
         # Force date strings to English.
-        'LC_TIME': 'en_US.UTF-8',
+        'LC_ALL': 'en_US.UTF-8',
         # Tell the shell to disable crash dialogs on windows.
         'XRE_NO_WINDOWS_CRASH_DIALOG': '1',
     }
     # Add the binary's directory to the library search path so that we find the
     # nspr and icu we built, instead of the platform supplied ones (or none at
     # all on windows).
     if sys.platform.startswith('linux'):
         env['LD_LIBRARY_PATH'] = os.path.dirname(js_shell)
--- a/js/src/tests/non262/Date/reset-time-zone-cache-same-offset.js
+++ b/js/src/tests/non262/Date/reset-time-zone-cache-same-offset.js
@@ -1,28 +1,30 @@
 // |reftest| skip-if(xulRuntime.OS=="WINNT") -- Windows doesn't accept IANA names for the TZ env variable
 
 const testCases = [
     {
         timeZone: "Europe/London",
         string: "Tue Aug 14 2018 00:00:00 GMT+0100 (BST)",
+        alternativeTimeZones: ["British Summer Time"],
         localeString: "8/14/2018, 12:00:00 AM GMT+1",
     },
     {
         timeZone: "UTC",
         string: "Tue Aug 14 2018 00:00:00 GMT+0000 (UTC)",
+        alternativeTimeZones: ["Coordinated Universal Time"],
         localeString: "8/14/2018, 12:00:00 AM UTC",
     },
 ];
 
 // Repeat twice to test both transitions (Europe/London -> UTC and UTC -> Europe/London).
 for (let i = 0; i < 2; ++i) {
-    for (let {timeZone, string, localeString} of testCases) {
+    for (let {timeZone, string, localeString, alternativeTimeZones} of testCases) {
         setTimeZone(timeZone);
 
         let dt = new Date(2018, 8 - 1, 14);
-        assertEq(dt.toString(), string);
+        assertDateTime(dt, string, ...alternativeTimeZones);
         assertEq(dt.toLocaleString("en-US", {timeZoneName: "short"}), localeString);
     }
 }
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
--- a/js/src/tests/non262/Date/shell.js
+++ b/js/src/tests/non262/Date/shell.js
@@ -67,16 +67,26 @@
             return "PST8PDT";
 
           default:
             // Other time zones abbrevations are not supported.
             return tz;
         }
     }
 
+    function getDefaultLocale() {
+        // If the default locale looks like a BCP-47 language tag, return it.
+        var locale = global.getDefaultLocale();
+        if (locale.match(/^[a-z][a-z0-9\-]+$/i))
+            return locale;
+
+        // Otherwise use undefined to reset to the default locale.
+        return undefined;
+    }
+
     let defaultTimeZone = null;
     let defaultLocale = null;
 
     // Run the given test in the requested time zone.
     function inTimeZone(tzname, fn) {
         if (defaultTimeZone === null)
             defaultTimeZone = getDefaultTimeZone();
 
@@ -84,16 +94,30 @@
         try {
             fn();
         } finally {
             setTimeZone(defaultTimeZone);
         }
     }
     global.inTimeZone = inTimeZone;
 
+    // Run the given test with the requested locale.
+    function withLocale(locale, fn) {
+        if (defaultLocale === null)
+            defaultLocale = getDefaultLocale();
+
+        setDefaultLocale(locale);
+        try {
+            fn();
+        } finally {
+            setDefaultLocale(defaultLocale);
+        }
+    }
+    global.withLocale = withLocale;
+
     const Month = {
         January: 0,
         February: 1,
         March: 2,
         April: 3,
         May: 4,
         June: 5,
         July: 6,
--- a/js/src/tests/non262/Date/time-zone-2038-pst.js
+++ b/js/src/tests/non262/Date/time-zone-2038-pst.js
@@ -1,17 +1,17 @@
 // |reftest| skip-if(!xulRuntime.shell)
 
 // Note: The default time zone is set to PST8PDT for all jstests (when run in the shell).
 
 assertEq(/^(PST|PDT)$/.test(getTimeZone()), true);
 
 // U.S. daylight saving rules changed in 2007, excerpt from tzdata's
 // northamerica file:
-// NAME  FROM  TO    IN   ON       AT    SAVE  LETTER/S   
+// NAME  FROM  TO    IN   ON       AT    SAVE  LETTER/S
 // US    1967  2006  Oct  lastSun  2:00  0     S
 // US    1967  1973  Apr  lastSun  2:00  1:00  D
 // US    1974  only  Jan  6        2:00  1:00  D
 // US    1975  only  Feb  23       2:00  1:00  D
 // US    1976  1986  Apr  lastSun  2:00  1:00  D
 // US    1987  2006  Apr  Sun>=1   2:00  1:00  D
 // US    2007  max   Mar  Sun>=8   2:00  1:00  D
 // US    2007  max   Nov  Sun>=1   2:00  0     S
@@ -20,25 +20,25 @@ assertEq(/^(PST|PDT)$/.test(getTimeZone(
 // When 2040 is mapped to 1984, the old U.S. rules are applied, i.e. DST isn't
 // yet observed on March 31. If mapped to 2012, the new U.S. rules are applied
 // and DST is already observed, which is the expected behaviour.
 // A similar effect is visible in November.
 // NOTE: This test expects that 2012 and 2040 use the same DST rules. If this
 //       ever changes, the test needs to be updated accordingly.
 {
     let dt1 = new Date(2040, Month.March, 31);
-    assertDateTime(dt1, "Sat Mar 31 2040 00:00:00 GMT-0700 (PDT)");
+    assertDateTime(dt1, "Sat Mar 31 2040 00:00:00 GMT-0700 (PDT)", "Pacific Daylight Time");
 
     let dt2 = new Date(2040, Month.November, 1);
-    assertDateTime(dt2, "Thu Nov 01 2040 00:00:00 GMT-0700 (PDT)");
+    assertDateTime(dt2, "Thu Nov 01 2040 00:00:00 GMT-0700 (PDT)", "Pacific Daylight Time");
 }
 
 // 2038 is mapped to 2027 instead of 1971.
 {
     let dt1 = new Date(2038, Month.March, 31);
-    assertDateTime(dt1, "Wed Mar 31 2038 00:00:00 GMT-0700 (PDT)");
+    assertDateTime(dt1, "Wed Mar 31 2038 00:00:00 GMT-0700 (PDT)", "Pacific Daylight Time");
 
     let dt2 = new Date(2038, Month.November, 1);
-    assertDateTime(dt2, "Mon Nov 01 2038 00:00:00 GMT-0700 (PDT)");
+    assertDateTime(dt2, "Mon Nov 01 2038 00:00:00 GMT-0700 (PDT)", "Pacific Daylight Time");
 }
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
--- a/js/src/tests/non262/Date/time-zone-pst.js
+++ b/js/src/tests/non262/Date/time-zone-pst.js
@@ -1,20 +1,25 @@
 // |reftest| skip-if(!xulRuntime.shell)
 
 // Note: The default time zone is set to PST8PDT for all jstests (when run in the shell).
 
 assertEq(/^(PST|PDT)$/.test(getTimeZone()), true);
 
 const msPerMinute = 60 * 1000;
 
+function shortTimeZone(str) {
+    return str.replace("(Pacific Standard Time)", "(PST)")
+              .replace("(Pacific Daylight Time)", "(PDT)");
+}
+
 function assertEqDate(dt, date, time) {
-    assertEq(dt.toString(), `${date} ${time}`);
+    assertEq(shortTimeZone(dt.toString()), `${date} ${time}`);
     assertEq(dt.toDateString(), date);
-    assertEq(dt.toTimeString(), time);
+    assertEq(shortTimeZone(dt.toTimeString()), time);
 }
 
 // PDT -> PST, using milliseconds from epoch.
 {
     let midnight = new Date(2016, Month.November, 6, 0, 0, 0, 0);
     let midnightUTC = Date.UTC(2016, Month.November, 6, 0, 0, 0, 0);
 
     // Ensure midnight time is correct.
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/Date/time-zones-historic.js
@@ -0,0 +1,510 @@
+// |reftest| skip-if(xulRuntime.OS=="WINNT"||!this.hasOwnProperty("Intl")) -- Windows doesn't accept IANA names for the TZ env variable; Requires ICU time zone support
+
+// bug 487897
+inTimeZone("Europe/London", () => {
+    let dt1 = new Date(1970, Month.January, 1, 0, 0, 0, 0);
+    assertDateTime(dt1, "Thu Jan 01 1970 00:00:00 GMT+0100 (Greenwich Mean Time)");
+    assertEq(dt1.getHours(), 0);
+
+    let dt2 = new Date(1915, Month.January, 1);
+    assertDateTime(dt2, "Fri Jan 01 1915 00:00:00 GMT+0000 (Greenwich Mean Time)");
+
+    let dt3 = new Date(1970, Month.January, 1);
+    assertDateTime(dt3, "Thu Jan 01 1970 00:00:00 GMT+0100 (Greenwich Mean Time)");
+});
+
+// bug 637244
+inTimeZone("Asia/Novosibirsk", () => {
+    let dt1 = new Date("1942-03-01T00:00:00");
+    assertDateTime(dt1, "Sun Mar 01 1942 00:00:00 GMT+0700 (Novosibirsk Standard Time)");
+    dt1.setMonth(Month.April);
+    assertDateTime(dt1, "Wed Apr 01 1942 00:00:00 GMT+0700 (Novosibirsk Standard Time)");
+
+    let dt2 = new Date(2010, Month.October, 31);
+    assertDateTime(dt2, "Sun Oct 31 2010 00:00:00 GMT+0700 (Novosibirsk Summer Time)");
+    dt2.setMonth(Month.November);
+    assertDateTime(dt2, "Wed Dec 01 2010 00:00:00 GMT+0600 (Novosibirsk Standard Time)");
+
+    let dt3 = new Date(1942, Month.April, 1);
+    assertDateTime(dt3, "Wed Apr 01 1942 00:00:00 GMT+0700 (Novosibirsk Standard Time)");
+
+    function getNumberOfDaysInMonth(year, month) {
+        switch (month) {
+          case Month.January:
+          case Month.March:
+          case Month.May:
+          case Month.July:
+          case Month.August:
+          case Month.October:
+          case Month.December:
+            return 31;
+          case Month.April:
+          case Month.June:
+          case Month.September:
+          case Month.November:
+            return 30;
+          case Month.February:
+            if (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0))
+                return 29;
+            return 28;
+        }
+        throw new Error(`Illegal month value: ${month}`);
+    }
+
+    for (let year = 1900; year <= 2142; ++year) {
+        for (let month = Month.January; month <= Month.December; ++month) {
+            const numDays = getNumberOfDaysInMonth(year, month);
+            for (let day = 1; day <= numDays; ++day) {
+                let date = new Date(year, month, day);
+                assertEq(date.getMonth(), month);
+            }
+        }
+    }
+
+    let dt4 = new Date(1984, Month.April, 1);
+    assertDateTime(dt4, "Sun Apr 01 1984 01:00:00 GMT+0800 (Novosibirsk Summer Time)");
+
+    let dt5 = new Date(1984, Month.March, 1);
+    assertDateTime(dt5, "Thu Mar 01 1984 00:00:00 GMT+0700 (Novosibirsk Standard Time)");
+
+    let dt6 = new Date(1984, Month.April, 1);
+    assertEq(dt6.toUTCString(), "Sat, 31 Mar 1984 17:00:00 GMT");
+    assertEq(dt6.getTime(), 449600400000);
+});
+inTimeZone("Europe/Tallinn", () => {
+    let dt = new Date(2040, Month.March, 31, 20);
+
+    for (let {datetime, date, hours} of [
+        {datetime: "Sat Mar 31 2040 20:00:00", date: 31, hours: 20},
+        {datetime: "Sat Mar 31 2040 22:00:00", date: 31, hours: 22},
+        {datetime: "Sun Apr 01 2040 00:00:00", date: 1, hours: 0},
+        {datetime: "Sun Apr 01 2040 02:00:00", date: 1, hours: 2},
+        {datetime: "Sun Apr 01 2040 04:00:00", date: 1, hours: 4},
+        {datetime: "Sun Apr 01 2040 06:00:00", date: 1, hours: 6},
+        {datetime: "Sun Apr 01 2040 08:00:00", date: 1, hours: 8},
+    ]) {
+        assertDateTime(dt, `${datetime} GMT+0300 (Eastern European Summer Time)`);
+        assertEq(dt.getDate(), date);
+        assertEq(dt.getHours(), hours);
+        assertEq(dt.getTimezoneOffset(), -180);
+
+        dt.setHours(dt.getHours() + 2);
+    }
+});
+inTimeZone("Europe/Riga", () => {
+    let dt1 = new Date(2016, Month.March, 27, 2, 59);
+    assertDateTime(dt1, "Sun Mar 27 2016 02:59:00 GMT+0200 (Eastern European Standard Time)");
+
+    let dt2 = new Date(2016, Month.March, 27, 3, 0);
+    assertDateTime(dt2, "Sun Mar 27 2016 04:00:00 GMT+0300 (Eastern European Summer Time)");
+});
+
+// bug 704486
+inTimeZone("Europe/Zagreb", () => {
+    let dt = new Date(Date.UTC(1942, Month.June, 11, 22, 0, 0, 0));
+    assertDateTime(dt, "Fri Jun 12 1942 00:00:00 GMT+0200 (Central European Summer Time)");
+    assertEq(dt.getFullYear(), 1942);
+    assertEq(dt.getMonth(), Month.June);
+    assertEq(dt.getDate(), 12);
+    assertEq(dt.getHours(), 0);
+    assertEq(dt.getMinutes(), 0);
+    assertEq(dt.getSeconds(), 0);
+    assertEq(dt.getMilliseconds(), 0);
+    assertEq(dt.getTimezoneOffset(), -120);
+});
+
+// bug 935909
+inTimeZone("Europe/London", () => {
+    let dt1 = new Date(1954, Month.January, 1);
+    assertDateTime(dt1, "Fri Jan 01 1954 00:00:00 GMT+0000 (Greenwich Mean Time)");
+
+    let dt2 = new Date(1965, Month.January, 1);
+    assertDateTime(dt2, "Fri Jan 01 1965 00:00:00 GMT+0000 (Greenwich Mean Time)");
+
+    let dt3 = new Date(1970, Month.January, 1);
+    assertDateTime(dt3, "Thu Jan 01 1970 00:00:00 GMT+0100 (Greenwich Mean Time)");
+
+    let dt4 = new Date(-504921600000);
+    assertDateTime(dt4, "Fri Jan 01 1954 00:00:00 GMT+0000 (Greenwich Mean Time)");
+
+    let dt5 = new Date(1974, Month.January, 1);
+    assertDateTime(dt5, "Tue Jan 01 1974 00:00:00 GMT+0000 (Greenwich Mean Time)");
+});
+inTimeZone("Europe/Dublin", () => {
+    let dt = new Date(0, Month.January, 1, 0, 0, 0, 0);
+    dt.setFullYear(1970);
+    assertEq(dt.getFullYear(), 1970);
+});
+
+// bug 937261
+inTimeZone("Europe/Lisbon", () => {
+    let dt = new Date(0, Month.January, 1, 0, 0, 0, 0);
+    assertDateTime(dt, "Mon Jan 01 1900 00:00:00 GMT-0036 (Western European Standard Time)");
+
+    dt.setFullYear(2015);
+    assertDateTime(dt, "Thu Jan 01 2015 00:00:00 GMT+0000 (Western European Standard Time)");
+
+    dt.setMonth(Month.November);
+    assertDateTime(dt, "Sun Nov 01 2015 00:00:00 GMT+0000 (Western European Standard Time)");
+});
+inTimeZone("Europe/London", () => {
+    let dt = new Date(0, Month.January, 1, 0, 0, 0, 0);
+    assertDateTime(dt, "Mon Jan 01 1900 00:00:00 GMT+0000 (Greenwich Mean Time)");
+
+    dt.setFullYear(2015);
+    assertDateTime(dt, "Thu Jan 01 2015 00:00:00 GMT+0000 (Greenwich Mean Time)");
+
+    dt.setMonth(Month.November);
+    assertDateTime(dt, "Sun Nov 01 2015 00:00:00 GMT+0000 (Greenwich Mean Time)");
+});
+
+// bug 1079720
+inTimeZone("Europe/Moscow", () => {
+    let dt1 = new Date(2014, Month.January, 1);
+    assertDateTime(dt1, "Wed Jan 01 2014 00:00:00 GMT+0400 (Moscow Standard Time)");
+    assertEq(dt1.toISOString(), "2013-12-31T20:00:00.000Z");
+    assertEq(dt1.getHours(), 0);
+
+    let dt2 = new Date(2013, Month.January, 1);
+    assertDateTime(dt2, "Tue Jan 01 2013 00:00:00 GMT+0400 (Moscow Standard Time)");
+
+    let dt3 = new Date(new Date(2014, Month.December, 1).setMonth(Month.January));
+    assertDateTime(dt3, "Wed Jan 01 2014 00:00:00 GMT+0400 (Moscow Standard Time)");
+    assertEq(dt3.getFullYear(), 2014);
+
+    let dt4 = new Date(2040, Month.April, 1);
+    assertDateTime(dt4, "Sun Apr 01 2040 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt5 = new Date(2043, Month.April, 1);
+    assertDateTime(dt5, "Wed Apr 01 2043 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt6 = new Date(2054, Month.April, 1);
+    assertDateTime(dt6, "Wed Apr 01 2054 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt7 = new Date(2065, Month.April, 1);
+    assertDateTime(dt7, "Wed Apr 01 2065 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt8 = new Date(2068, Month.April, 1);
+    assertDateTime(dt8, "Sun Apr 01 2068 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt9 = new Date(2071, Month.April, 1);
+    assertDateTime(dt9, "Wed Apr 01 2071 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt10 = new Date(2082, Month.April, 1);
+    assertDateTime(dt10, "Wed Apr 01 2082 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt11 = new Date(2093, Month.April, 1);
+    assertDateTime(dt11, "Wed Apr 01 2093 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt12 = new Date(2096, Month.April, 1);
+    assertDateTime(dt12, "Sun Apr 01 2096 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt13 = new Date(2099, Month.April, 1);
+    assertDateTime(dt13, "Wed Apr 01 2099 00:00:00 GMT+0300 (Moscow Standard Time)");
+});
+
+// bug 1107837
+inTimeZone("Europe/Moscow", () => {
+    let dt1 = new Date(2015, Month.January, 4);
+    assertDateTime(dt1, "Sun Jan 04 2015 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt2 = new Date(2015, Month.January, 5);
+    assertDateTime(dt2, "Mon Jan 05 2015 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt3 = new Date(2015, Month.January, 6);
+    assertDateTime(dt3, "Tue Jan 06 2015 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt4 = new Date(2015, Month.January, 7);
+    assertDateTime(dt4, "Wed Jan 07 2015 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt5 = new Date(2015, Month.January, 8);
+    assertDateTime(dt5, "Thu Jan 08 2015 00:00:00 GMT+0300 (Moscow Standard Time)");
+
+    let dt6 = new Date(2015, Month.January, 9);
+    assertDateTime(dt6, "Fri Jan 09 2015 00:00:00 GMT+0300 (Moscow Standard Time)");
+});
+
+// bug 1122571
+inTimeZone("Europe/Berlin", () => {
+    const locale = "en-001";
+    const opts = {timeZoneName: "long", hour12: false};
+
+    let dt1 = new Date(1950, Month.March, 28);
+    assertDateTime(dt1, "Tue Mar 28 1950 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt1.toLocaleString(locale, opts), "28/03/1950, 00:00:00 GMT+01:00");
+
+    let dt2 = new Date(1950, Month.July, 1);
+    assertDateTime(dt2, "Sat Jul 01 1950 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt2.toLocaleString(locale, opts), "01/07/1950, 00:00:00 GMT+01:00");
+
+    let dt3 = new Date(1960, Month.March, 27);
+    assertDateTime(dt3, "Sun Mar 27 1960 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt3.toLocaleString(locale, opts), "27/03/1960, 00:00:00 GMT+01:00");
+
+    let dt4 = new Date(1960, Month.March, 28);
+    assertDateTime(dt4, "Mon Mar 28 1960 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt4.toLocaleString(locale, opts), "28/03/1960, 00:00:00 GMT+01:00");
+
+    let dt5 = new Date(1960, Month.March, 29);
+    assertDateTime(dt5, "Tue Mar 29 1960 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt5.toLocaleString(locale, opts), "29/03/1960, 00:00:00 GMT+01:00");
+
+    let dt6 = new Date(1960, Month.July, 1);
+    assertDateTime(dt6, "Fri Jul 01 1960 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt6.toLocaleString(locale, opts), "01/07/1960, 00:00:00 GMT+01:00");
+
+    let dt7 = new Date(1961, Month.July, 1);
+    assertDateTime(dt7, "Sat Jul 01 1961 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt7.toLocaleString(locale, opts), "01/07/1961, 00:00:00 GMT+01:00");
+
+    let dt8 = new Date(1970, Month.March, 1);
+    assertDateTime(dt8, "Sun Mar 01 1970 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt8.toLocaleString(locale, opts), "01/03/1970, 00:00:00 Central European Standard Time");
+
+    let dt9 = new Date(1970, Month.March, 27);
+    assertDateTime(dt9, "Fri Mar 27 1970 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt9.toLocaleString(locale, opts), "27/03/1970, 00:00:00 Central European Standard Time");
+
+    let dt10 = new Date(1970, Month.March, 28);
+    assertDateTime(dt10, "Sat Mar 28 1970 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt10.toLocaleString(locale, opts), "28/03/1970, 00:00:00 Central European Standard Time");
+
+    let dt11 = new Date(1970, Month.July, 1);
+    assertDateTime(dt11, "Wed Jul 01 1970 00:00:00 GMT+0100 (Central European Standard Time)");
+    assertEq(dt11.toLocaleString(locale, opts), "01/07/1970, 00:00:00 Central European Standard Time");
+});
+
+// bug 1143398
+inTimeZone("Australia/Adelaide", () => {
+    let dt = new Date(621000);
+    assertDateTime(dt, "Thu Jan 01 1970 09:40:21 GMT+0930 (Australian Central Standard Time)");
+    assertEq(dt.getUTCFullYear(), 1970);
+
+    dt.setMilliseconds(dt.getMilliseconds());
+    dt.setSeconds(dt.getSeconds());
+
+    assertDateTime(dt, "Thu Jan 01 1970 09:40:21 GMT+0930 (Australian Central Standard Time)");
+    assertEq(dt.getUTCFullYear(), 1970);
+});
+inTimeZone("America/Denver", () => {
+    let dt = new Date(1446361200000);
+    assertDateTime(dt, "Sun Nov 01 2015 01:00:00 GMT-0600 (Mountain Daylight Time)");
+
+    assertEq(dt.getTime(), 1446361200000);
+    dt.setMilliseconds(0);
+    assertEq(dt.getTime(), 1446361200000);
+});
+
+// bug 1233809
+inTimeZone("America/New_York", () => {
+    let dt = new Date(1980, Month.March, 10);
+    assertDateTime(dt, "Mon Mar 10 1980 00:00:00 GMT-0500 (Eastern Standard Time)");
+});
+
+// bug 1254041
+inTimeZone("Asia/Ho_Chi_Minh", () => {
+    let dt1 = new Date(Date.UTC(1969, Month.December, 31, 23, 0));
+    assertDateTime(dt1, "Thu Jan 01 1970 07:00:00 GMT+0800 (Indochina Time)");
+    assertEq(dt1.getTime(), -3600000);
+
+    dt1.setMinutes(dt1.getMinutes() + 30);
+    assertDateTime(dt1, "Thu Jan 01 1970 07:30:00 GMT+0800 (Indochina Time)");
+    assertEq(dt1.getTime(), -1800000);
+
+    dt1.setMinutes(dt1.getMinutes() + 30);
+    assertDateTime(dt1, "Thu Jan 01 1970 08:00:00 GMT+0800 (Indochina Time)");
+    assertEq(dt1.getTime(), 0);
+
+    dt1.setMinutes(dt1.getMinutes() + 30);
+    assertDateTime(dt1, "Thu Jan 01 1970 08:30:00 GMT+0800 (Indochina Time)");
+    assertEq(dt1.getTime(), 1800000);
+
+    dt1.setMinutes(dt1.getMinutes() + 30);
+    assertDateTime(dt1, "Thu Jan 01 1970 09:00:00 GMT+0800 (Indochina Time)");
+    assertEq(dt1.getTime(), 3600000);
+
+    let dt2 = new Date(-1);
+    assertEq(dt2.getTime(), -1);
+
+    dt2.setMilliseconds(dt2.getMilliseconds() + 1);
+    assertEq(dt2.getTime(), 0);
+
+    dt2.setTime(1);
+    assertEq(dt2.getTime(), 1);
+
+    dt2.setMilliseconds(dt2.getMilliseconds() - 1);
+    assertEq(dt2.getTime(), 0);
+
+    dt2.setMilliseconds(dt2.getMilliseconds() - 1);
+    assertEq(dt2.getTime(), -1);
+
+    dt2.setMilliseconds(dt2.getMilliseconds() + 1);
+    assertEq(dt2.getTime(), 0);
+
+    dt2.setMilliseconds(dt2.getMilliseconds() + 3600000);
+    assertEq(dt2.getTime(), 3600000);
+
+    dt2.setMilliseconds(dt2.getMilliseconds() + (3600000 * 2 - 1));
+    assertEq(dt2.getTime(), 3600000 * 3 - 1);
+
+    dt2.setMilliseconds(dt2.getMilliseconds() + 1);
+    assertEq(dt2.getTime(), 3600000 * 3);
+
+    dt2.setMilliseconds(dt2.getMilliseconds() + (3600000 * 2));
+    assertEq(dt2.getTime(), 3600000 * 5);
+
+    let dt3 = new Date(0);
+    assertDateTime(dt3, "Thu Jan 01 1970 08:00:00 GMT+0800 (Indochina Time)");
+
+    let dt4 = new Date(-1);
+    assertDateTime(dt4, "Thu Jan 01 1970 07:59:59 GMT+0800 (Indochina Time)");
+});
+
+// bug 1300197
+inTimeZone("Europe/Dublin", () => {
+    let dt = new Date(1910, Month.January, 1);
+    assertDateTime(dt, "Sat Jan 01 1910 00:00:00 GMT-0025 (Greenwich Mean Time)");
+});
+
+// bug 1304774
+inTimeZone("Europe/Zurich", () => {
+    let dt = new Date(1986, Month.April, 4, 0, 0, 0, 0);
+    assertDateTime(dt, "Fri Apr 04 1986 00:00:00 GMT+0200 (Central European Summer Time)");
+    assertEq(dt.getTimezoneOffset(), -120);
+});
+
+// bug 1330307
+inTimeZone("Europe/Moscow", () => {
+    let dt = new Date(2012, Month.May, 14, 12, 13, 14);
+    assertDateTime(dt, "Mon May 14 2012 12:13:14 GMT+0400 (Moscow Standard Time)");
+
+    let dtf = new Intl.DateTimeFormat("en-US", {hour: "numeric", minute: "numeric"});
+    assertEq(dtf.format(dt), "12:13 PM");
+});
+inTimeZone("Asia/Baku", () => {
+    let dt = new Date(2012, Month.May, 14, 12, 13, 14);
+    assertDateTime(dt, "Mon May 14 2012 12:13:14 GMT+0500 (Azerbaijan Summer Time)");
+
+    let dtf = new Intl.DateTimeFormat("en-US", {hour: "numeric", minute: "numeric"});
+    assertEq(dtf.format(dt), "12:13 PM");
+});
+inTimeZone("Asia/Tbilisi", () => {
+    let dt = new Date(2012, Month.May, 14, 12, 13, 14);
+    assertDateTime(dt, "Mon May 14 2012 12:13:14 GMT+0400 (Georgia Standard Time)");
+
+    let dtf = new Intl.DateTimeFormat("en-US", {hour: "numeric", minute: "numeric"});
+    assertEq(dtf.format(dt), "12:13 PM");
+});
+
+// bug 1335818
+inTimeZone("Asia/Jerusalem", () => {
+    let dt1 = new Date(2013, Month.March, 22, 1, 0, 0, 0);
+    assertDateTime(dt1, "Fri Mar 22 2013 01:00:00 GMT+0200 (Israel Standard Time)");
+
+    let dt2 = new Date(2013, Month.March, 22, 2, 0, 0, 0);
+    assertDateTime(dt2, "Fri Mar 22 2013 02:00:00 GMT+0200 (Israel Standard Time)");
+
+    let dt3 = new Date(2013, Month.March, 22, 3, 0, 0, 0);
+    assertDateTime(dt3, "Fri Mar 22 2013 03:00:00 GMT+0200 (Israel Standard Time)");
+
+    let dt4 = new Date(2013, Month.March, 29, 1, 0, 0, 0);
+    assertDateTime(dt4, "Fri Mar 29 2013 01:00:00 GMT+0200 (Israel Standard Time)");
+
+    let dt5 = new Date(2013, Month.March, 29, 2, 0, 0, 0);
+    assertDateTime(dt5, "Fri Mar 29 2013 03:00:00 GMT+0300 (Israel Daylight Time)");
+
+    let dt6 = new Date(2013, Month.March, 29, 3, 0, 0, 0);
+    assertDateTime(dt6, "Fri Mar 29 2013 03:00:00 GMT+0300 (Israel Daylight Time)");
+});
+
+// bug 1342853
+inTimeZone("America/Sao_Paulo", () => {
+    let dt1 = new Date(2017, Month.October, 14, 12, 0, 0);
+    assertDateTime(dt1, "Sat Oct 14 2017 12:00:00 GMT-0300 (Brasilia Standard Time)");
+    assertEq(dt1.getTimezoneOffset(), 180);
+
+    let dt2 = new Date(2017, Month.October, 15, 12, 0, 0);
+    assertDateTime(dt2, "Sun Oct 15 2017 12:00:00 GMT-0200 (Brasilia Summer Time)");
+    assertEq(dt2.getTimezoneOffset(), 120);
+
+    let dt3 = new Date(2018, Month.February, 17, 12, 0, 0);
+    assertDateTime(dt3, "Sat Feb 17 2018 12:00:00 GMT-0200 (Brasilia Summer Time)");
+    assertEq(dt3.getTimezoneOffset(), 120);
+
+    let dt4 = new Date(2018, Month.February, 18, 12, 0, 0);
+    assertDateTime(dt4, "Sun Feb 18 2018 12:00:00 GMT-0300 (Brasilia Standard Time)");
+    assertEq(dt4.getTimezoneOffset(), 180);
+
+    let dt5 = new Date(2018, Month.November, 3, 12, 0, 0);
+    assertDateTime(dt5, "Sat Nov 03 2018 12:00:00 GMT-0300 (Brasilia Standard Time)");
+    assertEq(dt5.getTimezoneOffset(), 180);
+
+    let dt6 = new Date(2018, Month.November, 4, 12, 0, 0);
+    assertDateTime(dt6, "Sun Nov 04 2018 12:00:00 GMT-0200 (Brasilia Summer Time)");
+    assertEq(dt6.getTimezoneOffset(), 120);
+
+    let dt7 = new Date(2019, Month.February, 16, 12, 0, 0);
+    assertDateTime(dt7, "Sat Feb 16 2019 12:00:00 GMT-0200 (Brasilia Summer Time)");
+    assertEq(dt7.getTimezoneOffset(), 120);
+
+    let dt8 = new Date(2019, Month.February, 17, 12, 0, 0);
+    assertDateTime(dt8, "Sun Feb 17 2019 12:00:00 GMT-0300 (Brasilia Standard Time)");
+    assertEq(dt8.getTimezoneOffset(), 180);
+});
+
+// bug 1365192
+inTimeZone("America/Asuncion", () => {
+    let dt = new Date(2018, Month.March, 31);
+    assertDateTime(dt, "Sat Mar 31 2018 00:00:00 GMT-0400 (Paraguay Standard Time)");
+    assertEq(dt.getTimezoneOffset(), 240);
+});
+
+// bug 1385643
+inTimeZone("Europe/Warsaw", () => {
+    let shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+    let dtf = new Intl.DateTimeFormat("en-US", {month: "short"});
+
+    for (let year = 1900; year <= 2100; ++year) {
+        for (let month = Month.January; month <= Month.December; ++month) {
+            let date = new Date(year, month, 1);
+            assertEq(dtf.format(date), shortMonths[month]);
+        }
+    }
+});
+
+// bug 1401696
+inTimeZone("Europe/Berlin", () => {
+    let dt = new Date(1970, Month.January, 1, 0, 0, 0);
+    assertDateTime(dt, "Thu Jan 01 1970 00:00:00 GMT+0100 (Central European Standard Time)");
+});
+
+// bug 1459842
+inTimeZone("Europe/Moscow", () => {
+    let dt1 = new Date(1004, Month.April, 1, 2, 0, 0);
+    assertDateTime(dt1, "Sun Apr 01 1004 02:00:00 GMT+0230 (Moscow Standard Time)");
+    assertEq(dt1.getHours(), 2);
+
+    let dt2 = new Date(1004, Month.April, 1, 1, 0, 0);
+    assertDateTime(dt2, "Sun Apr 01 1004 01:00:00 GMT+0230 (Moscow Standard Time)");
+    assertEq(dt2.getHours(), 1);
+
+    let dt3 = new Date(1004, Month.April, 1, 0, 0, 0);
+    assertDateTime(dt3, "Sun Apr 01 1004 00:00:00 GMT+0230 (Moscow Standard Time)");
+    assertEq(dt3.getHours(), 0);
+
+    let dt4 = new Date(1004, Month.March, 1, 2, 0, 0);
+    assertDateTime(dt4, "Thu Mar 01 1004 02:00:00 GMT+0230 (Moscow Standard Time)");
+    assertEq(dt4.getHours(), 2);
+
+    let dt5 = new Date(1004, Month.March, 1, 1, 0, 0);
+    assertDateTime(dt5, "Thu Mar 01 1004 01:00:00 GMT+0230 (Moscow Standard Time)");
+    assertEq(dt5.getHours(), 1);
+
+    let dt6 = new Date(1004, Month.March, 1, 0, 0, 0);
+    assertDateTime(dt6, "Thu Mar 01 1004 00:00:00 GMT+0230 (Moscow Standard Time)");
+    assertEq(dt6.getHours(), 0);
+
+});
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/Date/time-zones-imported.js
@@ -0,0 +1,992 @@
+// |reftest| skip-if(xulRuntime.OS=="WINNT"||!this.hasOwnProperty("Intl")) -- Windows doesn't accept IANA names for the TZ env variable; Requires ICU time zone support
+
+// Imported tests from es6draft and then adapted to use ICU/CLDR time zone display names.
+
+function assertSame(expected, actual, message = undefined) {
+  if (message !== undefined)
+    assertEq(actual, expected, String(message));
+  else
+    assertEq(actual, expected);
+}
+
+function assertTrue(actual, message = undefined) {
+  assertSame(true, actual, message);
+}
+
+// File: lib/datetime.jsm
+const {
+  DayOfWeek,
+  Month,
+  DateTime,
+  TimeZone,
+  Format,
+} = (function() {
+
+// 5.2 Algorithm Conventions
+function modulo(dividend, divisor) {
+  assertTrue(typeof dividend === "number");
+  assertTrue(typeof divisor === "number");
+  assertTrue(divisor !== 0 && Number.isFinite(divisor));
+  let remainder = dividend % divisor;
+  // NB: add +0 to convert -0 to +0
+  return (remainder >= 0 ? remainder + 0 : remainder + divisor);
+}
+
+// 7.1.4 ToInteger ( argument )
+function ToInteger(number) {
+  /* steps 1-2 */
+  assertTrue(typeof number === "number");
+  /* step 3 */
+  if (Number.isNaN(number))
+    return +0.0;
+  /* step 4 */
+  if (number == 0.0 || !Number.isFinite(number))
+    return number;
+  /* step 5 */
+  return Math.sign(number) * Math.floor(Math.abs(number));
+}
+
+// 20.3.1.2 Day Number and Time within Day
+const msPerDay = 86400000;
+
+// 20.3.1.2 Day Number and Time within Day
+function Day(t) {
+  assertTrue(typeof t === "number");
+  return Math.floor(t / msPerDay);
+}
+
+// 20.3.1.2 Day Number and Time within Day
+function TimeWithinDay(t) {
+  assertTrue(typeof t === "number");
+  return modulo(t, msPerDay);
+}
+
+// 20.3.1.3 Year Number
+function DaysInYear(y) {
+  assertTrue(typeof y === "number");
+  if (y % 4 !== 0) {
+    return 365;
+  }
+  if (y % 100 !== 0) {
+    return 366;
+  }
+  if (y % 400 !== 0) {
+    return 365;
+  }
+  return 366;
+}
+
+// 20.3.1.3 Year Number
+function DayFromYear(y) {
+  assertTrue(typeof y === "number");
+  return 365 * (y - 1970) + Math.floor((y - 1969) / 4) - Math.floor((y - 1901) / 100) + Math.floor((y - 1601) / 400);
+}
+
+// 20.3.1.3 Year Number
+function TimeFromYear(y) {
+  assertTrue(typeof y === "number");
+  return msPerDay * DayFromYear(y);
+}
+
+// TODO: fill in rest
+
+// 20.3.1.10 Hours, Minutes, Second, and Milliseconds
+const HoursPerDay = 24;
+const MinutesPerHour = 60;
+const SecondsPerMinute = 60;
+const msPerSecond = 1000;
+const msPerMinute = msPerSecond * SecondsPerMinute;
+const msPerHour = msPerMinute * MinutesPerHour;
+
+// 20.3.1.10 Hours, Minutes, Second, and Milliseconds
+function HourFromTime(t) {
+  assertTrue(typeof t === "number");
+  return modulo(Math.floor(t / msPerHour), HoursPerDay);
+}
+
+// 20.3.1.10 Hours, Minutes, Second, and Milliseconds
+function MinFromTime(t) {
+  assertTrue(typeof t === "number");
+  return modulo(Math.floor(t / msPerMinute), MinutesPerHour);
+}
+
+// 20.3.1.10 Hours, Minutes, Second, and Milliseconds
+function SecFromTime(t) {
+  assertTrue(typeof t === "number");
+  return modulo(Math.floor(t / msPerSecond), SecondsPerMinute);
+}
+
+// 20.3.1.10 Hours, Minutes, Second, and Milliseconds
+function msFromTime(t) {
+  assertTrue(typeof t === "number");
+  return modulo(t, msPerSecond);
+}
+
+// 20.3.1.11 MakeTime (hour, min, sec, ms)
+function MakeTime(hour, min, sec, ms) {
+  assertTrue(typeof hour === "number");
+  assertTrue(typeof min === "number");
+  assertTrue(typeof sec === "number");
+  assertTrue(typeof ms === "number");
+  if (!Number.isFinite(hour) || !Number.isFinite(min) || !Number.isFinite(sec) || !Number.isFinite(ms)) {
+    return Number.NaN;
+  }
+  let h = ToInteger(hour);
+  let m = ToInteger(min);
+  let s = ToInteger(sec);
+  let milli = ToInteger(ms);
+  let t = h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
+  return t;
+}
+
+// 20.3.1.12 MakeDay (year, month, date)
+function MakeDay(year, month, date) {
+  assertTrue(typeof year === "number");
+  assertTrue(typeof month === "number");
+  assertTrue(typeof date === "number");
+  if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(date)) {
+    return Number.NaN;
+  }
+  let y = ToInteger(year);
+  let m = ToInteger(month);
+  let dt = ToInteger(date);
+  let ym = y + Math.floor(m / 12);
+  let mn = modulo(m, 12);
+
+  const monthStart = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
+  let day = Math.floor(TimeFromYear(ym) / msPerDay) + monthStart[mn];
+  if (mn >= 2 && DaysInYear(ym) == 366) {
+    day += 1;
+  }
+
+  return day + dt - 1;
+}
+
+// 20.3.1.13 MakeDate (day, time)
+function MakeDate(day, time) {
+  assertTrue(typeof day === "number");
+  assertTrue(typeof time === "number");
+  if (!Number.isFinite(day) || !Number.isFinite(time)) {
+    return Number.NaN;
+  }
+  return day * msPerDay + time;
+}
+
+// 20.3.1.14 TimeClip (time)
+function TimeClip(time) {
+  assertTrue(typeof time === "number");
+  if (!Number.isFinite(time)) {
+    return Number.NaN;
+  }
+  if (Math.abs(time) > 8.64e15) {
+    return Number.NaN;
+  }
+  return ToInteger(time) + (+0);
+}
+
+const DayOfWeek = {
+  Sunday: 0,
+  Monday: 1,
+  Tuesday: 2,
+  Wednesday: 3,
+  Thursday: 4,
+  Friday: 5,
+  Saturday: 6,
+};
+
+const Month = {
+  January: 0,
+  February: 1,
+  March: 2,
+  April: 3,
+  May: 4,
+  June: 5,
+  July: 6,
+  August: 7,
+  September: 8,
+  October: 9,
+  November: 10,
+  December: 11,
+};
+
+const DateTime = {
+  Local: class {
+    constructor(year, month, day, weekday, hour = 0, minute = 0, second = 0, ms = 0) {
+      Object.assign(this, {year, month, day, weekday, hour, minute, second, ms});
+    }
+
+    toDate() {
+      return new Date(this.year, this.month, this.day, this.hour, this.minute, this.second, this.ms);
+    }
+  },
+  UTC: class {
+    constructor(year, month, day, weekday, hour = 0, minute = 0, second = 0, ms = 0) {
+      Object.assign(this, {year, month, day, weekday, hour, minute, second, ms});
+    }
+
+    toInstant() {
+      return MakeDate(MakeDay(this.year, this.month, this.day), MakeTime(this.hour, this.minute, this.second, this.ms));
+    }
+  },
+};
+
+function TimeZone(hour, minute = 0, second = 0) {
+  return new class TimeZone {
+    constructor(hour, minute, second) {
+      Object.assign(this, {hour, minute, second});
+    }
+
+    toOffset() {
+      let offset = TimeZoneOffset(this.hour, this.minute, this.second);
+      return offset !== 0 ? -offset : 0;
+    }
+  }(hour, minute, second);
+
+  function TimeZoneOffset(hour, minute = 0, second = 0) {
+    assertTrue(typeof hour === "number");
+    assertTrue(typeof minute === "number");
+    assertTrue(typeof second === "number");
+    assertTrue(minute >= 0);
+    assertTrue(second >= 0);
+    if (hour < 0 || Object.is(-0, hour)) {
+      return hour * MinutesPerHour - minute - (second / 60);
+    }
+    return hour * MinutesPerHour + minute + (second / 60);
+  }
+}
+
+const Format = {
+  Locale: "en-US",
+  DateTime: {
+    localeMatcher: "lookup",
+    timeZone: void 0,
+    weekday: "short",
+    era: void 0,
+    year: "numeric",
+    month: "2-digit",
+    day: "2-digit",
+    hour: "2-digit",
+    minute: "2-digit",
+    second: "2-digit",
+    timeZoneName: "short",
+    formatMatcher: "best fit",
+    hour12: void 0,
+  },
+  Date: {
+    localeMatcher: "lookup",
+    timeZone: void 0,
+    weekday: "short",
+    era: void 0,
+    year: "numeric",
+    month: "2-digit",
+    day: "2-digit",
+    hour: void 0,
+    minute: void 0,
+    second: void 0,
+    timeZoneName: void 0,
+    formatMatcher: "best fit",
+    hour12: void 0,
+  },
+  Time: {
+    localeMatcher: "lookup",
+    timeZone: void 0,
+    weekday: void 0,
+    era: void 0,
+    year: void 0,
+    month: void 0,
+    day: void 0,
+    hour: "2-digit",
+    minute: "2-digit",
+    second: "2-digit",
+    timeZoneName: "short",
+    formatMatcher: "best fit",
+    hour12: void 0,
+  },
+};
+
+return {
+  DayOfWeek,
+  Month,
+  DateTime,
+  TimeZone,
+  Format,
+};
+})();
+
+
+// File: lib/assert-datetime.js
+
+function assertDate(local, utc, timeZone, options, formatArgs) {
+  let d = local.toDate();
+  assertDateValue(d, utc.toInstant(), timeZone.toOffset());
+  assertLocalDate(d, local);
+  assertUTCDate(d, utc);
+  assertDateString(d, options, formatArgs);
+}
+
+function assertDateValue(actual, dateValue, timeZoneOffset) {
+  assertSame(dateValue, actual.valueOf(), `valueOf()[${dateValue - actual.valueOf()}]`);
+  assertSame(dateValue, actual.getTime(), `valueOf()[${dateValue - actual.getTime()}]`);
+  assertSame(timeZoneOffset, actual.getTimezoneOffset(), "getTimezoneOffset()");
+}
+
+function assertLocalDate(actual, {year, month, day, weekday, hour = 0, minute = 0, second = 0, ms = 0}) {
+  assertSame(year, actual.getFullYear(), "getFullYear()");
+  assertSame(month, actual.getMonth(), "getMonth()");
+  assertSame(day, actual.getDate(), "getDate()");
+  assertSame(weekday, actual.getDay(), "getDay()");
+  assertSame(hour, actual.getHours(), "getHours()");
+  assertSame(minute, actual.getMinutes(), "getMinutes()");
+  assertSame(second, actual.getSeconds(), "getSeconds()");
+  assertSame(ms, actual.getMilliseconds(), "getMilliseconds()");
+}
+
+function assertUTCDate(actual, {year, month, day, weekday, hour = 0, minute = 0, second = 0, ms = 0}) {
+  assertSame(year, actual.getUTCFullYear(), "getUTCFullYear()");
+  assertSame(month, actual.getUTCMonth(), "getUTCMonth()");
+  assertSame(day, actual.getUTCDate(), "getUTCDate()");
+  assertSame(weekday, actual.getUTCDay(), "getUTCDay()");
+  assertSame(hour, actual.getUTCHours(), "getUTCHours()");
+  assertSame(minute, actual.getUTCMinutes(), "getUTCMinutes()");
+  assertSame(second, actual.getUTCSeconds(), "getUTCSeconds()");
+  assertSame(ms, actual.getUTCMilliseconds(), "getUTCMilliseconds()");
+}
+
+function assertDateString(actual, options, formatArgs = {
+  LocaleString: [Format.Locale, Format.DateTime],
+  LocaleDateString: [Format.Locale, Format.Date],
+  LocaleTimeString: [Format.Locale, Format.Time],
+}) {
+  for (var key of Object.keys(options)) {
+    var args = formatArgs[key] || [];
+    assertSame(options[key], actual[`to${key}`](...args), `to${key}()`);
+  }
+}
+
+
+// File: Date/Africa_Monrovia.js
+// Liberia was the last country to switch to UTC based offsets (1972 May).
+
+inTimeZone("Africa/Monrovia", () => {
+{
+  let local = new DateTime.Local(1972, Month.January, 6, DayOfWeek.Thursday, 0, 0, 0);
+  let utc = new DateTime.UTC(1972, Month.January, 6, DayOfWeek.Thursday, 0, 44, 30);
+
+  assertDate(local, utc, TimeZone(-0,44,30), {
+    String: "Thu Jan 06 1972 00:00:00 GMT-0044 (Greenwich Mean Time)",
+    UTCString: "Thu, 06 Jan 1972 00:44:30 GMT",
+  });
+}
+
+{
+  let local = new DateTime.Local(1972, Month.January, 6, DayOfWeek.Thursday, 23, 59, 0);
+  let utc = new DateTime.UTC(1972, Month.January, 7, DayOfWeek.Friday, 0, 43, 30);
+
+  assertDate(local, utc, TimeZone(-0,44,30), {
+    String: "Thu Jan 06 1972 23:59:00 GMT-0044 (Greenwich Mean Time)",
+    UTCString: "Fri, 07 Jan 1972 00:43:30 GMT",
+  });
+}
+
+{
+  let local = new DateTime.Local(1972, Month.January, 7, DayOfWeek.Friday, 0, 0, 0);
+  let utc = new DateTime.UTC(1972, Month.January, 7, DayOfWeek.Friday, 0, 44, 30);
+
+  assertDateValue(local.toDate(), utc.toInstant(), TimeZone(+0).toOffset());
+
+  assertDateString(local.toDate(), {
+    String: "Fri Jan 07 1972 00:44:30 GMT+0000 (Greenwich Mean Time)",
+    UTCString: "Fri, 07 Jan 1972 00:44:30 GMT",
+  });
+}
+
+{
+  let local = new DateTime.Local(1972, Month.January, 7, DayOfWeek.Friday, 0, 44, 30);
+  let utc = new DateTime.UTC(1972, Month.January, 7, DayOfWeek.Friday, 0, 44, 30);
+
+  assertDate(local, utc, TimeZone(+0), {
+    String: "Fri Jan 07 1972 00:44:30 GMT+0000 (Greenwich Mean Time)",
+    UTCString: "Fri, 07 Jan 1972 00:44:30 GMT",
+  });
+}
+
+{
+  let local = new DateTime.Local(1972, Month.January, 7, DayOfWeek.Friday, 0, 45, 0);
+  let utc = new DateTime.UTC(1972, Month.January, 7, DayOfWeek.Friday, 0, 45, 0);
+
+  assertDate(local, utc, TimeZone(+0), {
+    String: "Fri Jan 07 1972 00:45:00 GMT+0000 (Greenwich Mean Time)",
+    UTCString: "Fri, 07 Jan 1972 00:45:00 GMT",
+  });
+}
+
+{
+  let local = new DateTime.Local(1972, Month.January, 8, DayOfWeek.Saturday, 0, 0, 0);
+  let utc = new DateTime.UTC(1972, Month.January, 8, DayOfWeek.Saturday, 0, 0, 0);
+
+  assertDate(local, utc, TimeZone(+0), {
+    String: "Sat Jan 08 1972 00:00:00 GMT+0000 (Greenwich Mean Time)",
+    UTCString: "Sat, 08 Jan 1972 00:00:00 GMT",
+  });
+}
+});
+
+
+// File: Date/Africa_Monrovia.js
+// Africa/Tripoli switched from +02:00 to +01:00 and back.
+
+inTimeZone("Africa/Tripoli", () => {
+{
+  // +02:00 (standard time)
+  let local = new DateTime.Local(2012, Month.November, 1, DayOfWeek.Thursday, 0, 0, 0);
+  let utc = new DateTime.UTC(2012, Month.October, 31, DayOfWeek.Wednesday, 22, 0, 0);
+
+  assertDate(local, utc, TimeZone(+2), {
+    String: "Thu Nov 01 2012 00:00:00 GMT+0200 (Eastern European Standard Time)",
+    UTCString: "Wed, 31 Oct 2012 22:00:00 GMT",
+  });
+}
+
+{
+  // +01:00 (standard time)
+  let local = new DateTime.Local(2012, Month.December, 1, DayOfWeek.Saturday, 0, 0, 0);
+  let utc = new DateTime.UTC(2012, Month.November, 30, DayOfWeek.Friday, 23, 0, 0);
+
+  assertDate(local, utc, TimeZone(+1), {
+    String: "Sat Dec 01 2012 00:00:00 GMT+0100 (Eastern European Standard Time)",
+    UTCString: "Fri, 30 Nov 2012 23:00:00 GMT",
+  });
+}
+
+{
+  // +01:00 (daylight savings)
+  let local = new DateTime.Local(2013, Month.October, 1, DayOfWeek.Tuesday, 0, 0, 0);
+  let utc = new DateTime.UTC(2013, Month.September, 30, DayOfWeek.Monday, 22, 0, 0);
+
+  assertDate(local, utc, TimeZone(+2), {
+    String: "Tue Oct 01 2013 00:00:00 GMT+0200 (Eastern European Summer Time)",
+    UTCString: "Mon, 30 Sep 2013 22:00:00 GMT",
+  });
+}
+
+{
+  // +02:00 (standard time)
+  let local = new DateTime.Local(2013, Month.November, 1, DayOfWeek.Friday, 0, 0, 0);
+  let utc = new DateTime.UTC(2013, Month.October, 31, DayOfWeek.Thursday, 22, 0, 0);
+
+  assertDate(local, utc, TimeZone(+2), {
+    String: "Fri Nov 01 2013 00:00:00 GMT+0200 (Eastern European Standard Time)",
+    UTCString: "Thu, 31 Oct 2013 22:00:00 GMT",
+  });
+}
+});
+
+
+// File: Date/America_Caracas.js
+// America/Caracas switched from -04:00 to -04:30 on 2007 Dec 9.
+
+inTimeZone("America/Caracas", () => {
+{
+  // -04:00 (standard time)
+  let local = new DateTime.Local(2007, Month.December, 5, DayOfWeek.Wednesday, 0, 0, 0);
+  let utc = new DateTime.UTC(2007, Month.December, 5, DayOfWeek.Wednesday, 4, 0, 0);
+
+  assertDate(local, utc, TimeZone(-4), {
+    String: "Wed Dec 05 2007 00:00:00 GMT-0400 (Venezuela Time)",
+    DateString: "Wed Dec 05 2007",
+    TimeString: "00:00:00 GMT-0400 (Venezuela Time)",
+    UTCString: "Wed, 05 Dec 2007 04:00:00 GMT",
+    ISOString: "2007-12-05T04:00:00.000Z",
+    LocaleString: "Wed, 12/05/2007, 12:00:00 AM GMT-4",
+    LocaleDateString: "Wed, 12/05/2007",
+    LocaleTimeString: "12:00:00 AM GMT-4",
+  });
+}
+
+{
+  // -04:30 (standard time)
+  let local = new DateTime.Local(2007, Month.December, 12, DayOfWeek.Wednesday, 0, 0, 0);
+  let utc = new DateTime.UTC(2007, Month.December, 12, DayOfWeek.Wednesday, 4, 30, 0);
+
+  assertDate(local, utc, TimeZone(-4, 30), {
+    String: "Wed Dec 12 2007 00:00:00 GMT-0430 (Venezuela Time)",
+    DateString: "Wed Dec 12 2007",
+    TimeString: "00:00:00 GMT-0430 (Venezuela Time)",
+    UTCString: "Wed, 12 Dec 2007 04:30:00 GMT",
+    ISOString: "2007-12-12T04:30:00.000Z",
+    LocaleString: "Wed, 12/12/2007, 12:00:00 AM GMT-4:30",
+    LocaleDateString: "Wed, 12/12/2007",
+    LocaleTimeString: "12:00:00 AM GMT-4:30",
+  });
+}
+});
+
+
+// File: Date/Australia_Lord_Howe.js
+// Australia/Lord_Howe time zone offset is +10:30 and daylight savings amount is 00:30.
+
+inTimeZone("Australia/Lord_Howe", () => {
+{
+  // +10:30 (standard time)
+  let local = new DateTime.Local(2010, Month.August, 1, DayOfWeek.Sunday, 0, 0, 0);
+  let utc = new DateTime.UTC(2010, Month.July, 31, DayOfWeek.Saturday, 13, 30, 0);
+
+  assertDate(local, utc, TimeZone(+10,30), {
+    String: "Sun Aug 01 2010 00:00:00 GMT+1030 (Lord Howe Standard Time)",
+    DateString: "Sun Aug 01 2010",
+    TimeString: "00:00:00 GMT+1030 (Lord Howe Standard Time)",
+    UTCString: "Sat, 31 Jul 2010 13:30:00 GMT",
+    ISOString: "2010-07-31T13:30:00.000Z",
+    LocaleString: "Sun, 08/01/2010, 12:00:00 AM GMT+10:30",
+    LocaleDateString: "Sun, 08/01/2010",
+    LocaleTimeString: "12:00:00 AM GMT+10:30",
+  });
+}
+
+{
+  // +10:30 (daylight savings)
+  let local = new DateTime.Local(2010, Month.January, 3, DayOfWeek.Sunday, 0, 0, 0);
+  let utc = new DateTime.UTC(2010, Month.January, 2, DayOfWeek.Saturday, 13, 0, 0);
+
+  assertDate(local, utc, TimeZone(+11), {
+    String: "Sun Jan 03 2010 00:00:00 GMT+1100 (Lord Howe Daylight Time)",
+    DateString: "Sun Jan 03 2010",
+    TimeString: "00:00:00 GMT+1100 (Lord Howe Daylight Time)",
+    UTCString: "Sat, 02 Jan 2010 13:00:00 GMT",
+    ISOString: "2010-01-02T13:00:00.000Z",
+    LocaleString: "Sun, 01/03/2010, 12:00:00 AM GMT+11",
+    LocaleDateString: "Sun, 01/03/2010",
+    LocaleTimeString: "12:00:00 AM GMT+11",
+  });
+}
+});
+
+
+// File: Date/Europe_Amsterdam.js
+// Europe/Amsterdam as an example for mean time like timezones after LMT (AMT, NST).
+
+inTimeZone("Europe/Amsterdam", () => {
+{
+  let local = new DateTime.Local(1935, Month.January, 1, DayOfWeek.Tuesday, 0, 0, 0);
+  let utc = new DateTime.UTC(1934, Month.December, 31, DayOfWeek.Monday, 23, 40, 28);
+
+  assertDate(local, utc, TimeZone(+0,19,32), {
+    String: "Tue Jan 01 1935 00:00:00 GMT+0019 (Central European Standard Time)",
+    UTCString: "Mon, 31 Dec 1934 23:40:28 GMT",
+  });
+}
+
+{
+  let local = new DateTime.Local(1935, Month.July, 1, DayOfWeek.Monday, 0, 0, 0);
+  let utc = new DateTime.UTC(1935, Month.June, 30, DayOfWeek.Sunday, 22, 40, 28);
+
+  assertDate(local, utc, TimeZone(+1,19,32), {
+    String: "Mon Jul 01 1935 00:00:00 GMT+0119 (Central European Summer Time)",
+    UTCString: "Sun, 30 Jun 1935 22:40:28 GMT",
+  });
+}
+});
+
+
+// File: Date/Europe_London.js
+
+inTimeZone("Europe/London", () => {
+{
+  // +01:00 (standard time)
+  let local = new DateTime.Local(1970, Month.January, 1, DayOfWeek.Thursday, 0, 0, 0);
+  let utc = new DateTime.UTC(1969, Month.December, 31, DayOfWeek.Wednesday, 23, 0, 0);
+
+  assertDate(local, utc, TimeZone(+1), {
+    String: "Thu Jan 01 1970 00:00:00 GMT+0100 (Greenwich Mean Time)",
+    DateString: "Thu Jan 01 1970",
+    TimeString: "00:00:00 GMT+0100 (Greenwich Mean Time)",
+    UTCString: "Wed, 31 Dec 1969 23:00:00 GMT",
+    ISOString: "1969-12-31T23:00:00.000Z",
+    LocaleString: "Thu, 01/01/1970, 12:00:00 AM GMT+1",
+    LocaleDateString: "Thu, 01/01/1970",
+    LocaleTimeString: "12:00:00 AM GMT+1",
+  });
+}
+});
+
+
+// File: Date/Europe_Moscow.js
+
+inTimeZone("Europe/Moscow", () => {
+{
+  let local = new DateTime.Local(1970, Month.January, 1, DayOfWeek.Thursday, 0, 0, 0);
+  let utc = new DateTime.UTC(1969, Month.December, 31, DayOfWeek.Wednesday, 21, 0, 0);
+
+  assertDate(local, utc, TimeZone(+3), {
+    String: "Thu Jan 01 1970 00:00:00 GMT+0300 (Moscow Standard Time)",
+    DateString: "Thu Jan 01 1970",
+    TimeString: "00:00:00 GMT+0300 (Moscow Standard Time)",
+    UTCString: "Wed, 31 Dec 1969 21:00:00 GMT",
+    ISOString: "1969-12-31T21:00:00.000Z",
+    LocaleString: "Thu, 01/01/1970, 12:00:00 AM GMT+3",
+    LocaleDateString: "Thu, 01/01/1970",
+    LocaleTimeString: "12:00:00 AM GMT+3",
+  });
+}
+
+// Russia was in +02:00 starting on 1991-03-31 until 1992-01-19,
+// while still observing DST (transitions 1991-03-31 and 1991-09-29).
+
+{
+  // +03:00 (daylight savings)
+  let local = new DateTime.Local(1990, Month.September, 1, DayOfWeek.Saturday, 0, 0, 0);
+  let utc = new DateTime.UTC(1990, Month.August, 31, DayOfWeek.Friday, 20, 0, 0);
+
+  assertDate(local, utc, TimeZone(+4), {
+    String: "Sat Sep 01 1990 00:00:00 GMT+0400 (Moscow Summer Time)",
+    DateString: "Sat Sep 01 1990",
+    TimeString: "00:00:00 GMT+0400 (Moscow Summer Time)",
+    UTCString: "Fri, 31 Aug 1990 20:00:00 GMT",
+    ISOString: "1990-08-31T20:00:00.000Z",
+    LocaleString: "Sat, 09/01/1990, 12:00:00 AM GMT+4",
+    LocaleDateString: "Sat, 09/01/1990",
+    LocaleTimeString: "12:00:00 AM GMT+4",
+  });
+}
+
+{
+  // +03:00 (standard time)
+  let local = new DateTime.Local(1991, Month.March, 25, DayOfWeek.Monday, 0, 0, 0);
+  let utc = new DateTime.UTC(1991, Month.March, 24, DayOfWeek.Sunday, 21, 0, 0);
+
+  assertDate(local, utc, TimeZone(+3), {
+    String: "Mon Mar 25 1991 00:00:00 GMT+0300 (Moscow Standard Time)",
+    DateString: "Mon Mar 25 1991",
+    TimeString: "00:00:00 GMT+0300 (Moscow Standard Time)",
+    UTCString: "Sun, 24 Mar 1991 21:00:00 GMT",
+    ISOString: "1991-03-24T21:00:00.000Z",
+    LocaleString: "Mon, 03/25/1991, 12:00:00 AM GMT+3",
+    LocaleDateString: "Mon, 03/25/1991",
+    LocaleTimeString: "12:00:00 AM GMT+3",
+  });
+}
+
+{
+  // +02:00 (daylight savings)
+  let local = new DateTime.Local(1991, Month.March, 31, DayOfWeek.Sunday, 12, 0, 0);
+  let utc = new DateTime.UTC(1991, Month.March, 31, DayOfWeek.Sunday, 9, 0, 0);
+
+  assertDate(local, utc, TimeZone(+3), {
+    String: "Sun Mar 31 1991 12:00:00 GMT+0300 (Moscow Summer Time)",
+    DateString: "Sun Mar 31 1991",
+    TimeString: "12:00:00 GMT+0300 (Moscow Summer Time)",
+    UTCString: "Sun, 31 Mar 1991 09:00:00 GMT",
+    ISOString: "1991-03-31T09:00:00.000Z",
+    LocaleString: "Sun, 03/31/1991, 12:00:00 PM GMT+3",
+    LocaleDateString: "Sun, 03/31/1991",
+    LocaleTimeString: "12:00:00 PM GMT+3",
+  });
+}
+
+{
+  // +02:00 (daylight savings)
+  let local = new DateTime.Local(1991, Month.September, 28, DayOfWeek.Saturday, 0, 0, 0);
+  let utc = new DateTime.UTC(1991, Month.September, 27, DayOfWeek.Friday, 21, 0, 0);
+
+  assertDate(local, utc, TimeZone(+3), {
+    String: "Sat Sep 28 1991 00:00:00 GMT+0300 (Moscow Summer Time)",
+    DateString: "Sat Sep 28 1991",
+    TimeString: "00:00:00 GMT+0300 (Moscow Summer Time)",
+    UTCString: "Fri, 27 Sep 1991 21:00:00 GMT",
+    ISOString: "1991-09-27T21:00:00.000Z",
+    LocaleString: "Sat, 09/28/1991, 12:00:00 AM GMT+3",
+    LocaleDateString: "Sat, 09/28/1991",
+    LocaleTimeString: "12:00:00 AM GMT+3",
+  });
+}
+
+{
+  // +02:00 (standard time)
+  let local = new DateTime.Local(1991, Month.September, 30, DayOfWeek.Monday, 0, 0, 0);
+  let utc = new DateTime.UTC(1991, Month.September, 29, DayOfWeek.Sunday, 22, 0, 0);
+
+  assertDate(local, utc, TimeZone(+2), {
+    String: "Mon Sep 30 1991 00:00:00 GMT+0200 (Moscow Standard Time)",
+    DateString: "Mon Sep 30 1991",
+    TimeString: "00:00:00 GMT+0200 (Moscow Standard Time)",
+    UTCString: "Sun, 29 Sep 1991 22:00:00 GMT",
+    ISOString: "1991-09-29T22:00:00.000Z",
+    LocaleString: "Mon, 09/30/1991, 12:00:00 AM GMT+2",
+    LocaleDateString: "Mon, 09/30/1991",
+    LocaleTimeString: "12:00:00 AM GMT+2",
+  });
+}
+
+// Russia stopped observing DST in Oct. 2010 (last transition on 2010-10-31),
+// and changed timezone from +03:00 to +04:00 on 2011-03-27.
+
+{
+  // +03:00 (daylight savings)
+  let local = new DateTime.Local(2010, Month.October, 30, DayOfWeek.Saturday, 0, 0, 0);
+  let utc = new DateTime.UTC(2010, Month.October, 29, DayOfWeek.Friday, 20, 0, 0);
+
+  assertDate(local, utc, TimeZone(+4), {
+    String: "Sat Oct 30 2010 00:00:00 GMT+0400 (Moscow Summer Time)",
+    DateString: "Sat Oct 30 2010",
+    TimeString: "00:00:00 GMT+0400 (Moscow Summer Time)",
+    UTCString: "Fri, 29 Oct 2010 20:00:00 GMT",
+    ISOString: "2010-10-29T20:00:00.000Z",
+    LocaleString: "Sat, 10/30/2010, 12:00:00 AM GMT+4",
+    LocaleDateString: "Sat, 10/30/2010",
+    LocaleTimeString: "12:00:00 AM GMT+4",
+  });
+}
+
+{
+  // +03:00 (standard time)
+  let local = new DateTime.Local(2010, Month.November, 1, DayOfWeek.Monday, 0, 0, 0);
+  let utc = new DateTime.UTC(2010, Month.October, 31, DayOfWeek.Sunday, 21, 0, 0);
+
+  assertDate(local, utc, TimeZone(+3), {
+    String: "Mon Nov 01 2010 00:00:00 GMT+0300 (Moscow Standard Time)",
+    DateString: "Mon Nov 01 2010",
+    TimeString: "00:00:00 GMT+0300 (Moscow Standard Time)",
+    UTCString: "Sun, 31 Oct 2010 21:00:00 GMT",
+    ISOString: "2010-10-31T21:00:00.000Z",
+    LocaleString: "Mon, 11/01/2010, 12:00:00 AM GMT+3",
+    LocaleDateString: "Mon, 11/01/2010",
+    LocaleTimeString: "12:00:00 AM GMT+3",
+  });
+}
+
+{
+  // +04:00 (standard time)
+  let local = new DateTime.Local(2011, Month.October, 30, DayOfWeek.Sunday, 0, 0, 0);
+  let utc = new DateTime.UTC(2011, Month.October, 29, DayOfWeek.Saturday, 20, 0, 0);
+
+  assertDate(local, utc, TimeZone(+4), {
+    String: "Sun Oct 30 2011 00:00:00 GMT+0400 (Moscow Standard Time)",
+    DateString: "Sun Oct 30 2011",
+    TimeString: "00:00:00 GMT+0400 (Moscow Standard Time)",
+    UTCString: "Sat, 29 Oct 2011 20:00:00 GMT",
+    ISOString: "2011-10-29T20:00:00.000Z",
+    LocaleString: "Sun, 10/30/2011, 12:00:00 AM GMT+4",
+    LocaleDateString: "Sun, 10/30/2011",
+    LocaleTimeString: "12:00:00 AM GMT+4",
+  });
+}
+
+{
+  // +04:00 (standard time)
+  let local = new DateTime.Local(2011, Month.November, 1, DayOfWeek.Tuesday, 0, 0, 0);
+  let utc = new DateTime.UTC(2011, Month.October, 31, DayOfWeek.Monday, 20, 0, 0);
+
+  assertDate(local, utc, TimeZone(+4), {
+    String: "Tue Nov 01 2011 00:00:00 GMT+0400 (Moscow Standard Time)",
+    DateString: "Tue Nov 01 2011",
+    TimeString: "00:00:00 GMT+0400 (Moscow Standard Time)",
+    UTCString: "Mon, 31 Oct 2011 20:00:00 GMT",
+    ISOString: "2011-10-31T20:00:00.000Z",
+    LocaleString: "Tue, 11/01/2011, 12:00:00 AM GMT+4",
+    LocaleDateString: "Tue, 11/01/2011",
+    LocaleTimeString: "12:00:00 AM GMT+4",
+  });
+}
+
+// Russia changed timezone from +04:00 to +03:00 on 2014-10-26.
+
+{
+  // +04:00 (standard time)
+  let local = new DateTime.Local(2014, Month.October, 26, DayOfWeek.Sunday, 0, 0, 0);
+  let utc = new DateTime.UTC(2014, Month.October, 25, DayOfWeek.Saturday, 20, 0, 0);
+
+  assertDate(local, utc, TimeZone(+4), {
+    String: "Sun Oct 26 2014 00:00:00 GMT+0400 (Moscow Standard Time)",
+    DateString: "Sun Oct 26 2014",
+    TimeString: "00:00:00 GMT+0400 (Moscow Standard Time)",
+    UTCString: "Sat, 25 Oct 2014 20:00:00 GMT",
+    ISOString: "2014-10-25T20:00:00.000Z",
+    LocaleString: "Sun, 10/26/2014, 12:00:00 AM GMT+4",
+    LocaleDateString: "Sun, 10/26/2014",
+    LocaleTimeString: "12:00:00 AM GMT+4",
+  });
+}
+
+{
+  // +03:00 (standard time)
+  let local = new DateTime.Local(2014, Month.October, 27, DayOfWeek.Monday, 0, 0, 0);
+  let utc = new DateTime.UTC(2014, Month.October, 26, DayOfWeek.Sunday, 21, 0, 0);
+
+  assertDate(local, utc, TimeZone(+3), {
+    String: "Mon Oct 27 2014 00:00:00 GMT+0300 (Moscow Standard Time)",
+    DateString: "Mon Oct 27 2014",
+    TimeString: "00:00:00 GMT+0300 (Moscow Standard Time)",
+    UTCString: "Sun, 26 Oct 2014 21:00:00 GMT",
+    ISOString: "2014-10-26T21:00:00.000Z",
+    LocaleString: "Mon, 10/27/2014, 12:00:00 AM GMT+3",
+    LocaleDateString: "Mon, 10/27/2014",
+    LocaleTimeString: "12:00:00 AM GMT+3",
+  });
+}
+});
+
+
+// File: Date/Pacific_Apia.js
+// Pacific/Apia switched from -11:00 to +13:00 on 2011 Dec 29 24:00.
+
+inTimeZone("Pacific/Apia", () => {
+{
+  // -11:00 (daylight savings)
+  let local = new DateTime.Local(2011, Month.December, 29, DayOfWeek.Thursday, 0, 0, 0);
+  let utc = new DateTime.UTC(2011, Month.December, 29, DayOfWeek.Thursday, 10, 0, 0);
+
+  assertDate(local, utc, TimeZone(-10), {
+    String: "Thu Dec 29 2011 00:00:00 GMT-1000 (Apia Daylight Time)",
+    DateString: "Thu Dec 29 2011",
+    TimeString: "00:00:00 GMT-1000 (Apia Daylight Time)",
+    UTCString: "Thu, 29 Dec 2011 10:00:00 GMT",
+    ISOString: "2011-12-29T10:00:00.000Z",
+    LocaleString: "Thu, 12/29/2011, 12:00:00 AM GMT-10",
+    LocaleDateString: "Thu, 12/29/2011",
+    LocaleTimeString: "12:00:00 AM GMT-10",
+  });
+}
+
+{
+  // +13:00 (daylight savings)
+  let local = new DateTime.Local(2011, Month.December, 31, DayOfWeek.Saturday, 0, 0, 0);
+  let utc = new DateTime.UTC(2011, Month.December, 30, DayOfWeek.Friday, 10, 0, 0);
+
+  assertDate(local, utc, TimeZone(+14), {
+    String: "Sat Dec 31 2011 00:00:00 GMT+1400 (Apia Daylight Time)",
+    DateString: "Sat Dec 31 2011",
+    TimeString: "00:00:00 GMT+1400 (Apia Daylight Time)",
+    UTCString: "Fri, 30 Dec 2011 10:00:00 GMT",
+    ISOString: "2011-12-30T10:00:00.000Z",
+    LocaleString: "Sat, 12/31/2011, 12:00:00 AM GMT+14",
+    LocaleDateString: "Sat, 12/31/2011",
+    LocaleTimeString: "12:00:00 AM GMT+14",
+  });
+}
+
+{
+  // +13:00 (standard time)
+  let local = new DateTime.Local(2012, Month.April, 2, DayOfWeek.Monday, 0, 0, 0);
+  let utc = new DateTime.UTC(2012, Month.April, 1, DayOfWeek.Sunday, 11, 0, 0);
+
+  assertDate(local, utc, TimeZone(+13), {
+    String: "Mon Apr 02 2012 00:00:00 GMT+1300 (Apia Standard Time)",
+    DateString: "Mon Apr 02 2012",
+    TimeString: "00:00:00 GMT+1300 (Apia Standard Time)",
+    UTCString: "Sun, 01 Apr 2012 11:00:00 GMT",
+    ISOString: "2012-04-01T11:00:00.000Z",
+    LocaleString: "Mon, 04/02/2012, 12:00:00 AM GMT+13",
+    LocaleDateString: "Mon, 04/02/2012",
+    LocaleTimeString: "12:00:00 AM GMT+13",
+  });
+}
+});
+
+
+// File: Date/Pacific_Chatham.js
+// Pacific/Chatham time zone offset is 12:45.
+
+inTimeZone("Pacific/Chatham", () => {
+{
+  // +12:45 (standard time)
+  let local = new DateTime.Local(2010, Month.August, 1, DayOfWeek.Sunday, 0, 0, 0);
+  let utc = new DateTime.UTC(2010, Month.July, 31, DayOfWeek.Saturday, 11, 15, 0);
+
+  assertDate(local, utc, TimeZone(+12,45), {
+    String: "Sun Aug 01 2010 00:00:00 GMT+1245 (Chatham Standard Time)",
+    DateString: "Sun Aug 01 2010",
+    TimeString: "00:00:00 GMT+1245 (Chatham Standard Time)",
+    UTCString: "Sat, 31 Jul 2010 11:15:00 GMT",
+    ISOString: "2010-07-31T11:15:00.000Z",
+    LocaleString: "Sun, 08/01/2010, 12:00:00 AM GMT+12:45",
+    LocaleDateString: "Sun, 08/01/2010",
+    LocaleTimeString: "12:00:00 AM GMT+12:45",
+  });
+}
+
+{
+  // +12:45 (daylight savings)
+  let local = new DateTime.Local(2010, Month.January, 3, DayOfWeek.Sunday, 0, 0, 0);
+  let utc = new DateTime.UTC(2010, Month.January, 2, DayOfWeek.Saturday, 10, 15, 0);
+
+  assertDate(local, utc, TimeZone(+13,45), {
+    String: "Sun Jan 03 2010 00:00:00 GMT+1345 (Chatham Daylight Time)",
+    DateString: "Sun Jan 03 2010",
+    TimeString: "00:00:00 GMT+1345 (Chatham Daylight Time)",
+    UTCString: "Sat, 02 Jan 2010 10:15:00 GMT",
+    ISOString: "2010-01-02T10:15:00.000Z",
+    LocaleString: "Sun, 01/03/2010, 12:00:00 AM GMT+13:45",
+    LocaleDateString: "Sun, 01/03/2010",
+    LocaleTimeString: "12:00:00 AM GMT+13:45",
+  });
+}
+});
+
+
+// File: Date/Pacific_Kiritimati.js
+// Pacific/Kiritimati time zone offset is +14:00.
+
+inTimeZone("Pacific/Kiritimati", () => {
+{
+  // +14:00 (standard time)
+  let local = new DateTime.Local(2010, Month.August, 1, DayOfWeek.Sunday, 0, 0, 0);
+  let utc = new DateTime.UTC(2010, Month.July, 31, DayOfWeek.Saturday, 10, 0, 0);
+
+  assertDate(local, utc, TimeZone(+14), {
+    String: "Sun Aug 01 2010 00:00:00 GMT+1400 (Line Islands Time)",
+    DateString: "Sun Aug 01 2010",
+    TimeString: "00:00:00 GMT+1400 (Line Islands Time)",
+    UTCString: "Sat, 31 Jul 2010 10:00:00 GMT",
+    ISOString: "2010-07-31T10:00:00.000Z",
+    LocaleString: "Sun, 08/01/2010, 12:00:00 AM GMT+14",
+    LocaleDateString: "Sun, 08/01/2010",
+    LocaleTimeString: "12:00:00 AM GMT+14",
+  });
+}
+
+// Pacific/Kiritimati time zone offset was -10:40 until Oct. 1979.
+
+{
+  // -10:40 (standard time)
+  let local = new DateTime.Local(1975, Month.January, 1, DayOfWeek.Wednesday, 0, 0, 0);
+  let utc = new DateTime.UTC(1975, Month.January, 1, DayOfWeek.Wednesday, 10, 40, 0);
+
+  assertDate(local, utc, TimeZone(-10,40), {
+    String: "Wed Jan 01 1975 00:00:00 GMT-1040 (Line Islands Time)",
+    DateString: "Wed Jan 01 1975",
+    TimeString: "00:00:00 GMT-1040 (Line Islands Time)",
+    UTCString: "Wed, 01 Jan 1975 10:40:00 GMT",
+    ISOString: "1975-01-01T10:40:00.000Z",
+    LocaleString: "Wed, 01/01/1975, 12:00:00 AM GMT-10:40",
+    LocaleDateString: "Wed, 01/01/1975",
+    LocaleTimeString: "12:00:00 AM GMT-10:40",
+  });
+}
+});
+
+
+// File: Date/Pacifi_Niue.js
+// Pacific/Niue time zone offset was -11:20 until 1951.
+
+inTimeZone("Pacific/Niue", () => {
+{
+  // -11:20 (standard time)
+  let local = new DateTime.Local(1950, Month.January, 1, DayOfWeek.Sunday, 0, 0, 0);
+  let utc = new DateTime.UTC(1950, Month.January, 1, DayOfWeek.Sunday, 11, 20, 0);
+
+  assertDate(local, utc, TimeZone(-11,20), {
+    String: "Sun Jan 01 1950 00:00:00 GMT-1120 (Niue Time)",
+    DateString: "Sun Jan 01 1950",
+    TimeString: "00:00:00 GMT-1120 (Niue Time)",
+    UTCString: "Sun, 01 Jan 1950 11:20:00 GMT",
+    ISOString: "1950-01-01T11:20:00.000Z",
+    LocaleString: "Sun, 01/01/1950, 12:00:00 AM GMT-11:20",
+    LocaleDateString: "Sun, 01/01/1950",
+    LocaleTimeString: "12:00:00 AM GMT-11:20",
+  });
+}
+});
+
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/non262/Date/time-zones-pedantic.js
+++ b/js/src/tests/non262/Date/time-zones-pedantic.js
@@ -1,15 +1,15 @@
 // |reftest| skip-if(xulRuntime.OS=="WINNT"||xulRuntime.OS=="Darwin") -- Skip on OS X in addition to Windows
 
-// Contains the tests from "time-zones.js" which fail on OS X. 
+// Contains the tests from "time-zones.js" which fail on OS X.
 
 // bug 637244
 inTimeZone("Asia/Novosibirsk", () => {
     let dt1 = new Date(1984, Month.April, 1, -1);
-    assertDateTime(dt1, "Sat Mar 31 1984 23:00:00 GMT+0700 (NOVT)", "+07");
+    assertDateTime(dt1, "Sat Mar 31 1984 23:00:00 GMT+0700 (NOVT)", "+07", "Novosibirsk Standard Time");
 
     let dt2 = new Date(1984, Month.April, 1);
-    assertDateTime(dt2, "Sun Apr 01 1984 01:00:00 GMT+0800 (NOVST)", "+08");
+    assertDateTime(dt2, "Sun Apr 01 1984 01:00:00 GMT+0800 (NOVST)", "+08", "Novosibirsk Summer Time");
 });
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
--- a/js/src/tests/non262/Date/time-zones-posix.js
+++ b/js/src/tests/non262/Date/time-zones-posix.js
@@ -40,121 +40,121 @@
 //   America/Denver      -> MST7MDT
 //   America/Los_Angeles -> PST8PDT
 //
 // And remove any tests not matching one of the four time zones from above.
 
 // bug 294908
 inTimeZone("EST5EDT", () => {
     let dt = new Date(2003, Month.April, 6, 2, 30, 00);
-    assertDateTime(dt, "Sun Apr 06 2003 03:30:00 GMT-0400 (EDT)");
+    assertDateTime(dt, "Sun Apr 06 2003 03:30:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 
 // bug 610183
 inTimeZone("PST8PDT", () => {
     let dt = new Date(2014, Month.November, 2, 1, 47, 42);
-    assertDateTime(dt, "Sun Nov 02 2014 01:47:42 GMT-0700 (PDT)");
+    assertDateTime(dt, "Sun Nov 02 2014 01:47:42 GMT-0700 (PDT)", "Pacific Daylight Time");
 });
 
 // bug 629465
 inTimeZone("MST7MDT", () => {
     let dt1 = new Date(Date.UTC(2015, Month.November, 1, 0, 0, 0) + 6 * msPerHour);
-    assertDateTime(dt1, "Sun Nov 01 2015 00:00:00 GMT-0600 (MDT)");
+    assertDateTime(dt1, "Sun Nov 01 2015 00:00:00 GMT-0600 (MDT)", "Mountain Daylight Time");
 
     let dt2 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 6 * msPerHour);
-    assertDateTime(dt2, "Sun Nov 01 2015 01:00:00 GMT-0600 (MDT)");
+    assertDateTime(dt2, "Sun Nov 01 2015 01:00:00 GMT-0600 (MDT)", "Mountain Daylight Time");
 
     let dt3 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 7 * msPerHour);
-    assertDateTime(dt3, "Sun Nov 01 2015 01:00:00 GMT-0700 (MST)");
+    assertDateTime(dt3, "Sun Nov 01 2015 01:00:00 GMT-0700 (MST)", "Mountain Standard Time");
 });
 
 // bug 742427
 inTimeZone("EST5EDT", () => {
     let dt = new Date(2009, Month.March, 8, 1, 0, 0);
-    assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0500 (EST)");
+    assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0500 (EST)", "Eastern Standard Time");
     dt.setHours(dt.getHours() + 1);
-    assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0400 (EDT)");
+    assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 inTimeZone("MST7MDT", () => {
     let dt = new Date(2009, Month.March, 8, 1, 0, 0);
-    assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0700 (MST)");
+    assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0700 (MST)", "Mountain Standard Time");
     dt.setHours(dt.getHours() + 1);
-    assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0600 (MDT)");
+    assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0600 (MDT)", "Mountain Daylight Time");
 });
 inTimeZone("EST5EDT", () => {
     let dt1 = new Date(Date.UTC(2008, Month.March, 9, 0, 0, 0) + 5 * msPerHour);
-    assertDateTime(dt1, "Sun Mar 09 2008 00:00:00 GMT-0500 (EST)");
+    assertDateTime(dt1, "Sun Mar 09 2008 00:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 
     let dt2 = new Date(Date.UTC(2008, Month.March, 9, 1, 0, 0) + 5 * msPerHour);
-    assertDateTime(dt2, "Sun Mar 09 2008 01:00:00 GMT-0500 (EST)");
+    assertDateTime(dt2, "Sun Mar 09 2008 01:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 
     let dt3 = new Date(Date.UTC(2008, Month.March, 9, 4, 0, 0) + 4 * msPerHour);
-    assertDateTime(dt3, "Sun Mar 09 2008 04:00:00 GMT-0400 (EDT)");
+    assertDateTime(dt3, "Sun Mar 09 2008 04:00:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 
 // bug 802627
 inTimeZone("EST5EDT", () => {
     let dt = new Date(0);
-    assertDateTime(dt, "Wed Dec 31 1969 19:00:00 GMT-0500 (EST)");
+    assertDateTime(dt, "Wed Dec 31 1969 19:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 });
 
 // bug 879261
 inTimeZone("EST5EDT", () => {
     let dt1 = new Date(1362891600000);
-    assertDateTime(dt1, "Sun Mar 10 2013 00:00:00 GMT-0500 (EST)");
+    assertDateTime(dt1, "Sun Mar 10 2013 00:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 
     let dt2 = new Date(dt1.setHours(dt1.getHours() + 24));
-    assertDateTime(dt2, "Mon Mar 11 2013 00:00:00 GMT-0400 (EDT)");
+    assertDateTime(dt2, "Mon Mar 11 2013 00:00:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 inTimeZone("PST8PDT", () => {
     let dt1 = new Date(2014, Month.January, 1);
-    assertDateTime(dt1, "Wed Jan 01 2014 00:00:00 GMT-0800 (PST)");
+    assertDateTime(dt1, "Wed Jan 01 2014 00:00:00 GMT-0800 (PST)", "Pacific Standard Time");
 
     let dt2 = new Date(2014, Month.August, 1);
-    assertDateTime(dt2, "Fri Aug 01 2014 00:00:00 GMT-0700 (PDT)");
+    assertDateTime(dt2, "Fri Aug 01 2014 00:00:00 GMT-0700 (PDT)", "Pacific Daylight Time");
 });
 inTimeZone("EST5EDT", () => {
     let dt1 = new Date(2016, Month.October, 14, 3, 5, 9);
-    assertDateTime(dt1, "Fri Oct 14 2016 03:05:09 GMT-0400 (EDT)");
+    assertDateTime(dt1, "Fri Oct 14 2016 03:05:09 GMT-0400 (EDT)", "Eastern Daylight Time");
 
     let dt2 = new Date(2016, Month.January, 9, 23, 26, 40);
-    assertDateTime(dt2, "Sat Jan 09 2016 23:26:40 GMT-0500 (EST)");
+    assertDateTime(dt2, "Sat Jan 09 2016 23:26:40 GMT-0500 (EST)", "Eastern Standard Time");
 });
 
 // bug 1084547
 inTimeZone("EST5EDT", () => {
     let dt = new Date(Date.parse("2014-11-02T02:00:00-04:00"));
-    assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0500 (EST)");
+    assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 
     dt.setMilliseconds(0);
-    assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0400 (EDT)");
+    assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 
 // bug 1303306
 inTimeZone("EST5EDT", () => {
     let dt = new Date(2016, Month.September, 15, 16, 14, 48);
-    assertDateTime(dt, "Thu Sep 15 2016 16:14:48 GMT-0400 (EDT)");
+    assertDateTime(dt, "Thu Sep 15 2016 16:14:48 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 
 // bug 1317364
 inTimeZone("PST8PDT", () => {
     let dt = new Date(2016, Month.March, 13, 2, 30, 0, 0);
-    assertDateTime(dt, "Sun Mar 13 2016 03:30:00 GMT-0700 (PDT)");
+    assertDateTime(dt, "Sun Mar 13 2016 03:30:00 GMT-0700 (PDT)", "Pacific Daylight Time");
 
     let dt2 = new Date(2016, Month.January, 5, 0, 30, 30, 500);
-    assertDateTime(dt2, "Tue Jan 05 2016 00:30:30 GMT-0800 (PST)");
+    assertDateTime(dt2, "Tue Jan 05 2016 00:30:30 GMT-0800 (PST)", "Pacific Standard Time");
 
     let dt3 = new Date(dt2.getTime());
     dt3.setMonth(dt2.getMonth() + 2);
     dt3.setDate(dt2.getDate() + 7 + 1);
     dt3.setHours(dt2.getHours() + 2);
 
     assertEq(dt3.getHours(), 3);
 });
 
 // bug 1355272
 inTimeZone("PST8PDT", () => {
     let dt = new Date(2017, Month.April, 10, 17, 25, 07);
-    assertDateTime(dt, "Mon Apr 10 2017 17:25:07 GMT-0700 (PDT)");
+    assertDateTime(dt, "Mon Apr 10 2017 17:25:07 GMT-0700 (PDT)", "Pacific Daylight Time");
 });
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
--- a/js/src/tests/non262/Date/time-zones.js
+++ b/js/src/tests/non262/Date/time-zones.js
@@ -1,183 +1,183 @@
 // |reftest| skip-if(xulRuntime.OS=="WINNT") -- Windows doesn't accept IANA names for the TZ env variable
 
 // bug 158328
 inTimeZone("Europe/London", () => {
     let dt1 = new Date(2002, Month.July, 19, 16, 10, 55);
-    assertDateTime(dt1, "Fri Jul 19 2002 16:10:55 GMT+0100 (BST)");
+    assertDateTime(dt1, "Fri Jul 19 2002 16:10:55 GMT+0100 (BST)", "British Summer Time");
 
     let dt2 = new Date(2009, Month.December, 24, 13, 44, 52);
-    assertDateTime(dt2, "Thu Dec 24 2009 13:44:52 GMT+0000 (GMT)");
+    assertDateTime(dt2, "Thu Dec 24 2009 13:44:52 GMT+0000 (GMT)", "Greenwich Mean Time");
 });
 
 // bug 294908
 inTimeZone("America/New_York", () => {
     let dt = new Date(2003, Month.April, 6, 2, 30, 00);
-    assertDateTime(dt, "Sun Apr 06 2003 03:30:00 GMT-0400 (EDT)");
+    assertDateTime(dt, "Sun Apr 06 2003 03:30:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 
 // bug 610183
 inTimeZone("America/Los_Angeles", () => {
     let dt = new Date(2014, Month.November, 2, 1, 47, 42);
-    assertDateTime(dt, "Sun Nov 02 2014 01:47:42 GMT-0700 (PDT)");
+    assertDateTime(dt, "Sun Nov 02 2014 01:47:42 GMT-0700 (PDT)", "Pacific Daylight Time");
 });
 
 // bug 629465
 inTimeZone("America/Denver", () => {
     let dt1 = new Date(Date.UTC(2015, Month.November, 1, 0, 0, 0) + 6 * msPerHour);
-    assertDateTime(dt1, "Sun Nov 01 2015 00:00:00 GMT-0600 (MDT)");
+    assertDateTime(dt1, "Sun Nov 01 2015 00:00:00 GMT-0600 (MDT)", "Mountain Daylight Time");
 
     let dt2 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 6 * msPerHour);
-    assertDateTime(dt2, "Sun Nov 01 2015 01:00:00 GMT-0600 (MDT)");
+    assertDateTime(dt2, "Sun Nov 01 2015 01:00:00 GMT-0600 (MDT)", "Mountain Daylight Time");
 
     let dt3 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 7 * msPerHour);
-    assertDateTime(dt3, "Sun Nov 01 2015 01:00:00 GMT-0700 (MST)");
+    assertDateTime(dt3, "Sun Nov 01 2015 01:00:00 GMT-0700 (MST)", "Mountain Standard Time");
 });
 
 // bug 637244
 inTimeZone("Europe/Helsinki", () => {
     let dt1 = new Date(2016, Month.March, 27, 2, 59);
-    assertDateTime(dt1, "Sun Mar 27 2016 02:59:00 GMT+0200 (EET)");
+    assertDateTime(dt1, "Sun Mar 27 2016 02:59:00 GMT+0200 (EET)", "Eastern European Standard Time");
 
     let dt2 = new Date(2016, Month.March, 27, 3, 0);
-    assertDateTime(dt2, "Sun Mar 27 2016 04:00:00 GMT+0300 (EEST)");
+    assertDateTime(dt2, "Sun Mar 27 2016 04:00:00 GMT+0300 (EEST)", "Eastern European Summer Time");
 });
 
 // bug 718175
 inTimeZone("Europe/London", () => {
     let dt = new Date(0);
     assertEq(dt.getHours(), 1);
 });
 
 // bug 719274
 inTimeZone("Pacific/Auckland", () => {
     let dt = new Date(2012, Month.January, 19, 12, 54, 27);
-    assertDateTime(dt, "Thu Jan 19 2012 12:54:27 GMT+1300 (NZDT)");
+    assertDateTime(dt, "Thu Jan 19 2012 12:54:27 GMT+1300 (NZDT)", "New Zealand Daylight Time");
 });
 
 // bug 742427
 inTimeZone("Europe/Paris", () => {
     let dt1 = new Date(2009, Month.March, 29, 1, 0, 0);
-    assertDateTime(dt1, "Sun Mar 29 2009 01:00:00 GMT+0100 (CET)");
+    assertDateTime(dt1, "Sun Mar 29 2009 01:00:00 GMT+0100 (CET)", "Central European Standard Time");
     dt1.setHours(dt1.getHours() + 1);
-    assertDateTime(dt1, "Sun Mar 29 2009 03:00:00 GMT+0200 (CEST)");
+    assertDateTime(dt1, "Sun Mar 29 2009 03:00:00 GMT+0200 (CEST)", "Central European Summer Time");
 
     let dt2 = new Date(2010, Month.March, 29, 1, 0, 0);
-    assertDateTime(dt2, "Mon Mar 29 2010 01:00:00 GMT+0200 (CEST)");
+    assertDateTime(dt2, "Mon Mar 29 2010 01:00:00 GMT+0200 (CEST)", "Central European Summer Time");
     dt2.setHours(dt2.getHours() + 1);
-    assertDateTime(dt2, "Mon Mar 29 2010 02:00:00 GMT+0200 (CEST)");
+    assertDateTime(dt2, "Mon Mar 29 2010 02:00:00 GMT+0200 (CEST)", "Central European Summer Time");
 });
 inTimeZone("America/New_York", () => {
     let dt = new Date(2009, Month.March, 8, 1, 0, 0);
-    assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0500 (EST)");
+    assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0500 (EST)", "Eastern Standard Time");
     dt.setHours(dt.getHours() + 1);
-    assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0400 (EDT)");
+    assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 inTimeZone("America/Denver", () => {
     let dt = new Date(2009, Month.March, 8, 1, 0, 0);
-    assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0700 (MST)");
+    assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0700 (MST)", "Mountain Standard Time");
     dt.setHours(dt.getHours() + 1);
-    assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0600 (MDT)");
+    assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0600 (MDT)", "Mountain Daylight Time");
 });
 inTimeZone("America/New_York", () => {
     let dt1 = new Date(Date.UTC(2008, Month.March, 9, 0, 0, 0) + 5 * msPerHour);
-    assertDateTime(dt1, "Sun Mar 09 2008 00:00:00 GMT-0500 (EST)");
+    assertDateTime(dt1, "Sun Mar 09 2008 00:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 
     let dt2 = new Date(Date.UTC(2008, Month.March, 9, 1, 0, 0) + 5 * msPerHour);
-    assertDateTime(dt2, "Sun Mar 09 2008 01:00:00 GMT-0500 (EST)");
+    assertDateTime(dt2, "Sun Mar 09 2008 01:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 
     let dt3 = new Date(Date.UTC(2008, Month.March, 9, 4, 0, 0) + 4 * msPerHour);
-    assertDateTime(dt3, "Sun Mar 09 2008 04:00:00 GMT-0400 (EDT)");
+    assertDateTime(dt3, "Sun Mar 09 2008 04:00:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 inTimeZone("Europe/Paris", () => {
     let dt1 = new Date(Date.UTC(2008, Month.March, 30, 0, 0, 0) - 1 * msPerHour);
-    assertDateTime(dt1, "Sun Mar 30 2008 00:00:00 GMT+0100 (CET)");
+    assertDateTime(dt1, "Sun Mar 30 2008 00:00:00 GMT+0100 (CET)", "Central European Standard Time");
 
     let dt2 = new Date(Date.UTC(2008, Month.March, 30, 1, 0, 0) - 1 * msPerHour);
-    assertDateTime(dt2, "Sun Mar 30 2008 01:00:00 GMT+0100 (CET)");
+    assertDateTime(dt2, "Sun Mar 30 2008 01:00:00 GMT+0100 (CET)", "Central European Standard Time");
 
     let dt3 = new Date(Date.UTC(2008, Month.March, 30, 3, 0, 0) - 2 * msPerHour);
-    assertDateTime(dt3, "Sun Mar 30 2008 03:00:00 GMT+0200 (CEST)");
+    assertDateTime(dt3, "Sun Mar 30 2008 03:00:00 GMT+0200 (CEST)", "Central European Summer Time");
 
     let dt4 = new Date(Date.UTC(2008, Month.March, 30, 4, 0, 0) - 2 * msPerHour);
-    assertDateTime(dt4, "Sun Mar 30 2008 04:00:00 GMT+0200 (CEST)");
+    assertDateTime(dt4, "Sun Mar 30 2008 04:00:00 GMT+0200 (CEST)", "Central European Summer Time");
 });
 
 // bug 802627
 inTimeZone("America/New_York", () => {
     let dt = new Date(0);
-    assertDateTime(dt, "Wed Dec 31 1969 19:00:00 GMT-0500 (EST)");
+    assertDateTime(dt, "Wed Dec 31 1969 19:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 });
 
 // bug 819820
 inTimeZone("Europe/London", () => {
     let dt1 = new Date(Date.UTC(2012, Month.October, 28, 0, 59, 59));
-    assertDateTime(dt1, "Sun Oct 28 2012 01:59:59 GMT+0100 (BST)");
+    assertDateTime(dt1, "Sun Oct 28 2012 01:59:59 GMT+0100 (BST)", "British Summer Time");
 
     let dt2 = new Date(Date.UTC(2012, Month.October, 28, 1, 0, 0));
-    assertDateTime(dt2, "Sun Oct 28 2012 01:00:00 GMT+0000 (GMT)");
+    assertDateTime(dt2, "Sun Oct 28 2012 01:00:00 GMT+0000 (GMT)", "Greenwich Mean Time");
 
     let dt3 = new Date(Date.UTC(2012, Month.October, 28, 1, 59, 59));
-    assertDateTime(dt3, "Sun Oct 28 2012 01:59:59 GMT+0000 (GMT)");
+    assertDateTime(dt3, "Sun Oct 28 2012 01:59:59 GMT+0000 (GMT)", "Greenwich Mean Time");
 
     let dt4 = new Date(Date.UTC(2012, Month.October, 28, 2, 0, 0));
-    assertDateTime(dt4, "Sun Oct 28 2012 02:00:00 GMT+0000 (GMT)");
+    assertDateTime(dt4, "Sun Oct 28 2012 02:00:00 GMT+0000 (GMT)", "Greenwich Mean Time");
 });
 
 // bug 879261
 inTimeZone("America/New_York", () => {
     let dt1 = new Date(1362891600000);
-    assertDateTime(dt1, "Sun Mar 10 2013 00:00:00 GMT-0500 (EST)");
+    assertDateTime(dt1, "Sun Mar 10 2013 00:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 
     let dt2 = new Date(dt1.setHours(dt1.getHours() + 24));
-    assertDateTime(dt2, "Mon Mar 11 2013 00:00:00 GMT-0400 (EDT)");
+    assertDateTime(dt2, "Mon Mar 11 2013 00:00:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 inTimeZone("America/Los_Angeles", () => {
     let dt1 = new Date(2014, Month.January, 1);
-    assertDateTime(dt1, "Wed Jan 01 2014 00:00:00 GMT-0800 (PST)");
+    assertDateTime(dt1, "Wed Jan 01 2014 00:00:00 GMT-0800 (PST)", "Pacific Standard Time");
 
     let dt2 = new Date(2014, Month.August, 1);
-    assertDateTime(dt2, "Fri Aug 01 2014 00:00:00 GMT-0700 (PDT)");
+    assertDateTime(dt2, "Fri Aug 01 2014 00:00:00 GMT-0700 (PDT)", "Pacific Daylight Time");
 });
 inTimeZone("America/New_York", () => {
     let dt1 = new Date(2016, Month.October, 14, 3, 5, 9);
-    assertDateTime(dt1, "Fri Oct 14 2016 03:05:09 GMT-0400 (EDT)");
+    assertDateTime(dt1, "Fri Oct 14 2016 03:05:09 GMT-0400 (EDT)", "Eastern Daylight Time");
 
     let dt2 = new Date(2016, Month.January, 9, 23, 26, 40);
-    assertDateTime(dt2, "Sat Jan 09 2016 23:26:40 GMT-0500 (EST)");
+    assertDateTime(dt2, "Sat Jan 09 2016 23:26:40 GMT-0500 (EST)", "Eastern Standard Time");
 });
 
 // bug 994086
 inTimeZone("Europe/Vienna", () => {
     let dt1 = new Date(2014, Month.March, 30, 2, 0);
-    assertDateTime(dt1, "Sun Mar 30 2014 03:00:00 GMT+0200 (CEST)");
+    assertDateTime(dt1, "Sun Mar 30 2014 03:00:00 GMT+0200 (CEST)", "Central European Summer Time");
 
     let dt2 = new Date(2014, Month.March, 30, 3, 0);
-    assertDateTime(dt2, "Sun Mar 30 2014 03:00:00 GMT+0200 (CEST)");
+    assertDateTime(dt2, "Sun Mar 30 2014 03:00:00 GMT+0200 (CEST)", "Central European Summer Time");
 
     let dt3 = new Date(2014, Month.March, 30, 4, 0);
-    assertDateTime(dt3, "Sun Mar 30 2014 04:00:00 GMT+0200 (CEST)");
+    assertDateTime(dt3, "Sun Mar 30 2014 04:00:00 GMT+0200 (CEST)", "Central European Summer Time");
 });
 
 // bug 1084434
 inTimeZone("America/Sao_Paulo", () => {
     let dt = new Date(2014, Month.October, 19);
     assertEq(dt.getDate(), 19);
     assertEq(dt.getHours(), 1);
-    assertDateTime(dt, "Sun Oct 19 2014 01:00:00 GMT-0200 (BRST)", "-02");
+    assertDateTime(dt, "Sun Oct 19 2014 01:00:00 GMT-0200 (BRST)", "-02", "Brasilia Summer Time");
 });
 
 // bug 1084547
 inTimeZone("America/New_York", () => {
     let dt = new Date(Date.parse("2014-11-02T02:00:00-04:00"));
-    assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0500 (EST)");
+    assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0500 (EST)", "Eastern Standard Time");
 
     dt.setMilliseconds(0);
-    assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0400 (EDT)");
+    assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 
 // bug 1118690
 inTimeZone("Europe/London", () => {
     let dt = new Date(1965, Month.January, 1);
     assertEq(dt.getFullYear(), 1965);
 });
 
@@ -194,68 +194,68 @@ inTimeZone("Europe/Moscow", () => {
 
     let dt4 = new Date(1984, Month.March, 32);
     assertEq(dt4.getDate(), 1);
 });
 
 // bug 1284507
 inTimeZone("Atlantic/Azores", () => {
     let dt1 = new Date(2017, Month.March, 25, 0, 0, 0);
-    assertDateTime(dt1, "Sat Mar 25 2017 00:00:00 GMT-0100 (AZOT)", "-01");
+    assertDateTime(dt1, "Sat Mar 25 2017 00:00:00 GMT-0100 (AZOT)", "-01", "Azores Standard Time");
 
     let dt2 = new Date(2016, Month.October, 30, 0, 0, 0);
-    assertDateTime(dt2, "Sun Oct 30 2016 00:00:00 GMT+0000 (AZOST)", "+00");
+    assertDateTime(dt2, "Sun Oct 30 2016 00:00:00 GMT+0000 (AZOST)", "+00", "Azores Summer Time");
 
     let dt3 = new Date(2016, Month.October, 30, 23, 0, 0);
-    assertDateTime(dt3, "Sun Oct 30 2016 23:00:00 GMT-0100 (AZOT)", "-01");
+    assertDateTime(dt3, "Sun Oct 30 2016 23:00:00 GMT-0100 (AZOT)", "-01", "Azores Standard Time");
 });
 
 // bug 1303306
 inTimeZone("America/New_York", () => {
     let dt = new Date(2016, Month.September, 15, 16, 14, 48);
-    assertDateTime(dt, "Thu Sep 15 2016 16:14:48 GMT-0400 (EDT)");
+    assertDateTime(dt, "Thu Sep 15 2016 16:14:48 GMT-0400 (EDT)", "Eastern Daylight Time");
 });
 
 // bug 1317364
 inTimeZone("America/Los_Angeles", () => {
     let dt = new Date(2016, Month.March, 13, 2, 30, 0, 0);
-    assertDateTime(dt, "Sun Mar 13 2016 03:30:00 GMT-0700 (PDT)");
+    assertDateTime(dt, "Sun Mar 13 2016 03:30:00 GMT-0700 (PDT)", "Pacific Daylight Time");
 
     let dt2 = new Date(2016, Month.January, 5, 0, 30, 30, 500);
-    assertDateTime(dt2, "Tue Jan 05 2016 00:30:30 GMT-0800 (PST)");
+    assertDateTime(dt2, "Tue Jan 05 2016 00:30:30 GMT-0800 (PST)", "Pacific Standard Time");
 
     let dt3 = new Date(dt2.getTime());
     dt3.setMonth(dt2.getMonth() + 2);
     dt3.setDate(dt2.getDate() + 7 + 1);
     dt3.setHours(dt2.getHours() + 2);
 
     assertEq(dt3.getHours(), 3);
 });
 
 // bug 1335818
 inTimeZone("Asia/Jerusalem", () => {
     let dt1 = new Date(2013, Month.March, 22, 1, 0, 0, 0);
-    assertDateTime(dt1, "Fri Mar 22 2013 01:00:00 GMT+0200 (IST)");
+    assertDateTime(dt1, "Fri Mar 22 2013 01:00:00 GMT+0200 (IST)", "Israel Standard Time");
 
     let dt2 = new Date(2013, Month.March, 22, 2, 0, 0, 0);
-    assertDateTime(dt2, "Fri Mar 22 2013 02:00:00 GMT+0200 (IST)");
+    assertDateTime(dt2, "Fri Mar 22 2013 02:00:00 GMT+0200 (IST)", "Israel Standard Time");
 
     let dt3 = new Date(2013, Month.March, 22, 3, 0, 0, 0);
-    assertDateTime(dt3, "Fri Mar 22 2013 03:00:00 GMT+0200 (IST)");
+    assertDateTime(dt3, "Fri Mar 22 2013 03:00:00 GMT+0200 (IST)", "Israel Standard Time");
 
     let dt4 = new Date(2013, Month.March, 29, 1, 0, 0, 0);
-    assertDateTime(dt4, "Fri Mar 29 2013 01:00:00 GMT+0200 (IST)");
+    assertDateTime(dt4, "Fri Mar 29 2013 01:00:00 GMT+0200 (IST)", "Israel Standard Time");
 
     let dt5 = new Date(2013, Month.March, 29, 2, 0, 0, 0);
-    assertDateTime(dt5, "Fri Mar 29 2013 03:00:00 GMT+0300 (IDT)");
+    assertDateTime(dt5, "Fri Mar 29 2013 03:00:00 GMT+0300 (IDT)", "Israel Daylight Time");
 
     let dt6 = new Date(2013, Month.March, 29, 3, 0, 0, 0);
-    assertDateTime(dt6, "Fri Mar 29 2013 03:00:00 GMT+0300 (IDT)");
+    assertDateTime(dt6, "Fri Mar 29 2013 03:00:00 GMT+0300 (IDT)", "Israel Daylight Time");
 });
 
 // bug 1355272
 inTimeZone("America/Los_Angeles", () => {
     let dt = new Date(2017, Month.April, 10, 17, 25, 07);
-    assertDateTime(dt, "Mon Apr 10 2017 17:25:07 GMT-0700 (PDT)");
+    assertDateTime(dt, "Mon Apr 10 2017 17:25:07 GMT-0700 (PDT)", "Pacific Daylight Time");
 });
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/Date/toString-localized-posix.js
@@ -0,0 +1,71 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl")) -- Requires ICU time zone support
+
+// Date.prototype.toString includes a localized time zone name comment.
+
+// Repeats the test from "toString-localized.js", but uses POSIX instead of IANA
+// names for the time zones. This allows to run these tests on Windows, too.
+
+inTimeZone("PST8PDT", () => {
+    let dt = new Date(2018, Month.July, 14);
+
+    withLocale("en", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (Pacific Daylight Time)");
+    });
+    withLocale("fr", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (heure d’été du Pacifique)");
+    });
+    withLocale("de", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (Nordamerikanische Westküsten-Sommerzeit)");
+    });
+    withLocale("ar", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (توقيت المحيط الهادي الصيفي)");
+    });
+    withLocale("zh", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (北美太平洋夏令时间)");
+    });
+});
+
+for (let tz of ["UTC", "UCT"]) {
+    inTimeZone(tz, () => {
+        let dt = new Date(2018, Month.July, 14);
+
+        withLocale("en", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Coordinated Universal Time)");
+        });
+        withLocale("fr", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Temps universel coordonné)");
+        });
+        withLocale("de", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Koordinierte Weltzeit)");
+        });
+        withLocale("ar", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (التوقيت العالمي المنسق)");
+        });
+        withLocale("zh", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (协调世界时)");
+        });
+    });
+}
+
+inTimeZone("GMT", () => {
+    let dt = new Date(2018, Month.July, 14);
+
+    withLocale("en", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Greenwich Mean Time)");
+    });
+    withLocale("fr", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (heure moyenne de Greenwich)");
+    });
+    withLocale("de", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Mittlere Greenwich-Zeit)");
+    });
+    withLocale("ar", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (توقيت غرينتش)");
+    });
+    withLocale("zh", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (格林尼治标准时间)");
+    });
+});
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/Date/toString-localized.js
@@ -0,0 +1,90 @@
+// |reftest| skip-if(xulRuntime.OS=="WINNT"||!this.hasOwnProperty("Intl")) -- Windows doesn't accept IANA names for the TZ env variable; Requires ICU time zone support
+
+// Date.prototype.toString includes a localized time zone name comment.
+
+inTimeZone("Europe/Paris", () => {
+    let dt = new Date(2018, Month.July, 14);
+
+    withLocale("en", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0200 (Central European Summer Time)");
+    });
+    withLocale("fr", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0200 (heure d’été d’Europe centrale)");
+    });
+    withLocale("de", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0200 (Mitteleuropäische Sommerzeit)");
+    });
+    withLocale("ar", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0200 (توقيت وسط أوروبا الصيفي)");
+    });
+    withLocale("zh", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0200 (中欧夏令时间)");
+    });
+});
+
+inTimeZone("America/Los_Angeles", () => {
+    let dt = new Date(2018, Month.July, 14);
+
+    withLocale("en", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (Pacific Daylight Time)");
+    });
+    withLocale("fr", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (heure d’été du Pacifique)");
+    });
+    withLocale("de", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (Nordamerikanische Westküsten-Sommerzeit)");
+    });
+    withLocale("ar", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (توقيت المحيط الهادي الصيفي)");
+    });
+    withLocale("zh", () => {
+        assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT-0700 (北美太平洋夏令时间)");
+    });
+});
+
+for (let tz of ["UTC", "UCT", "Zulu", "Universal", "Etc/UTC", "Etc/UCT", "Etc/Zulu", "Etc/Universal"]) {
+    inTimeZone(tz, () => {
+        let dt = new Date(2018, Month.July, 14);
+
+        withLocale("en", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Coordinated Universal Time)");
+        });
+        withLocale("fr", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Temps universel coordonné)");
+        });
+        withLocale("de", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Koordinierte Weltzeit)");
+        });
+        withLocale("ar", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (التوقيت العالمي المنسق)");
+        });
+        withLocale("zh", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (协调世界时)");
+        });
+    });
+}
+
+for (let tz of ["GMT", "Etc/GMT"]) {
+    inTimeZone(tz, () => {
+        let dt = new Date(2018, Month.July, 14);
+
+        withLocale("en", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Greenwich Mean Time)");
+        });
+        withLocale("fr", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (heure moyenne de Greenwich)");
+        });
+        withLocale("de", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (Mittlere Greenwich-Zeit)");
+        });
+        withLocale("ar", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (توقيت غرينتش)");
+        });
+        withLocale("zh", () => {
+            assertDateTime(dt, "Sat Jul 14 2018 00:00:00 GMT+0000 (格林尼治标准时间)");
+        });
+    });
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/non262/regress/regress-58116.js
+++ b/js/src/tests/non262/regress/regress-58116.js
@@ -1,8 +1,9 @@
+// |reftest| skip-if(this.hasOwnProperty("Intl")) -- Requires inaccurate historic time zone data.
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  * Contributor: Bob Clary
  */
 
 //-----------------------------------------------------------------------------
--- a/js/src/vm/DateObject.h
+++ b/js/src/vm/DateObject.h
@@ -79,16 +79,17 @@ class DateObject : public NativeObject
     static MOZ_ALWAYS_INLINE bool getDate_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getUTCDate_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getDay_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getUTCDay_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getHours_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getUTCHours_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getMinutes_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getUTCMinutes_impl(JSContext* cx, const CallArgs& args);
+    static MOZ_ALWAYS_INLINE bool getSeconds_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getUTCSeconds_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getUTCMilliseconds_impl(JSContext* cx, const CallArgs& args);
     static MOZ_ALWAYS_INLINE bool getTimezoneOffset_impl(JSContext* cx, const CallArgs& args);
 };
 
 } // namespace js
 
 #endif // vm_DateObject_h_
--- a/js/src/vm/DateTime.cpp
+++ b/js/src/vm/DateTime.cpp
@@ -4,36 +4,39 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/DateTime.h"
 
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Unused.h"
 
+#include <algorithm>
 #if defined(XP_WIN)
-#include "mozilla/UniquePtr.h"
-
 #include <cstdlib>
+#endif /* defined(XP_WIN) */
 #include <cstring>
-#endif /* defined(XP_WIN) */
 #include <time.h>
 
 #include "jsutil.h"
 
 #include "js/Date.h"
 #include "threading/ExclusiveData.h"
 
-#if ENABLE_INTL_API
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+#include "unicode/basictz.h"
+#include "unicode/locid.h"
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
+
+#if ENABLE_INTL_API && (!MOZ_SYSTEM_ICU || defined(ICU_TZ_HAS_RECREATE_DEFAULT))
 #include "unicode/timezone.h"
-#if defined(XP_WIN)
 #include "unicode/unistr.h"
-#endif
-#endif /* ENABLE_INTL_API */
+#endif /* ENABLE_INTL_API && (!MOZ_SYSTEM_ICU || defined(ICU_TZ_HAS_RECREATE_DEFAULT)) */
 
+#include "util/Text.h"
 #include "vm/MutexIDs.h"
 
 static bool
 ComputeLocalTime(time_t local, struct tm* ptm)
 {
 #if defined(_WIN32)
     return localtime_s(ptm, &local) == 0;
 #elif defined(HAVE_LOCALTIME_R)
@@ -157,24 +160,42 @@ js::DateTimeInfo::internalUpdateTimeZone
     int32_t newTZA = utcToLocalStandardOffsetSeconds_ * msPerSecond;
     if (mode == ResetTimeZoneMode::DontResetIfOffsetUnchanged && newTZA == localTZA_)
         return false;
 
     localTZA_ = newTZA;
 
     dstRange_.reset();
 
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+    utcRange_.reset();
+    localRange_.reset();
+
+    {
+        // Tell the analysis the |pFree| function pointer called by uprv_free
+        // cannot GC.
+        JS::AutoSuppressGCAnalysis nogc;
+
+        timeZone_ = nullptr;
+    }
+
+    standardName_ = nullptr;
+    daylightSavingsName_ = nullptr;
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
+
     return true;
 }
 
 js::DateTimeInfo::DateTimeInfo()
 {
     internalUpdateTimeZoneAdjustment(ResetTimeZoneMode::ResetEvenIfOffsetUnchaged);
 }
 
+js::DateTimeInfo::~DateTimeInfo() = default;
+
 int64_t
 js::DateTimeInfo::toClampedSeconds(int64_t milliseconds)
 {
     int64_t seconds = milliseconds / msPerSecond;
     if (seconds > MaxTimeT) {
         seconds = MaxTimeT;
     } else if (seconds < MinTimeT) {
         /* Go ahead a day to make localtime work (does not work with 0). */
@@ -184,16 +205,28 @@ js::DateTimeInfo::toClampedSeconds(int64
 }
 
 int32_t
 js::DateTimeInfo::computeDSTOffsetMilliseconds(int64_t utcSeconds)
 {
     MOZ_ASSERT(utcSeconds >= MinTimeT);
     MOZ_ASSERT(utcSeconds <= MaxTimeT);
 
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+    UDate date = UDate(utcSeconds * msPerSecond);
+    constexpr bool dateIsLocalTime = false;
+    int32_t rawOffset, dstOffset;
+    UErrorCode status = U_ZERO_ERROR;
+
+    timeZone()->getOffset(date, dateIsLocalTime, rawOffset, dstOffset, status);
+    if (U_FAILURE(status))
+        return 0;
+
+    return dstOffset;
+#else
     struct tm tm;
     if (!ComputeLocalTime(static_cast<time_t>(utcSeconds), &tm))
         return 0;
 
     // NB: The offset isn't computed correctly when the standard local offset
     //     at |utcSeconds| is different from |utcToLocalStandardOffsetSeconds|.
     int32_t dayoff = int32_t((utcSeconds + utcToLocalStandardOffsetSeconds_) % SecondsPerDay);
     int32_t tmoff = tm.tm_sec + (tm.tm_min * SecondsPerMinute) + (tm.tm_hour * SecondsPerHour);
@@ -201,16 +234,17 @@ js::DateTimeInfo::computeDSTOffsetMillis
     int32_t diff = tmoff - dayoff;
 
     if (diff < 0)
         diff += SecondsPerDay;
     else if (uint32_t(diff) >= SecondsPerDay)
         diff -= SecondsPerDay;
 
     return diff * msPerSecond;
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
 }
 
 int32_t
 js::DateTimeInfo::internalGetDSTOffsetMilliseconds(int64_t utcMilliseconds)
 {
     int64_t utcSeconds = toClampedSeconds(utcMilliseconds);
     return getOrComputeValue(dstRange_, utcSeconds, &DateTimeInfo::computeDSTOffsetMilliseconds);
 }
@@ -312,16 +346,147 @@ js::DateTimeInfo::RangeCache::sanityChec
         MOZ_ASSERT_IF(start != INT64_MIN,
                       start <= MaxTimeT && end <= MaxTimeT);
     };
 
     assertRange(startSeconds, endSeconds);
     assertRange(oldStartSeconds, oldEndSeconds);
 }
 
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+int32_t
+js::DateTimeInfo::computeUTCOffsetMilliseconds(int64_t localSeconds)
+{
+    MOZ_ASSERT(localSeconds >= MinTimeT);
+    MOZ_ASSERT(localSeconds <= MaxTimeT);
+
+    UDate date = UDate(localSeconds * msPerSecond);
+
+    // ES2019 draft rev 0ceb728a1adbffe42b26972a6541fd7f398b1557
+    //
+    // 20.3.1.7 LocalTZA
+    //
+    // If |localSeconds| represents either a skipped (at a positive time zone
+    // transition) or repeated (at a negative time zone transition) locale
+    // time, it must be interpreted as a time value before the transition.
+    constexpr int32_t skippedTime = icu::BasicTimeZone::kFormer;
+    constexpr int32_t repeatedTime = icu::BasicTimeZone::kFormer;
+
+    int32_t rawOffset, dstOffset;
+    UErrorCode status = U_ZERO_ERROR;
+
+    // All ICU TimeZone classes derive from BasicTimeZone, so we can safely
+    // perform the static_cast.
+    // Once <https://unicode-org.atlassian.net/browse/ICU-13705> is fixed we
+    // can remove this extra cast.
+    auto* basicTz = static_cast<icu::BasicTimeZone*>(timeZone());
+    basicTz->getOffsetFromLocal(date, skippedTime, repeatedTime, rawOffset, dstOffset, status);
+    if (U_FAILURE(status))
+        return 0;
+
+    return rawOffset + dstOffset;
+}
+
+int32_t
+js::DateTimeInfo::computeLocalOffsetMilliseconds(int64_t utcSeconds)
+{
+    MOZ_ASSERT(utcSeconds >= MinTimeT);
+    MOZ_ASSERT(utcSeconds <= MaxTimeT);
+
+    UDate date = UDate(utcSeconds * msPerSecond);
+    constexpr bool dateIsLocalTime = false;
+    int32_t rawOffset, dstOffset;
+    UErrorCode status = U_ZERO_ERROR;
+
+    timeZone()->getOffset(date, dateIsLocalTime, rawOffset, dstOffset, status);
+    if (U_FAILURE(status))
+        return 0;
+
+    return rawOffset + dstOffset;
+}
+
+int32_t
+js::DateTimeInfo::internalGetOffsetMilliseconds(int64_t milliseconds, TimeZoneOffset offset)
+{
+    int64_t seconds = toClampedSeconds(milliseconds);
+    return offset == TimeZoneOffset::UTC
+           ? getOrComputeValue(localRange_, seconds, &DateTimeInfo::computeLocalOffsetMilliseconds)
+           : getOrComputeValue(utcRange_, seconds, &DateTimeInfo::computeUTCOffsetMilliseconds);
+}
+
+bool
+js::DateTimeInfo::internalTimeZoneDisplayName(char16_t* buf, size_t buflen,
+                                              int64_t utcMilliseconds, const char* locale)
+{
+    MOZ_ASSERT(buf != nullptr);
+    MOZ_ASSERT(buflen > 0);
+    MOZ_ASSERT(locale != nullptr);
+
+    // Clear any previously cached names when the default locale changed.
+    if (!locale_ || std::strcmp(locale_.get(), locale) != 0) {
+        locale_ = DuplicateString(locale);
+        if (!locale_)
+            return false;
+
+        standardName_.reset();
+        daylightSavingsName_.reset();
+    }
+
+    bool daylightSavings = internalGetDSTOffsetMilliseconds(utcMilliseconds) != 0;
+
+    JS::UniqueTwoByteChars& cachedName = daylightSavings ? daylightSavingsName_ : standardName_;
+    if (!cachedName) {
+        // Retrieve the display name for the given locale.
+        icu::UnicodeString displayName;
+        timeZone()->getDisplayName(daylightSavings, icu::TimeZone::LONG, icu::Locale(locale),
+                                   displayName);
+
+        size_t capacity = displayName.length() + 1; // Null-terminate.
+        JS::UniqueTwoByteChars displayNameChars(js_pod_malloc<char16_t>(capacity));
+        if (!displayNameChars)
+            return false;
+
+        // Copy the display name. This operation always succeeds because the
+        // destination buffer is large enough to hold the complete string.
+        UErrorCode status = U_ZERO_ERROR;
+        displayName.extract(displayNameChars.get(), capacity, status);
+        MOZ_ASSERT(U_SUCCESS(status));
+        MOZ_ASSERT(displayNameChars[capacity - 1] == '\0');
+
+        cachedName = std::move(displayNameChars);
+    }
+
+    // Return an empty string if the display name doesn't fit into the buffer.
+    size_t length = js_strlen(cachedName.get());
+    if (length < buflen)
+        std::copy(cachedName.get(), cachedName.get() + length, buf);
+    else
+        length = 0;
+
+    buf[length] = '\0';
+    return true;
+}
+
+icu::TimeZone*
+js::DateTimeInfo::timeZone()
+{
+    if (!timeZone_) {
+        // The current default might be stale, because JS::ResetTimeZone()
+        // doesn't immediately update ICU's default time zone. So perform an
+        // update if needed.
+        js::ResyncICUDefaultTimeZone();
+
+        timeZone_.reset(icu::TimeZone::createDefault());
+        MOZ_ASSERT(timeZone_);
+    }
+
+    return timeZone_.get();
+}
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
+
 /* static */ js::ExclusiveData<js::DateTimeInfo>*
 js::DateTimeInfo::instance;
 
 /* static */ js::ExclusiveData<js::IcuTimeZoneStatus>*
 js::IcuTimeZoneState;
 
 #if defined(XP_WIN)
 static bool
--- a/js/src/vm/DateTime.h
+++ b/js/src/vm/DateTime.h
@@ -4,21 +4,31 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_DateTime_h
 #define vm_DateTime_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
 
 #include <stdint.h>
 
+#include "js/Utility.h"
 #include "threading/ExclusiveData.h"
 
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+#include "unicode/uversion.h"
+
+U_NAMESPACE_BEGIN
+class TimeZone;
+U_NAMESPACE_END
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
+
 namespace js {
 
 /* Constants defined by ES5 15.9.1.10. */
 constexpr double HoursPerDay = 24;
 constexpr double MinutesPerHour = 60;
 constexpr double SecondsPerMinute = 60;
 constexpr double msPerSecond = 1000;
 constexpr double msPerMinute = msPerSecond * SecondsPerMinute;
@@ -109,16 +119,17 @@ class DateTimeInfo
 {
     static ExclusiveData<DateTimeInfo>* instance;
     friend class ExclusiveData<DateTimeInfo>;
 
     friend bool InitDateTimeState();
     friend void FinishDateTimeState();
 
     DateTimeInfo();
+    ~DateTimeInfo();
 
   public:
     // The spec implicitly assumes DST and time zone adjustment information
     // never change in the course of a function -- sometimes even across
     // reentrancy.  So make critical sections as narrow as possible.
 
     /**
      * Get the DST offset in milliseconds at a UTC time.  This is usually
@@ -135,16 +146,42 @@ class DateTimeInfo
      * Return the local time zone adjustment (ES2019 20.3.1.7) as computed by
      * the operating system.
      */
     static int32_t localTZA() {
         auto guard = instance->lock();
         return guard->localTZA_;
     }
 
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+    enum class TimeZoneOffset { UTC, Local };
+
+    /**
+     * Return the time zone offset, including DST, in milliseconds at the
+     * given time. The input time can be either at UTC or at local time.
+     */
+    static int32_t getOffsetMilliseconds(int64_t milliseconds, TimeZoneOffset offset) {
+        auto guard = instance->lock();
+        return guard->internalGetOffsetMilliseconds(milliseconds, offset);
+    }
+
+    /**
+     * Copy the display name for the current time zone at the given time,
+     * localized for the specified locale, into the supplied buffer. If the
+     * buffer is too small, an empty string is stored. The stored display name
+     * is null-terminated in any case.
+     */
+    static bool timeZoneDisplayName(char16_t* buf, size_t buflen, int64_t utcMilliseconds,
+                                    const char* locale)
+    {
+        auto guard = instance->lock();
+        return guard->internalTimeZoneDisplayName(buf, buflen, utcMilliseconds, locale);
+    }
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
+
   private:
     // We don't want anyone accidentally calling *only*
     // DateTimeInfo::updateTimeZoneAdjustment() to respond to a system time
     // zone change (missing the necessary poking of ICU as well), so ensure
     // only js::ResetTimeZoneInternal() can call this via access restrictions.
     friend void js::ResetTimeZoneInternal(ResetTimeZoneMode);
 
     // Returns true iff the internal DST offset cache was purged.
@@ -183,26 +220,54 @@ class DateTimeInfo
     /*
      * Cached offset in seconds from the current UTC time to the current
      * local standard time (i.e. not including any offset due to DST).
      */
     int32_t utcToLocalStandardOffsetSeconds_;
 
     RangeCache dstRange_; // UTC-based ranges
 
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+    // ICU's TimeZone class is currently only available through the C++ API,
+    // see <https://unicode-org.atlassian.net/browse/ICU-13706>. Due to the
+    // lack of a stable ABI in C++, we therefore need to restrict this class
+    // to only use ICU when we use our in-tree ICU copy.
+
+    // Use the full date-time range when we can use ICU's TimeZone support.
+    static constexpr int64_t MinTimeT = static_cast<int64_t>(StartOfTime / msPerSecond);
+    static constexpr int64_t MaxTimeT = static_cast<int64_t>(EndOfTime / msPerSecond);
+
+    RangeCache utcRange_; // localtime-based ranges
+    RangeCache localRange_; // UTC-based ranges
+
+    /**
+     * The current ICU time zone. Lazily constructed to avoid potential I/O
+     * access when initializing this class.
+     */
+    mozilla::UniquePtr<icu::TimeZone> timeZone_;
+
+    /**
+     * Cached names of the standard and daylight savings display names of the
+     * current time zone for the default locale.
+     */
+    JS::UniqueChars locale_;
+    JS::UniqueTwoByteChars standardName_;
+    JS::UniqueTwoByteChars daylightSavingsName_;
+#else
     // Restrict the data-time range to the minimum required time_t range as
     // specified in POSIX. Most operating systems support 64-bit time_t
     // values, but we currently still have some configurations which use
     // 32-bit time_t, e.g. the ARM simulator on 32-bit Linux (bug 1406993).
     // Bug 1406992 explores to use 64-bit time_t when supported by the
     // underlying operating system.
-    static const int64_t MinTimeT = 0; /* time_t 01/01/1970 */
-    static const int64_t MaxTimeT = 2145830400; /* time_t 12/31/2037 */
+    static constexpr int64_t MinTimeT = 0; /* time_t 01/01/1970 */
+    static constexpr int64_t MaxTimeT = 2145830400; /* time_t 12/31/2037 */
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
 
-    static const int64_t RangeExpansionAmount = 30 * SecondsPerDay;
+    static constexpr int64_t RangeExpansionAmount = 30 * SecondsPerDay;
 
     bool internalUpdateTimeZoneAdjustment(ResetTimeZoneMode mode);
 
     int64_t toClampedSeconds(int64_t milliseconds);
 
     using ComputeFn = int32_t (DateTimeInfo::*)(int64_t);
 
     /**
@@ -213,16 +278,37 @@ class DateTimeInfo
     /**
      * Compute the DST offset at the given UTC time in seconds from the epoch.
      * (getDSTOffsetMilliseconds attempts to return a cached value from the
      * dstRange_ member, but in case of a cache miss it calls this method.)
      */
     int32_t computeDSTOffsetMilliseconds(int64_t utcSeconds);
 
     int32_t internalGetDSTOffsetMilliseconds(int64_t utcMilliseconds);
+
+#if ENABLE_INTL_API && !MOZ_SYSTEM_ICU
+    /**
+     * Compute the UTC offset in milliseconds for the given local time. Called
+     * by internalGetOffsetMilliseconds on a cache miss.
+     */
+    int32_t computeUTCOffsetMilliseconds(int64_t localSeconds);
+
+    /**
+     * Compute the local time offset in milliseconds for the given UTC time.
+     * Called by internalGetOffsetMilliseconds on a cache miss.
+     */
+    int32_t computeLocalOffsetMilliseconds(int64_t utcSeconds);
+
+    int32_t internalGetOffsetMilliseconds(int64_t milliseconds, TimeZoneOffset offset);
+
+    bool internalTimeZoneDisplayName(char16_t* buf, size_t buflen, int64_t utcMilliseconds,
+                                     const char* locale);
+
+    icu::TimeZone* timeZone();
+#endif /* ENABLE_INTL_API && !MOZ_SYSTEM_ICU */
 };
 
 enum class IcuTimeZoneStatus { Valid, NeedsUpdate };
 
 extern ExclusiveData<IcuTimeZoneStatus>*
 IcuTimeZoneState;
 
 /**
--- a/js/src/vm/MutexIDs.h
+++ b/js/src/vm/MutexIDs.h
@@ -37,30 +37,30 @@
   _(ShellOffThreadState,         500) \
   _(SimulatorCacheLock,          500) \
   _(Arm64SimulatorLock,          500) \
   _(IonSpewer,                   500) \
   _(PerfSpewer,                  500) \
   _(CacheIRSpewer,               500) \
   _(TraceLoggerThreadState,      500) \
   _(DateTimeInfoMutex,           500) \
-  _(IcuTimeZoneStateMutex,       500) \
   _(ProcessExecutableRegion,     500) \
   _(OffThreadPromiseState,       500) \
   _(BufferStreamState,           500) \
   _(SharedArrayGrow,             500) \
   _(RuntimeScriptData,           500) \
   _(WasmFuncTypeIdSet,           500) \
   _(WasmCodeProfilingLabels,     500) \
   _(WasmCompileTaskState,        500) \
   _(WasmCodeStreamEnd,           500) \
   _(WasmTailBytesPtr,            500) \
   _(WasmStreamStatus,            500) \
   _(WasmRuntimeInstances,        500) \
                                       \
+  _(IcuTimeZoneStateMutex,       600) \
   _(ThreadId,                    600) \
   _(WasmCodeSegmentMap,          600) \
   _(TraceLoggerGraphState,       600) \
   _(VTuneLock,                   600)
 
 namespace js {
 namespace mutexid {