Bug 1205586 - new Date().toLocale{,Date,Time}String() should return appropriately differing strings as the local time zone/default locale change. r=till

This commit is contained in:
Jeff Walden 2015-09-18 17:16:08 -07:00
parent 2cf79d22b7
commit 5e91264490
4 changed files with 133 additions and 15 deletions

View File

@ -5,9 +5,73 @@
/*global intl_DateTimeFormat: false, */
// This cache, once primed, has these properties:
//
// runtimeDefaultLocale:
// Locale information provided by the embedding, guiding SpiderMonkey's
// selection of a default locale. See RuntimeDefaultLocale(), whose
// value controls the value returned by DefaultLocale() that's what's
// *actually* used.
// localTZA:
// The local time zone's adjustment from UTC. See LocalTZA().
// formatters:
// A Record storing formatters consistent with the above
// runtimeDefaultLocale/localTZA values, for use with the appropriate
// ES6 toLocale*String Date method when called with its first two
// arguments having the value |undefined|.
//
// The "formatters" Record has (some subset of) these properties, as determined
// by all values of the first argument passed to |GetCachedFormat|:
//
// dateTimeFormat: for Date's toLocaleString operation
// dateFormat: for Date's toLocaleDateString operation
// timeFormat: for Date's toLocaleTimeString operation
//
// Using this cache, then, requires 1) verifying the current
// runtimeDefaultLocale/localTZA are consistent with cached values, then
// 2) seeing if the desired formatter is cached and returning it if so, or else
// 3) create the desired formatter and store and return it.
var dateTimeFormatCache = new Record();
/**
* Get a cached DateTimeFormat formatter object, created like so:
*
* var opts = ToDateTimeOptions(undefined, required, defaults);
* return new Intl.DateTimeFormat(undefined, opts);
*
* |format| must be a key from the "formatters" Record described above.
*/
function GetCachedFormat(format, required, defaults) {
assert(format === "dateTimeFormat" ||
format === "dateFormat" ||
format === "timeFormat",
"unexpected format key: please update the comment by " +
"dateTimeFormatCache");
var runtimeDefaultLocale = RuntimeDefaultLocale();
var localTZA = LocalTZA();
var formatters;
if (dateTimeFormatCache.runtimeDefaultLocale !== runtimeDefaultLocale ||
dateTimeFormatCache.localTZA !== localTZA)
{
formatters = dateTimeFormatCache.formatters = new Record();
dateTimeFormatCache.runtimeDefaultLocale = runtimeDefaultLocale;
dateTimeFormatCache.localTZA = localTZA;
} else {
formatters = dateTimeFormatCache.formatters;
}
var fmt = formatters[format];
if (fmt === undefined) {
var options = ToDateTimeOptions(undefined, required, defaults);
fmt = formatters[format] = intl_DateTimeFormat(undefined, options);
}
return fmt;
}
/**
* Format this Date object into a date and time string, using the locale and
* formatting options provided.
@ -30,11 +94,7 @@ function Date_toLocaleString() {
if (locales === undefined && options === undefined) {
// This cache only optimizes for the old ES5 toLocaleString without
// locales and options.
if (dateTimeFormatCache.dateTimeFormat === undefined) {
options = ToDateTimeOptions(options, "any", "all");
dateTimeFormatCache.dateTimeFormat = intl_DateTimeFormat(locales, options);
}
dateTimeFormat = dateTimeFormatCache.dateTimeFormat;
dateTimeFormat = GetCachedFormat("dateTimeFormat", "any", "all");
} else {
options = ToDateTimeOptions(options, "any", "all");
dateTimeFormat = intl_DateTimeFormat(locales, options);
@ -67,11 +127,7 @@ function Date_toLocaleDateString() {
if (locales === undefined && options === undefined) {
// This cache only optimizes for the old ES5 toLocaleDateString without
// locales and options.
if (dateTimeFormatCache.dateFormat === undefined) {
options = ToDateTimeOptions(options, "date", "date");
dateTimeFormatCache.dateFormat = intl_DateTimeFormat(locales, options);
}
dateTimeFormat = dateTimeFormatCache.dateFormat;
dateTimeFormat = GetCachedFormat("dateFormat", "date", "date");
} else {
options = ToDateTimeOptions(options, "date", "date");
dateTimeFormat = intl_DateTimeFormat(locales, options);
@ -104,11 +160,7 @@ function Date_toLocaleTimeString() {
if (locales === undefined && options === undefined) {
// This cache only optimizes for the old ES5 toLocaleTimeString without
// locales and options.
if (dateTimeFormatCache.timeFormat === undefined) {
options = ToDateTimeOptions(options, "time", "time");
dateTimeFormatCache.timeFormat = intl_DateTimeFormat(locales, options);
}
dateTimeFormat = dateTimeFormatCache.timeFormat;
dateTimeFormat = GetCachedFormat("timeFormat", "time", "time");
} else {
options = ToDateTimeOptions(options, "time", "time");
dateTimeFormat = intl_DateTimeFormat(locales, options);

View File

@ -18,6 +18,7 @@ UNIFIED_SOURCES += [
'testClassGetter.cpp',
'testCloneScript.cpp',
'testContexts.cpp',
'testDateToLocaleString.cpp',
'testDebugger.cpp',
'testDeepFreeze.cpp',
'testDefineGetterSetterNonEnumerable.cpp',

View File

@ -0,0 +1,54 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "jsapi-tests/tests.h"
BEGIN_TEST(testDateToLocaleString)
{
// This test should only attempt to run if we have Intl support: necessary
// to properly assume that changes to the default locale will predictably
// affect the behavior of the locale-sensitive Date methods tested here.
JS::Rooted<JS::Value> haveIntl(cx);
EVAL("typeof Intl !== 'undefined'", &haveIntl);
if (!haveIntl.toBoolean())
return true;
// Pervasive assumption: our Intl support includes "de" (German) and
// "en" (English) and treats them differently for purposes of
// Date.prototype.toLocale{,Date,Time}String behavior.
// Start with German.
CHECK(JS_SetDefaultLocale(rt, "de"));
// The (constrained) Date object we'll use to test behavior.
EXEC("var d = new Date(Date.UTC(2015, 9 - 1, 17));");
// Test that toLocaleString behavior changes with default locale changes.
EXEC("var deAll = d.toLocaleString();");
CHECK(JS_SetDefaultLocale(rt, "en"));
EXEC("if (d.toLocaleString() === deAll) \n"
" throw 'toLocaleString results should have changed with system locale change';");
// Test that toLocaleDateString behavior changes with default locale changes.
EXEC("var enDate = d.toLocaleDateString();");
CHECK(JS_SetDefaultLocale(rt, "de"));
EXEC("if (d.toLocaleDateString() === enDate) \n"
" throw 'toLocaleDateString results should have changed with system locale change';");
// Test that toLocaleTimeString behavior changes with default locale changes.
EXEC("var deTime = d.toLocaleTimeString();");
CHECK(JS_SetDefaultLocale(rt, "en"));
EXEC("if (d.toLocaleTimeString() === deTime) \n"
" throw 'toLocaleTimeString results should have changed with system locale change';");
JS_ResetDefaultLocale(rt);
return true;
}
END_TEST(testDateToLocaleString)

View File

@ -1204,6 +1204,16 @@ intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
intrinsic_LocalTZA(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0, "the LocalTZA intrinsic takes no arguments");
args.rval().setDouble(cx->runtime()->dateTimeInfo.localTZA());
return true;
}
static bool
intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp)
{
@ -1324,6 +1334,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0),
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
JS_FN("LocalTZA", intrinsic_LocalTZA, 0,0),
JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0,
IntrinsicIsConstructing),