summaryrefslogtreecommitdiffstats
path: root/js/src/tests/Intl
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/Intl')
-rw-r--r--js/src/tests/Intl/Array/shell.js0
-rw-r--r--js/src/tests/Intl/Array/toLocaleString-date.js53
-rw-r--r--js/src/tests/Intl/Array/toLocaleString-number.js34
-rw-r--r--js/src/tests/Intl/Array/toLocaleString.js35
-rw-r--r--js/src/tests/Intl/Collator/browser.js0
-rw-r--r--js/src/tests/Intl/Collator/compare.js129
-rw-r--r--js/src/tests/Intl/Collator/shell.js0
-rw-r--r--js/src/tests/Intl/Collator/supportedLocalesOf.js355
-rw-r--r--js/src/tests/Intl/Date/browser.js0
-rw-r--r--js/src/tests/Intl/Date/shell.js0
-rw-r--r--js/src/tests/Intl/Date/toLocaleDateString_timeZone.js77
-rw-r--r--js/src/tests/Intl/Date/toLocaleString_timeZone.js77
-rw-r--r--js/src/tests/Intl/Date/toLocaleTimeString_timeZone.js77
-rw-r--r--js/src/tests/Intl/DateTimeFormat/browser.js0
-rw-r--r--js/src/tests/Intl/DateTimeFormat/format.js36
-rw-r--r--js/src/tests/Intl/DateTimeFormat/formatToParts.js170
-rw-r--r--js/src/tests/Intl/DateTimeFormat/format_timeZone.js104
-rw-r--r--js/src/tests/Intl/DateTimeFormat/shell.js0
-rw-r--r--js/src/tests/Intl/DateTimeFormat/supportedLocalesOf.js371
-rw-r--r--js/src/tests/Intl/DateTimeFormat/timeZone.js152
-rw-r--r--js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js134
-rw-r--r--js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js115
-rw-r--r--js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js38
-rw-r--r--js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js51
-rw-r--r--js/src/tests/Intl/NumberFormat/StringBuffer.js17
-rw-r--r--js/src/tests/Intl/NumberFormat/browser.js0
-rw-r--r--js/src/tests/Intl/NumberFormat/duplicate-singleton-variant.js49
-rw-r--r--js/src/tests/Intl/NumberFormat/format-as-code-or-name.js75
-rw-r--r--js/src/tests/Intl/NumberFormat/format.js47
-rw-r--r--js/src/tests/Intl/NumberFormat/remove-unicode-extensions.js24
-rw-r--r--js/src/tests/Intl/NumberFormat/shell.js0
-rw-r--r--js/src/tests/Intl/NumberFormat/significantDigitsOfZero.js36
-rw-r--r--js/src/tests/Intl/NumberFormat/supportedLocalesOf.js371
-rw-r--r--js/src/tests/Intl/README.txt27
-rw-r--r--js/src/tests/Intl/TypedArray/shell.js0
-rw-r--r--js/src/tests/Intl/TypedArray/toLocaleString.js103
-rw-r--r--js/src/tests/Intl/browser.js0
-rw-r--r--js/src/tests/Intl/extensions/browser.js0
-rw-r--r--js/src/tests/Intl/extensions/options-value-emulates-undefined.js35
-rw-r--r--js/src/tests/Intl/extensions/shell.js0
-rw-r--r--js/src/tests/Intl/four-letter-language-codes.js20
-rw-r--r--js/src/tests/Intl/getCalendarInfo.js83
-rw-r--r--js/src/tests/Intl/getCanonicalLocales-overridden-arg-length.js20
-rw-r--r--js/src/tests/Intl/getCanonicalLocales-overridden-push.js16
-rw-r--r--js/src/tests/Intl/getCanonicalLocales-overridden-set.js16
-rw-r--r--js/src/tests/Intl/getCanonicalLocales-weird-cases.js24
-rw-r--r--js/src/tests/Intl/getCanonicalLocales-with-duplicates.js15
-rw-r--r--js/src/tests/Intl/getCanonicalLocales.js37
-rw-r--r--js/src/tests/Intl/shell.js0
49 files changed, 3023 insertions, 0 deletions
diff --git a/js/src/tests/Intl/Array/shell.js b/js/src/tests/Intl/Array/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/Array/shell.js
diff --git a/js/src/tests/Intl/Array/toLocaleString-date.js b/js/src/tests/Intl/Array/toLocaleString-date.js
new file mode 100644
index 000000000..7711c6064
--- /dev/null
+++ b/js/src/tests/Intl/Array/toLocaleString-date.js
@@ -0,0 +1,53 @@
+if (typeof Intl === "object") {
+ const localeSep = [,,].toLocaleString();
+
+ const date = new Date(Date.UTC(2012, 11, 12, 3, 0, 0));
+
+ assertEq([date].toLocaleString("en-us", {timeZone: "UTC"}), "12/12/2012, 3:00:00 AM");
+ assertEq([date].toLocaleString(["de", "en"], {timeZone: "UTC"}), "12.12.2012, 03:00:00");
+ assertEq([date].toLocaleString("th-th", {timeZone: "UTC"}), "12/12/2555 03:00:00");
+ assertEq([date].toLocaleString("th-th-u-nu-thai", {timeZone: "UTC"}), "๑๒/๑๒/๒๕๕๕ ๐๓:๐๐:๐๐");
+
+ const sampleValues = [
+ date, new Date(0),
+ ];
+ const sampleLocales = [
+ void 0,
+ "en",
+ "th-th-u-nu-thai",
+ "ja-jp",
+ "ar-ma-u-ca-islamicc",
+ ["tlh", "de"],
+ ];
+ const numericFormatOptions = {
+ timeZone: "UTC",
+ year: "numeric", month: "numeric", day: "numeric",
+ hour: "numeric", minute: "numeric", second: "numeric",
+ };
+ const longFormatOptions = {
+ timeZone: "UTC",
+ year: "numeric", month: "long", day: "numeric",
+ hour: "numeric", minute: "numeric", second: "numeric"
+ };
+ const sampleOptions = [
+ {timeZone: "UTC"},
+ longFormatOptions,
+ ];
+
+ for (let locale of sampleLocales) {
+ for (let options of sampleOptions) {
+ let dtfOptions;
+ if (options === longFormatOptions) {
+ dtfOptions = longFormatOptions;
+ } else {
+ dtfOptions = numericFormatOptions;
+ }
+ let dtf = new Intl.DateTimeFormat(locale, dtfOptions);
+ let expected = sampleValues.map(dtf.format).join(localeSep);
+ assertEq(sampleValues.toLocaleString(locale, options), expected);
+ }
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/Intl/Array/toLocaleString-number.js b/js/src/tests/Intl/Array/toLocaleString-number.js
new file mode 100644
index 000000000..6ca8a1783
--- /dev/null
+++ b/js/src/tests/Intl/Array/toLocaleString-number.js
@@ -0,0 +1,34 @@
+if (typeof Intl === "object") {
+ const localeSep = [,,].toLocaleString();
+
+ assertEq([NaN].toLocaleString("ar"), "ليس رقم");
+ assertEq([NaN].toLocaleString(["zh-hant", "ar"]), "非數值");
+ assertEq([Infinity].toLocaleString("dz"), "གྲངས་མེད");
+ assertEq([-Infinity].toLocaleString(["fr", "en"]), "-∞");
+
+ const sampleValues = [
+ -0, +0, -1, +1, -2, +2, -0.5, +0.5,
+ ];
+ const sampleLocales = [
+ void 0,
+ "en",
+ "th-th-u-nu-thai",
+ ["tlh", "de"],
+ ];
+ const sampleOptions = [
+ void 0,
+ {},
+ {style: "percent"},
+ {style: "currency", currency: "USD", minimumIntegerDigits: 4},
+ ];
+ for (let locale of sampleLocales) {
+ for (let options of sampleOptions) {
+ let nf = new Intl.NumberFormat(locale, options);
+ let expected = sampleValues.map(nf.format).join(localeSep);
+ assertEq(sampleValues.toLocaleString(locale, options), expected);
+ }
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/Intl/Array/toLocaleString.js b/js/src/tests/Intl/Array/toLocaleString.js
new file mode 100644
index 000000000..f94e531f8
--- /dev/null
+++ b/js/src/tests/Intl/Array/toLocaleString.js
@@ -0,0 +1,35 @@
+if (typeof Intl === "object") {
+ const localeSep = [,,].toLocaleString();
+
+ // Missing arguments are passed as |undefined|.
+ const objNoArgs = {
+ toLocaleString() {
+ assertEq(arguments.length, 2);
+ assertEq(arguments[0], undefined);
+ assertEq(arguments[1], undefined);
+ return "pass";
+ }
+ };
+ // - Single element case.
+ assertEq([objNoArgs].toLocaleString(), "pass");
+ // - More than one element.
+ assertEq([objNoArgs, objNoArgs].toLocaleString(), "pass" + localeSep + "pass");
+
+ // Ensure "locales" and "options" arguments are passed to the array elements.
+ const locales = {}, options = {};
+ const objWithArgs = {
+ toLocaleString() {
+ assertEq(arguments.length, 2);
+ assertEq(arguments[0], locales);
+ assertEq(arguments[1], options);
+ return "pass";
+ }
+ };
+ // - Single element case.
+ assertEq([objWithArgs].toLocaleString(locales, options), "pass");
+ // - More than one element.
+ assertEq([objWithArgs, objWithArgs].toLocaleString(locales, options), "pass" + localeSep + "pass");
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/Intl/Collator/browser.js b/js/src/tests/Intl/Collator/browser.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/Collator/browser.js
diff --git a/js/src/tests/Intl/Collator/compare.js b/js/src/tests/Intl/Collator/compare.js
new file mode 100644
index 000000000..9279b85f7
--- /dev/null
+++ b/js/src/tests/Intl/Collator/compare.js
@@ -0,0 +1,129 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Tests the compare function with a diverse set of locales and options.
+
+var input = [
+ "Argentina",
+ "Oerlikon",
+ "Offenbach",
+ "Sverige",
+ "Vaticano",
+ "Zimbabwe",
+ "la France",
+ "¡viva España!",
+ "Österreich",
+ "中国",
+ "日本",
+ "한국",
+];
+
+var collator, expected;
+
+function assertEqualArray(actual, expected, collator) {
+ var description = JSON.stringify(collator.resolvedOptions());
+ assertEq(actual.length, expected.length, "array length, " + description);
+ for (var i = 0; i < actual.length; i++) {
+ assertEq(actual[i], expected[i], "element " + i + ", " + description);
+ }
+}
+
+
+// Locale en-US; default options.
+collator = new Intl.Collator("en-US");
+expected = [
+ "¡viva España!",
+ "Argentina",
+ "la France",
+ "Oerlikon",
+ "Offenbach",
+ "Österreich",
+ "Sverige",
+ "Vaticano",
+ "Zimbabwe",
+ "한국",
+ "中国",
+ "日本",
+];
+assertEqualArray(input.sort(collator.compare), expected, collator);
+
+// Locale sv-SE; default options.
+// Swedish treats "Ö" as a separate character, which sorts after "Z".
+collator = new Intl.Collator("sv-SE");
+expected = [
+ "¡viva España!",
+ "Argentina",
+ "la France",
+ "Oerlikon",
+ "Offenbach",
+ "Sverige",
+ "Vaticano",
+ "Zimbabwe",
+ "Österreich",
+ "한국",
+ "中国",
+ "日本",
+];
+assertEqualArray(input.sort(collator.compare), expected, collator);
+
+// Locale sv-SE; ignore punctuation.
+collator = new Intl.Collator("sv-SE", {ignorePunctuation: true});
+expected = [
+ "Argentina",
+ "la France",
+ "Oerlikon",
+ "Offenbach",
+ "Sverige",
+ "Vaticano",
+ "¡viva España!",
+ "Zimbabwe",
+ "Österreich",
+ "한국",
+ "中国",
+ "日本",
+];
+assertEqualArray(input.sort(collator.compare), expected, collator);
+
+// Locale de-DE; default options.
+// In German standard sorting, umlauted characters are treated as variants
+// of their base characters: ä ≅ a, ö ≅ o, ü ≅ u.
+collator = new Intl.Collator("de-DE");
+expected = [
+ "¡viva España!",
+ "Argentina",
+ "la France",
+ "Oerlikon",
+ "Offenbach",
+ "Österreich",
+ "Sverige",
+ "Vaticano",
+ "Zimbabwe",
+ "한국",
+ "中国",
+ "日本",
+];
+assertEqualArray(input.sort(collator.compare), expected, collator);
+
+// Locale de-DE; phonebook sort order.
+// In German phonebook sorting, umlauted characters are expanded to two-vowel
+// sequences: ä → ae, ö → oe, ü → ue.
+collator = new Intl.Collator("de-DE-u-co-phonebk");
+expected = [
+ "¡viva España!",
+ "Argentina",
+ "la France",
+ "Oerlikon",
+ "Österreich",
+ "Offenbach",
+ "Sverige",
+ "Vaticano",
+ "Zimbabwe",
+ "한국",
+ "中国",
+ "日本",
+];
+assertEqualArray(input.sort(collator.compare), expected, collator);
+
+reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/Intl/Collator/shell.js b/js/src/tests/Intl/Collator/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/Collator/shell.js
diff --git a/js/src/tests/Intl/Collator/supportedLocalesOf.js b/js/src/tests/Intl/Collator/supportedLocalesOf.js
new file mode 100644
index 000000000..7fbebca09
--- /dev/null
+++ b/js/src/tests/Intl/Collator/supportedLocalesOf.js
@@ -0,0 +1,355 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl")||xulRuntime.shell)
+// -- test in browser only that ICU has locale data for all Mozilla languages
+
+/* 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/. */
+
+// This array contains the locales that ICU supports in
+// collation whose languages Mozilla localizes Firefox into.
+// Current as of ICU 50.1.2 and Firefox March 2013.
+var locales = [
+ "af",
+ "af-NA",
+ "af-ZA",
+ "ar",
+ "ar-001",
+ "ar-AE",
+ "ar-BH",
+ "ar-DJ",
+ "ar-DZ",
+ "ar-EG",
+ "ar-EH",
+ "ar-ER",
+ "ar-IL",
+ "ar-IQ",
+ "ar-JO",
+ "ar-KM",
+ "ar-KW",
+ "ar-LB",
+ "ar-LY",
+ "ar-MA",
+ "ar-MR",
+ "ar-OM",
+ "ar-PS",
+ "ar-QA",
+ "ar-SA",
+ "ar-SD",
+ "ar-SO",
+ "ar-SY",
+ "ar-TD",
+ "ar-TN",
+ "ar-YE",
+ "as",
+ "as-IN",
+ "be",
+ "be-BY",
+ "bg",
+ "bg-BG",
+ "bn",
+ "bn-BD",
+ "bn-IN",
+ "bs",
+ "bs-Cyrl",
+ "bs-Cyrl-BA",
+ "bs-Latn",
+ "bs-Latn-BA",
+ "ca",
+ "ca-AD",
+ "ca-ES",
+ "cs",
+ "cs-CZ",
+ "cy",
+ "cy-GB",
+ "da",
+ "da-DK",
+ "de",
+ "de-AT",
+ "de-BE",
+ "de-CH",
+ "de-DE",
+ "de-LI",
+ "de-LU",
+ "el",
+ "el-CY",
+ "el-GR",
+ "en",
+ "en-150",
+ "en-AG",
+ "en-AS",
+ "en-AU",
+ "en-BB",
+ "en-BE",
+ "en-BM",
+ "en-BS",
+ "en-BW",
+ "en-BZ",
+ "en-CA",
+ "en-CM",
+ "en-DM",
+ "en-FJ",
+ "en-FM",
+ "en-GB",
+ "en-GD",
+ "en-GG",
+ "en-GH",
+ "en-GI",
+ "en-GM",
+ "en-GU",
+ "en-HK",
+ "en-IE",
+ "en-IM",
+ "en-IN",
+ "en-JE",
+ "en-JM",
+ "en-KE",
+ "en-KI",
+ "en-KN",
+ "en-KY",
+ "en-LC",
+ "en-LR",
+ "en-LS",
+ "en-MG",
+ "en-MH",
+ "en-MP",
+ "en-MT",
+ "en-MU",
+ "en-MW",
+ "en-NA",
+ "en-NG",
+ "en-NZ",
+ "en-PG",
+ "en-PH",
+ "en-PK",
+ "en-PR",
+ "en-PW",
+ "en-SB",
+ "en-SC",
+ "en-SG",
+ "en-SL",
+ "en-SS",
+ "en-TC",
+ "en-TO",
+ "en-TT",
+ "en-TZ",
+ "en-UG",
+ "en-UM",
+ "en-US",
+ "en-US-POSIX",
+ "en-VC",
+ "en-VG",
+ "en-VI",
+ "en-VU",
+ "en-WS",
+ "en-ZA",
+ "en-ZM",
+ "en-ZW",
+ "eo",
+ "es",
+ "es-419",
+ "es-AR",
+ "es-BO",
+ "es-CL",
+ "es-CO",
+ "es-CR",
+ "es-CU",
+ "es-DO",
+ "es-EA",
+ "es-EC",
+ "es-ES",
+ "es-GQ",
+ "es-GT",
+ "es-HN",
+ "es-IC",
+ "es-MX",
+ "es-NI",
+ "es-PA",
+ "es-PE",
+ "es-PH",
+ "es-PR",
+ "es-PY",
+ "es-SV",
+ "es-US",
+ "es-UY",
+ "es-VE",
+ "et",
+ "et-EE",
+ "fa",
+ "fa-AF",
+ "fa-IR",
+ "fi",
+ "fi-FI",
+ "fr",
+ "fr-BE",
+ "fr-BF",
+ "fr-BI",
+ "fr-BJ",
+ "fr-BL",
+ "fr-CA",
+ "fr-CD",
+ "fr-CF",
+ "fr-CG",
+ "fr-CH",
+ "fr-CI",
+ "fr-CM",
+ "fr-DJ",
+ "fr-DZ",
+ "fr-FR",
+ "fr-GA",
+ "fr-GN",
+ "fr-GP",
+ "fr-GQ",
+ "fr-HT",
+ "fr-KM",
+ "fr-LU",
+ "fr-MA",
+ "fr-MC",
+ "fr-MF",
+ "fr-MG",
+ "fr-ML",
+ "fr-MQ",
+ "fr-MR",
+ "fr-MU",
+ "fr-NC",
+ "fr-NE",
+ "fr-PF",
+ "fr-RE",
+ "fr-RW",
+ "fr-SC",
+ "fr-SN",
+ "fr-SY",
+ "fr-TD",
+ "fr-TG",
+ "fr-TN",
+ "fr-VU",
+ "ga",
+ "ga-IE",
+ "gu",
+ "gu-IN",
+ "he",
+ "he-IL",
+ "hi",
+ "hi-IN",
+ "hr",
+ "hr-BA",
+ "hr-HR",
+ "hu",
+ "hu-HU",
+ "hy",
+ "hy-AM",
+ "id",
+ "id-ID",
+ "is",
+ "is-IS",
+ "it",
+ "it-CH",
+ "it-IT",
+ "it-SM",
+ "ja",
+ "ja-JP",
+ "kk",
+ "kk-KZ",
+ "km",
+ "km-KH",
+ "kn",
+ "kn-IN",
+ "ko",
+ "ko-KP",
+ "ko-KR",
+ "lt",
+ "lt-LT",
+ "lv",
+ "lv-LV",
+ "mk",
+ "mk-MK",
+ "ml",
+ "ml-IN",
+ "mr",
+ "mr-IN",
+ "nb",
+ "nb-NO",
+ "nl",
+ "nl-AW",
+ "nl-BE",
+ "nl-CW",
+ "nl-NL",
+ "nl-SR",
+ "nl-SX",
+ "nn",
+ "nn-NO",
+ "or",
+ "or-IN",
+ "pa",
+ "pa-Arab",
+ "pa-Arab-PK",
+ "pa-Guru",
+ "pa-Guru-IN",
+ "pl",
+ "pl-PL",
+ "pt",
+ "pt-AO",
+ "pt-BR",
+ "pt-CV",
+ "pt-GW",
+ "pt-MO",
+ "pt-MZ",
+ "pt-PT",
+ "pt-ST",
+ "pt-TL",
+ "ro",
+ "ro-MD",
+ "ro-RO",
+ "ru",
+ "ru-BY",
+ "ru-KG",
+ "ru-KZ",
+ "ru-MD",
+ "ru-RU",
+ "ru-UA",
+ "si",
+ "si-LK",
+ "sk",
+ "sk-SK",
+ "sl",
+ "sl-SI",
+ "sq",
+ "sq-AL",
+ "sq-MK",
+ "sr",
+ "sr-Cyrl",
+ "sr-Cyrl-BA",
+ "sr-Cyrl-ME",
+ "sr-Cyrl-RS",
+ "sr-Latn",
+ "sr-Latn-BA",
+ "sr-Latn-ME",
+ "sr-Latn-RS",
+ "sv",
+ "sv-AX",
+ "sv-FI",
+ "sv-SE",
+ "te",
+ "te-IN",
+ "th",
+ "th-TH",
+ "tr",
+ "tr-CY",
+ "tr-TR",
+ "uk",
+ "uk-UA",
+ "vi",
+ "vi-VN",
+ "zh",
+ "zh-Hans",
+ "zh-Hans-CN",
+ "zh-Hans-SG",
+ "zh-Hant",
+ "zh-Hant-HK",
+ "zh-Hant-MO",
+ "zh-Hant-TW",
+];
+
+
+var count = Intl.Collator.supportedLocalesOf(locales).length;
+
+reportCompare(locales.length, count, "Number of supported locales in Intl.Collator");
diff --git a/js/src/tests/Intl/Date/browser.js b/js/src/tests/Intl/Date/browser.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/Date/browser.js
diff --git a/js/src/tests/Intl/Date/shell.js b/js/src/tests/Intl/Date/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/Date/shell.js
diff --git a/js/src/tests/Intl/Date/toLocaleDateString_timeZone.js b/js/src/tests/Intl/Date/toLocaleDateString_timeZone.js
new file mode 100644
index 000000000..47b77dbe8
--- /dev/null
+++ b/js/src/tests/Intl/Date/toLocaleDateString_timeZone.js
@@ -0,0 +1,77 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const defaultLocale = "en-US";
+const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0);
+const defaultOptions = { timeZoneName: "short" };
+
+const tests = [
+ {
+ timeZone: "UTC",
+ result: "12/6/2012, GMT",
+ },
+ {
+ timeZone: "America/Los_Angeles",
+ result: "12/6/2012, PST",
+ },
+ {
+ timeZone: "Europe/Berlin", locale: "de",
+ options: { timeZoneName: "short" },
+ result: "6.12.2012, MEZ",
+ },
+ {
+ timeZone: "Europe/Paris", locale: "fr",
+ options: { timeZoneName: "long" },
+ result: "06/12/2012 à heure normale d’Europe centrale",
+ },
+ {
+ timeZone: "Asia/Shanghai", locale: "zh-Hans-CN",
+ options: { timeZoneName: "long" },
+ result: "2012/12/6 中国标准时间",
+ },
+ {
+ timeZone: { toString: () => "Australia/Melbourne" }, locale: "en-AU",
+ result: "06/12/2012, AEDT",
+ },
+];
+
+for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) {
+ let s = new Date(date).toLocaleDateString(locale, Object.assign({timeZone}, options));
+ assertEq(s, result);
+}
+
+
+// |undefined| or absent "timeZone" option selects the default time zone.
+{
+ let locale = defaultLocale;
+ let date = defaultDate;
+ let options = defaultOptions;
+
+ let absentTz = new Date(date).toLocaleDateString(locale, Object.assign({}, options));
+ let undefinedTz = new Date(date).toLocaleDateString(locale, Object.assign({timeZone: undefined}, options));
+ assertEq(undefinedTz, absentTz);
+}
+
+
+// RangeError is thrown for invalid time zone names.
+for (let timeZone of ["", "undefined", "UTC\0", "Vienna", "Africa", "America/NewYork"]) {
+ assertThrowsInstanceOf(() => {
+ new Date(defaultDate).toLocaleDateString(undefined, {timeZone});
+ }, RangeError);
+}
+
+// RangeError is thrown for these values, because ToString(<value>)
+// isn't a valid time zone name.
+for (let timeZone of [null, 0, 0.5, true, false, [], {}, function() {}]) {
+ assertThrowsInstanceOf(() => {
+ new Date(defaultDate).toLocaleDateString(undefined, {timeZone});
+ }, RangeError);
+}
+
+// ToString(<symbol>) throws TypeError.
+assertThrowsInstanceOf(() => {
+ new Date(defaultDate).toLocaleDateString(undefined, {timeZone: Symbol()});
+}, TypeError);
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/Intl/Date/toLocaleString_timeZone.js b/js/src/tests/Intl/Date/toLocaleString_timeZone.js
new file mode 100644
index 000000000..63e370034
--- /dev/null
+++ b/js/src/tests/Intl/Date/toLocaleString_timeZone.js
@@ -0,0 +1,77 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const defaultLocale = "en-US";
+const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0);
+const defaultOptions = {};
+
+const tests = [
+ {
+ timeZone: "UTC",
+ result: "12/6/2012, 12:00:00 PM",
+ },
+ {
+ timeZone: "America/Los_Angeles",
+ result: "12/6/2012, 4:00:00 AM",
+ },
+ {
+ timeZone: "Europe/Berlin", locale: "de",
+ options: { timeZoneName: "short" },
+ result: "6.12.2012, 13:00:00 MEZ",
+ },
+ {
+ timeZone: "Europe/Paris", locale: "fr",
+ options: { timeZoneName: "long" },
+ result: "06/12/2012 à 13:00:00 heure normale d’Europe centrale",
+ },
+ {
+ timeZone: "Asia/Shanghai", locale: "zh-Hans-CN",
+ options: { timeZoneName: "long" },
+ result: "2012/12/6 中国标准时间 下午8:00:00",
+ },
+ {
+ timeZone: { toString: () => "Australia/Melbourne" }, locale: "en-AU",
+ result: "06/12/2012, 11:00:00 pm",
+ },
+];
+
+for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) {
+ let s = new Date(date).toLocaleString(locale, Object.assign({timeZone}, options));
+ assertEq(s, result);
+}
+
+
+// |undefined| or absent "timeZone" option selects the default time zone.
+{
+ let locale = defaultLocale;
+ let date = defaultDate;
+ let options = defaultOptions;
+
+ let absentTz = new Date(date).toLocaleString(locale, Object.assign({}, options));
+ let undefinedTz = new Date(date).toLocaleString(locale, Object.assign({timeZone: undefined}, options));
+ assertEq(undefinedTz, absentTz);
+}
+
+
+// RangeError is thrown for invalid time zone names.
+for (let timeZone of ["", "undefined", "UTC\0", "Vienna", "Africa", "America/NewYork"]) {
+ assertThrowsInstanceOf(() => {
+ new Date(defaultDate).toLocaleString(undefined, {timeZone});
+ }, RangeError);
+}
+
+// RangeError is thrown for these values, because ToString(<value>)
+// isn't a valid time zone name.
+for (let timeZone of [null, 0, 0.5, true, false, [], {}, function() {}]) {
+ assertThrowsInstanceOf(() => {
+ new Date(defaultDate).toLocaleString(undefined, {timeZone});
+ }, RangeError);
+}
+
+// ToString(<symbol>) throws TypeError.
+assertThrowsInstanceOf(() => {
+ new Date(defaultDate).toLocaleString(undefined, {timeZone: Symbol()});
+}, TypeError);
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/Intl/Date/toLocaleTimeString_timeZone.js b/js/src/tests/Intl/Date/toLocaleTimeString_timeZone.js
new file mode 100644
index 000000000..14799a287
--- /dev/null
+++ b/js/src/tests/Intl/Date/toLocaleTimeString_timeZone.js
@@ -0,0 +1,77 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const defaultLocale = "en-US";
+const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0);
+const defaultOptions = {};
+
+const tests = [
+ {
+ timeZone: "UTC",
+ result: "12:00:00 PM",
+ },
+ {
+ timeZone: "America/Los_Angeles",
+ result: "4:00:00 AM",
+ },
+ {
+ timeZone: "Europe/Berlin", locale: "de",
+ options: { timeZoneName: "short" },
+ result: "13:00:00 MEZ",
+ },
+ {
+ timeZone: "Europe/Paris", locale: "fr",
+ options: { timeZoneName: "long" },
+ result: "13:00:00 heure normale d’Europe centrale",
+ },
+ {
+ timeZone: "Asia/Shanghai", locale: "zh-Hans-CN",
+ options: { timeZoneName: "long" },
+ result: "中国标准时间 下午8:00:00",
+ },
+ {
+ timeZone: { toString: () => "Australia/Melbourne" }, locale: "en-AU",
+ result: "11:00:00 pm",
+ },
+];
+
+for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) {
+ let s = new Date(date).toLocaleTimeString(locale, Object.assign({timeZone}, options));
+ assertEq(s, result);
+}
+
+
+// |undefined| or absent "timeZone" option selects the default time zone.
+{
+ let locale = defaultLocale;
+ let date = defaultDate;
+ let options = defaultOptions;
+
+ let absentTz = new Date(date).toLocaleTimeString(locale, Object.assign({}, options));
+ let undefinedTz = new Date(date).toLocaleTimeString(locale, Object.assign({timeZone: undefined}, options));
+ assertEq(undefinedTz, absentTz);
+}
+
+
+// RangeError is thrown for invalid time zone names.
+for (let timeZone of ["", "undefined", "UTC\0", "Vienna", "Africa", "America/NewYork"]) {
+ assertThrowsInstanceOf(() => {
+ new Date(defaultDate).toLocaleTimeString(undefined, {timeZone});
+ }, RangeError);
+}
+
+// RangeError is thrown for these values, because ToString(<value>)
+// isn't a valid time zone name.
+for (let timeZone of [null, 0, 0.5, true, false, [], {}, function() {}]) {
+ assertThrowsInstanceOf(() => {
+ new Date(defaultDate).toLocaleTimeString(undefined, {timeZone});
+ }, RangeError);
+}
+
+// ToString(<symbol>) throws TypeError.
+assertThrowsInstanceOf(() => {
+ new Date(defaultDate).toLocaleTimeString(undefined, {timeZone: Symbol()});
+}, TypeError);
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/Intl/DateTimeFormat/browser.js b/js/src/tests/Intl/DateTimeFormat/browser.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/browser.js
diff --git a/js/src/tests/Intl/DateTimeFormat/format.js b/js/src/tests/Intl/DateTimeFormat/format.js
new file mode 100644
index 000000000..936a7dae3
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/format.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Tests the format function with a diverse set of locales and options.
+// Always use UTC to avoid dependencies on test environment.
+
+var format;
+var date = Date.UTC(2012, 11, 12, 3, 0, 0);
+var longFormatOptions = {timeZone: "UTC",
+ year: "numeric", month: "long", day: "numeric",
+ hour: "numeric", minute: "numeric", second: "numeric"};
+
+// Locale en-US; default options.
+format = new Intl.DateTimeFormat("en-us", {timeZone: "UTC"});
+assertEq(format.format(date), "12/12/2012");
+
+// Locale th-TH; default options.
+// Thailand uses the Buddhist calendar.
+format = new Intl.DateTimeFormat("th-th", {timeZone: "UTC"});
+assertEq(format.format(date), "12/12/2555");
+
+// Locale th-TH; long format, Thai digits.
+format = new Intl.DateTimeFormat("th-th-u-nu-thai", longFormatOptions);
+assertEq(format.format(date), "๑๒ ธันวาคม ๒๕๕๕ ๐๓:๐๐:๐๐");
+
+// Locale ja-JP; long format.
+format = new Intl.DateTimeFormat("ja-jp", longFormatOptions);
+assertEq(format.format(date), "2012年12月12日 3:00:00");
+
+// Locale ar-MA; long format, Islamic civilian calendar.
+format = new Intl.DateTimeFormat("ar-ma-u-ca-islamicc", longFormatOptions);
+assertEq(format.format(date), "28 محرم، 1434 03:00:00");
+
+reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/Intl/DateTimeFormat/formatToParts.js b/js/src/tests/Intl/DateTimeFormat/formatToParts.js
new file mode 100644
index 000000000..95831e67a
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/formatToParts.js
@@ -0,0 +1,170 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+// Tests the format function with a diverse set of locales and options.
+// Always use UTC to avoid dependencies on test environment.
+
+/*
+ * Return true if A is equal to B, where equality on arrays and objects
+ * means that they have the same set of enumerable properties, the values
+ * of each property are deep_equal, and their 'length' properties are
+ * equal. Equality on other types is ==.
+ */
+function deepEqual(a, b) {
+ if (typeof a !== typeof b)
+ return false;
+
+ if (a === null)
+ return b === null;
+
+ if (typeof a === 'object') {
+ // For every property of a, does b have that property with an equal value?
+ var props = {};
+ for (var prop in a) {
+ if (!deepEqual(a[prop], b[prop]))
+ return false;
+ props[prop] = true;
+ }
+
+ // Are all of b's properties present on a?
+ for (var prop in b)
+ if (!props[prop])
+ return false;
+
+ // length isn't enumerable, but we want to check it, too.
+ return a.length === b.length;
+ }
+
+ return Object.is(a, b);
+}
+
+function composeDate(parts) {
+ return parts.map(({value}) => value)
+ .reduce((string, part) => string + part);
+}
+
+var format;
+var date = Date.UTC(2012, 11, 17, 3, 0, 42);
+
+// Locale en-US; default options.
+format = new Intl.DateTimeFormat("en-us", {timeZone: "UTC"});
+assertEq(deepEqual(format.formatToParts(date), [
+ { type: 'month', value: '12' },
+ { type: 'literal', value: '/' },
+ { type: 'day', value: '17' },
+ { type: 'literal', value: '/' },
+ { type: 'year', value: '2012' }
+]), true);
+
+// Just date
+format = new Intl.DateTimeFormat("en-us", {
+ year: 'numeric',
+ month: 'numeric',
+ day: 'numeric',
+ timeZone: "UTC"});
+assertEq(deepEqual(format.formatToParts(date), [
+ { type: 'month', value: '12' },
+ { type: 'literal', value: '/' },
+ { type: 'day', value: '17' },
+ { type: 'literal', value: '/' },
+ { type: 'year', value: '2012' }
+]), true);
+assertEq(composeDate(format.formatToParts(date)), format.format(date));
+
+// Just time in hour24
+format = new Intl.DateTimeFormat("en-us", {
+ hour: 'numeric',
+ minute: 'numeric',
+ second: 'numeric',
+ hour12: false,
+ timeZone: "UTC"});
+assertEq(deepEqual(format.formatToParts(date), [
+ { type: 'hour', value: '03' },
+ { type: 'literal', value: ':' },
+ { type: 'minute', value: '00' },
+ { type: 'literal', value: ':' },
+ { type: 'second', value: '42' }
+]), true);
+assertEq(composeDate(format.formatToParts(date)), format.format(date));
+
+// Just time in hour12
+format = new Intl.DateTimeFormat("en-us", {
+ hour: 'numeric',
+ minute: 'numeric',
+ second: 'numeric',
+ hour12: true,
+ timeZone: "UTC"});
+assertEq(deepEqual(format.formatToParts(date), [
+ { type: 'hour', value: '3' },
+ { type: 'literal', value: ':' },
+ { type: 'minute', value: '00' },
+ { type: 'literal', value: ':' },
+ { type: 'second', value: '42' },
+ { type: 'literal', value: ' ' },
+ { type: 'dayPeriod', value: 'AM' }
+]), true);
+assertEq(composeDate(format.formatToParts(date)), format.format(date));
+
+// Just month.
+format = new Intl.DateTimeFormat("en-us", {
+ month: "narrow",
+ timeZone: "UTC"});
+assertEq(deepEqual(format.formatToParts(date), [
+ { type: 'month', value: 'D' }
+]), true);
+assertEq(composeDate(format.formatToParts(date)), format.format(date));
+
+// Just weekday.
+format = new Intl.DateTimeFormat("en-us", {
+ weekday: "narrow",
+ timeZone: "UTC"});
+assertEq(deepEqual(format.formatToParts(date), [
+ { type: 'weekday', value: 'M' }
+]), true);
+assertEq(composeDate(format.formatToParts(date)), format.format(date));
+
+// Year and era.
+format = new Intl.DateTimeFormat("en-us", {
+ year: "numeric",
+ era: "short",
+ timeZone: "UTC"});
+assertEq(deepEqual(format.formatToParts(date), [
+ { type: 'year', value: '2012' },
+ { type: 'literal', value: ' ' },
+ { type: 'era', value: 'AD' }
+]), true);
+assertEq(composeDate(format.formatToParts(date)), format.format(date));
+
+// Time and date
+format = new Intl.DateTimeFormat("en-us", {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'numeric',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ second: 'numeric',
+ hour12: true,
+ timeZone: "UTC"});
+assertEq(deepEqual(format.formatToParts(date), [
+ { type: 'weekday', value: 'Monday' },
+ { type: 'literal', value: ', ' },
+ { type: 'month', value: '12' },
+ { type: 'literal', value: '/' },
+ { type: 'day', value: '17' },
+ { type: 'literal', value: '/' },
+ { type: 'year', value: '2012' },
+ { type: 'literal', value: ', ' },
+ { type: 'hour', value: '3' },
+ { type: 'literal', value: ':' },
+ { type: 'minute', value: '00' },
+ { type: 'literal', value: ':' },
+ { type: 'second', value: '42' },
+ { type: 'literal', value: ' ' },
+ { type: 'dayPeriod', value: 'AM' }
+]), true);
+assertEq(composeDate(format.formatToParts(date)), format.format(date));
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/Intl/DateTimeFormat/format_timeZone.js b/js/src/tests/Intl/DateTimeFormat/format_timeZone.js
new file mode 100644
index 000000000..a4d4dd1c4
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/format_timeZone.js
@@ -0,0 +1,104 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const defaultLocale = "en-US";
+const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0);
+const defaultOptions = {
+ year: "numeric", month: "numeric", day: "numeric",
+ hour: "numeric", minute: "numeric", second: "numeric",
+};
+const longFormatOptions = Object.assign({}, defaultOptions, {
+ month: "long"
+});
+const tzNameFormatOptions = Object.assign({}, defaultOptions, {
+ timeZoneName: "short"
+});
+
+const tzMapper = [
+ x => x,
+ x => x.toUpperCase(),
+ x => x.toLowerCase(),
+];
+
+const tests = [
+ {
+ timeZone: "UTC",
+ result: "12/6/2012, 12:00:00 PM",
+ },
+ {
+ timeZone: "America/Los_Angeles",
+ result: "12/6/2012, 4:00:00 AM",
+ },
+ {
+ timeZone: "America/New_York",
+ options: tzNameFormatOptions,
+ result: "12/6/2012, 7:00:00 AM EST",
+ },
+ {
+ timeZone: "America/Caracas",
+ result: "12/6/2012, 7:30:00 AM",
+ },
+ {
+ timeZone: "Europe/London",
+ result: "12/6/2012, 12:00:00 PM",
+ },
+ {
+ timeZone: "Africa/Casablanca",
+ locale: "ar-MA-u-ca-islamicc", options: longFormatOptions,
+ result: "22 محرم، 1434 12:00:00",
+ },
+ {
+ timeZone: "Europe/Berlin",
+ locale: "de-DE", options: tzNameFormatOptions,
+ result: "6.12.2012, 13:00:00 MEZ",
+ },
+ {
+ timeZone: "Asia/Kathmandu",
+ result: "12/6/2012, 5:45:00 PM",
+ },
+ {
+ timeZone: "Asia/Bangkok",
+ locale: "th-th-u-nu-thai", options: longFormatOptions,
+ result: "๖ ธันวาคม ๒๕๕๕ ๑๙:๐๐:๐๐",
+ },
+ {
+ timeZone: "Asia/Tokyo",
+ locale: "ja-JP", options: longFormatOptions,
+ result: "2012年12月6日 21:00:00",
+ },
+ {
+ timeZone: "Australia/Lord_Howe",
+ result: "12/6/2012, 11:00:00 PM",
+ },
+ {
+ timeZone: "Australia/Lord_Howe",
+ date: Date.UTC(2012, 7-1, 6, 12, 0, 0),
+ result: "7/6/2012, 10:30:00 PM",
+ },
+ {
+ timeZone: "Pacific/Kiritimati",
+ date: Date.UTC(1978, 12-1, 6, 12, 0, 0),
+ result: "12/6/1978, 1:20:00 AM",
+ },
+ {
+ timeZone: "Africa/Monrovia",
+ date: Date.UTC(1971, 12-1, 6, 12, 0, 0),
+ result: "12/6/1971, 11:15:30 AM",
+ },
+ {
+ timeZone: "Asia/Riyadh",
+ date: Date.UTC(1946, 12-1, 6, 12, 0, 0),
+ result: "12/6/1946, 3:06:52 PM",
+ },
+];
+
+for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) {
+ for (let map of tzMapper) {
+ let dtf = new Intl.DateTimeFormat(locale, Object.assign({timeZone: map(timeZone)}, options));
+ assertEq(dtf.format(date), result);
+ assertEq(dtf.resolvedOptions().timeZone, timeZone);
+ }
+}
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/Intl/DateTimeFormat/shell.js b/js/src/tests/Intl/DateTimeFormat/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/shell.js
diff --git a/js/src/tests/Intl/DateTimeFormat/supportedLocalesOf.js b/js/src/tests/Intl/DateTimeFormat/supportedLocalesOf.js
new file mode 100644
index 000000000..b957f9cd6
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/supportedLocalesOf.js
@@ -0,0 +1,371 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl")||xulRuntime.shell)
+// -- test in browser only that ICU has locale data for all Mozilla languages
+
+/* 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/. */
+
+// This array contains the locales that ICU supports in
+// date and time formatting whose languages Mozilla localizes Firefox into.
+// Current as of ICU 50.1.2 and Firefox March 2013.
+var locales = [
+ "af",
+ "af-NA",
+ "af-ZA",
+ "ar",
+ "ar-001",
+ "ar-AE",
+ "ar-BH",
+ "ar-DJ",
+ "ar-DZ",
+ "ar-EG",
+ "ar-EH",
+ "ar-ER",
+ "ar-IL",
+ "ar-IQ",
+ "ar-JO",
+ "ar-KM",
+ "ar-KW",
+ "ar-LB",
+ "ar-LY",
+ "ar-MA",
+ "ar-MR",
+ "ar-OM",
+ "ar-PS",
+ "ar-QA",
+ "ar-SA",
+ "ar-SD",
+ "ar-SO",
+ "ar-SY",
+ "ar-TD",
+ "ar-TN",
+ "ar-YE",
+ "as",
+ "as-IN",
+ "be",
+ "be-BY",
+ "bg",
+ "bg-BG",
+ "bn",
+ "bn-BD",
+ "bn-IN",
+ "br",
+ "br-FR",
+ "bs",
+ "bs-Cyrl",
+ "bs-Cyrl-BA",
+ "bs-Latn",
+ "bs-Latn-BA",
+ "ca",
+ "ca-AD",
+ "ca-ES",
+ "cs",
+ "cs-CZ",
+ "cy",
+ "cy-GB",
+ "da",
+ "da-DK",
+ "de",
+ "de-AT",
+ "de-BE",
+ "de-CH",
+ "de-DE",
+ "de-LI",
+ "de-LU",
+ "el",
+ "el-CY",
+ "el-GR",
+ "en",
+ "en-150",
+ "en-AG",
+ "en-AS",
+ "en-AU",
+ "en-BB",
+ "en-BE",
+ "en-BM",
+ "en-BS",
+ "en-BW",
+ "en-BZ",
+ "en-CA",
+ "en-CM",
+ "en-DM",
+ "en-FJ",
+ "en-FM",
+ "en-GB",
+ "en-GD",
+ "en-GG",
+ "en-GH",
+ "en-GI",
+ "en-GM",
+ "en-GU",
+ "en-GY",
+ "en-HK",
+ "en-IE",
+ "en-IM",
+ "en-IN",
+ "en-JE",
+ "en-JM",
+ "en-KE",
+ "en-KI",
+ "en-KN",
+ "en-KY",
+ "en-LC",
+ "en-LR",
+ "en-LS",
+ "en-MG",
+ "en-MH",
+ "en-MP",
+ "en-MT",
+ "en-MU",
+ "en-MW",
+ "en-NA",
+ "en-NG",
+ "en-NZ",
+ "en-PG",
+ "en-PH",
+ "en-PK",
+ "en-PR",
+ "en-PW",
+ "en-SB",
+ "en-SC",
+ "en-SG",
+ "en-SL",
+ "en-SS",
+ "en-SZ",
+ "en-TC",
+ "en-TO",
+ "en-TT",
+ "en-TZ",
+ "en-UG",
+ "en-UM",
+ "en-US",
+ "en-US-POSIX",
+ "en-VC",
+ "en-VG",
+ "en-VI",
+ "en-VU",
+ "en-WS",
+ "en-ZA",
+ "en-ZM",
+ "en-ZW",
+ "eo",
+ "es",
+ "es-419",
+ "es-AR",
+ "es-BO",
+ "es-CL",
+ "es-CO",
+ "es-CR",
+ "es-CU",
+ "es-DO",
+ "es-EA",
+ "es-EC",
+ "es-ES",
+ "es-GQ",
+ "es-GT",
+ "es-HN",
+ "es-IC",
+ "es-MX",
+ "es-NI",
+ "es-PA",
+ "es-PE",
+ "es-PH",
+ "es-PR",
+ "es-PY",
+ "es-SV",
+ "es-US",
+ "es-UY",
+ "es-VE",
+ "et",
+ "et-EE",
+ "eu",
+ "eu-ES",
+ "fa",
+ "fa-AF",
+ "fa-IR",
+ "ff",
+ "ff-SN",
+ "fi",
+ "fi-FI",
+ "fr",
+ "fr-BE",
+ "fr-BF",
+ "fr-BI",
+ "fr-BJ",
+ "fr-BL",
+ "fr-CA",
+ "fr-CD",
+ "fr-CF",
+ "fr-CG",
+ "fr-CH",
+ "fr-CI",
+ "fr-CM",
+ "fr-DJ",
+ "fr-DZ",
+ "fr-FR",
+ "fr-GA",
+ "fr-GF",
+ "fr-GN",
+ "fr-GP",
+ "fr-GQ",
+ "fr-HT",
+ "fr-KM",
+ "fr-LU",
+ "fr-MA",
+ "fr-MC",
+ "fr-MF",
+ "fr-MG",
+ "fr-ML",
+ "fr-MQ",
+ "fr-MR",
+ "fr-MU",
+ "fr-NC",
+ "fr-NE",
+ "fr-PF",
+ "fr-RE",
+ "fr-RW",
+ "fr-SC",
+ "fr-SN",
+ "fr-SY",
+ "fr-TD",
+ "fr-TG",
+ "fr-TN",
+ "fr-VU",
+ "fr-YT",
+ "ga",
+ "ga-IE",
+ "gl",
+ "gl-ES",
+ "gu",
+ "gu-IN",
+ "he",
+ "he-IL",
+ "hi",
+ "hi-IN",
+ "hr",
+ "hr-BA",
+ "hr-HR",
+ "hu",
+ "hu-HU",
+ "hy",
+ "hy-AM",
+ "id",
+ "id-ID",
+ "is",
+ "is-IS",
+ "it",
+ "it-CH",
+ "it-IT",
+ "it-SM",
+ "ja",
+ "ja-JP",
+ "kk",
+ "kk-Cyrl",
+ "kk-Cyrl-KZ",
+ "km",
+ "km-KH",
+ "kn",
+ "kn-IN",
+ "ko",
+ "ko-KP",
+ "ko-KR",
+ "lt",
+ "lt-LT",
+ "lv",
+ "lv-LV",
+ "mk",
+ "mk-MK",
+ "ml",
+ "ml-IN",
+ "mr",
+ "mr-IN",
+ "nb",
+ "nb-NO",
+ "nl",
+ "nl-AW",
+ "nl-BE",
+ "nl-CW",
+ "nl-NL",
+ "nl-SR",
+ "nl-SX",
+ "nn",
+ "nn-NO",
+ "or",
+ "or-IN",
+ "pa",
+ "pa-Arab",
+ "pa-Arab-PK",
+ "pa-Guru",
+ "pa-Guru-IN",
+ "pl",
+ "pl-PL",
+ "pt",
+ "pt-AO",
+ "pt-BR",
+ "pt-CV",
+ "pt-GW",
+ "pt-MO",
+ "pt-MZ",
+ "pt-PT",
+ "pt-ST",
+ "pt-TL",
+ "rm",
+ "rm-CH",
+ "ro",
+ "ro-MD",
+ "ro-RO",
+ "ru",
+ "ru-BY",
+ "ru-KG",
+ "ru-KZ",
+ "ru-MD",
+ "ru-RU",
+ "ru-UA",
+ "si",
+ "si-LK",
+ "sk",
+ "sk-SK",
+ "sl",
+ "sl-SI",
+ "sq",
+ "sq-AL",
+ "sq-MK",
+ "sr",
+ "sr-Cyrl",
+ "sr-Cyrl-BA",
+ "sr-Cyrl-ME",
+ "sr-Cyrl-RS",
+ "sr-Latn",
+ "sr-Latn-BA",
+ "sr-Latn-ME",
+ "sr-Latn-RS",
+ "sv",
+ "sv-AX",
+ "sv-FI",
+ "sv-SE",
+ "te",
+ "te-IN",
+ "th",
+ "th-TH",
+ "tr",
+ "tr-CY",
+ "tr-TR",
+ "uk",
+ "uk-UA",
+ "vi",
+ "vi-VN",
+ "zh",
+ "zh-Hans",
+ "zh-Hans-CN",
+ "zh-Hans-HK",
+ "zh-Hans-MO",
+ "zh-Hans-SG",
+ "zh-Hant",
+ "zh-Hant-HK",
+ "zh-Hant-MO",
+ "zh-Hant-TW",
+];
+
+var count = Intl.DateTimeFormat.supportedLocalesOf(locales).length;
+
+reportCompare(locales.length, count, "Number of supported locales in Intl.DateTimeFormat");
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone.js b/js/src/tests/Intl/DateTimeFormat/timeZone.js
new file mode 100644
index 000000000..94d0fdc1f
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone.js
@@ -0,0 +1,152 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+const tzMapper = [
+ x => x,
+ x => x.toUpperCase(),
+ x => x.toLowerCase(),
+];
+
+
+const utcTimeZones = [
+ // Etc/UTC and Etc/GMT are normalized to UTC.
+ "Etc/UTC", "Etc/GMT",
+
+ // Links to Etc/GMT. (tzdata/etcetera)
+ "GMT", "Etc/Greenwich", "Etc/GMT-0", "Etc/GMT+0", "Etc/GMT0",
+
+ // Links to Etc/UTC. (tzdata/etcetera)
+ "Etc/Universal", "Etc/Zulu",
+
+ // Links to Etc/GMT. (tzdata/backward)
+ "GMT+0", "GMT-0", "GMT0", "Greenwich",
+
+ // Links to Etc/UTC. (tzdata/backward)
+ "UTC", "Universal", "Zulu",
+];
+
+for (let timeZone of utcTimeZones) {
+ for (let map of tzMapper) {
+ let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(timeZone)});
+ assertEq(dtf.resolvedOptions().timeZone, "UTC");
+ }
+}
+
+
+// ECMA-402 doesn't normalize Etc/UCT to UTC.
+const uctTimeZones = [
+ "Etc/UCT", "UCT",
+];
+
+for (let timeZone of uctTimeZones) {
+ for (let map of tzMapper) {
+ let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(timeZone)});
+ assertEq(dtf.resolvedOptions().timeZone, "Etc/UCT");
+ }
+}
+
+
+const invalidTimeZones = [
+ "", "null", "undefined", "UTC\0",
+
+ // ICU time zone name for invalid time zones.
+ "Etc/Unknown",
+
+ // ICU custom time zones.
+ "GMT-1", "GMT+1", "GMT-10", "GMT+10",
+ "GMT-10:00", "GMT+10:00",
+ "GMT-1000", "GMT+1000",
+
+ // Legacy ICU time zones.
+ "ACT", "AET", "AGT", "ART", "AST", "BET", "BST", "CAT", "CNT", "CST",
+ "CTT", "EAT", "ECT", "IET", "IST", "JST", "MIT", "NET", "NST", "PLT",
+ "PNT", "PRT", "PST", "SST", "VST",
+
+ // Deprecated IANA time zones.
+ "SystemV/AST4ADT", "SystemV/EST5EDT", "SystemV/CST6CDT", "SystemV/MST7MDT",
+ "SystemV/PST8PDT", "SystemV/YST9YDT", "SystemV/AST4", "SystemV/EST5",
+ "SystemV/CST6", "SystemV/MST7", "SystemV/PST8", "SystemV/YST9", "SystemV/HST10",
+];
+
+for (let timeZone of invalidTimeZones) {
+ for (let map of tzMapper) {
+ assertThrowsInstanceOf(() => {
+ new Intl.DateTimeFormat(undefined, {timeZone: map(timeZone)});
+ }, RangeError);
+ }
+}
+
+
+// GMT[+-]hh is invalid, but Etc/GMT[+-]hh is a valid IANA time zone.
+for (let gmtOffset = -14; gmtOffset <= 12; ++gmtOffset) {
+ // Skip Etc/GMT0.
+ if (gmtOffset === 0)
+ continue;
+
+ let timeZone = `Etc/GMT${gmtOffset > 0 ? "+" : ""}${gmtOffset}`;
+ for (let map of tzMapper) {
+ let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(timeZone)});
+ assertEq(dtf.resolvedOptions().timeZone, timeZone);
+ }
+}
+
+
+const invalidEtcGMTNames = [
+ // Out of bounds GMT offset.
+ "Etc/GMT-15", "Etc/GMT+13",
+
+ // Etc/GMT[+-]hh:mm isn't a IANA time zone name.
+ "Etc/GMT-10:00", "Etc/GMT+10:00",
+ "Etc/GMT-1000", "Etc/GMT+1000",
+];
+
+for (let timeZone of invalidEtcGMTNames) {
+ for (let map of tzMapper) {
+ assertThrowsInstanceOf(() => {
+ new Intl.DateTimeFormat(undefined, {timeZone: map(timeZone)});
+ }, RangeError);
+ }
+}
+
+
+// RangeError is thrown for primitive values, because ToString(<primitive>)
+// isn't a valid time zone name.
+for (let nonStrings of [null, 0, 0.5, true, false]) {
+ assertThrowsInstanceOf(() => {
+ new Intl.DateTimeFormat(undefined, {timeZone: nonStrings});
+ }, RangeError);
+}
+
+// ToString(<symbol>) throws TypeError.
+assertThrowsInstanceOf(() => {
+ new Intl.DateTimeFormat(undefined, {timeZone: Symbol()});
+}, TypeError);
+
+// |undefined| or absent "timeZone" option selects the default time zone.
+{
+ let {timeZone: tzAbsent} = new Intl.DateTimeFormat(undefined, {}).resolvedOptions();
+ let {timeZone: tzUndefined} = new Intl.DateTimeFormat(undefined, {timeZone: undefined}).resolvedOptions();
+
+ assertEq(typeof tzAbsent, "string");
+ assertEq(typeof tzUndefined, "string");
+ assertEq(tzUndefined, tzAbsent);
+
+ // The default time zone isn't a link name.
+ let {timeZone: tzDefault} = new Intl.DateTimeFormat(undefined, {timeZone: tzAbsent}).resolvedOptions();
+ assertEq(tzDefault, tzAbsent);
+}
+
+// Objects are converted through ToString().
+{
+ let timeZone = "Europe/Warsaw";
+ let obj = {
+ toString() {
+ return timeZone;
+ }
+ };
+ let dtf = new Intl.DateTimeFormat(undefined, {timeZone: obj});
+ assertEq(dtf.resolvedOptions().timeZone, timeZone);
+}
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js
new file mode 100644
index 000000000..aeb1fc043
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js
@@ -0,0 +1,134 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+// Generated by make_intl_data.py. DO NOT EDIT.
+// tzdata version = 2017c
+
+const tzMapper = [
+ x => x,
+ x => x.toUpperCase(),
+ x => x.toLowerCase(),
+];
+
+// Link names derived from IANA Time Zone Database, backward file.
+const links = {
+ "Africa/Asmera": "Africa/Asmara",
+ "America/Atka": "America/Adak",
+ "America/Buenos_Aires": "America/Argentina/Buenos_Aires",
+ "America/Catamarca": "America/Argentina/Catamarca",
+ "America/Cordoba": "America/Argentina/Cordoba",
+ "America/Fort_Wayne": "America/Indiana/Indianapolis",
+ "America/Indianapolis": "America/Indiana/Indianapolis",
+ "America/Jujuy": "America/Argentina/Jujuy",
+ "America/Knox_IN": "America/Indiana/Knox",
+ "America/Louisville": "America/Kentucky/Louisville",
+ "America/Mendoza": "America/Argentina/Mendoza",
+ "America/Porto_Acre": "America/Rio_Branco",
+ "America/Santa_Isabel": "America/Tijuana",
+ "America/Shiprock": "America/Denver",
+ "America/Virgin": "America/Port_of_Spain",
+ "Antarctica/South_Pole": "Antarctica/McMurdo",
+ "Asia/Ashkhabad": "Asia/Ashgabat",
+ "Asia/Calcutta": "Asia/Kolkata",
+ "Asia/Chungking": "Asia/Chongqing",
+ "Asia/Dacca": "Asia/Dhaka",
+ "Asia/Katmandu": "Asia/Kathmandu",
+ "Asia/Macao": "Asia/Macau",
+ "Asia/Rangoon": "Asia/Yangon",
+ "Asia/Saigon": "Asia/Ho_Chi_Minh",
+ "Asia/Thimbu": "Asia/Thimphu",
+ "Asia/Ujung_Pandang": "Asia/Makassar",
+ "Asia/Ulan_Bator": "Asia/Ulaanbaatar",
+ "Atlantic/Faeroe": "Atlantic/Faroe",
+ "Australia/ACT": "Australia/Sydney",
+ "Australia/Canberra": "Australia/Sydney",
+ "Australia/LHI": "Australia/Lord_Howe",
+ "Australia/NSW": "Australia/Sydney",
+ "Australia/North": "Australia/Darwin",
+ "Australia/Queensland": "Australia/Brisbane",
+ "Australia/South": "Australia/Adelaide",
+ "Australia/Tasmania": "Australia/Hobart",
+ "Australia/Victoria": "Australia/Melbourne",
+ "Australia/West": "Australia/Perth",
+ "Australia/Yancowinna": "Australia/Broken_Hill",
+ "Brazil/Acre": "America/Rio_Branco",
+ "Brazil/DeNoronha": "America/Noronha",
+ "Brazil/East": "America/Sao_Paulo",
+ "Brazil/West": "America/Manaus",
+ "Canada/Atlantic": "America/Halifax",
+ "Canada/Central": "America/Winnipeg",
+ "Canada/Eastern": "America/Toronto",
+ "Canada/Mountain": "America/Edmonton",
+ "Canada/Newfoundland": "America/St_Johns",
+ "Canada/Pacific": "America/Vancouver",
+ "Canada/Saskatchewan": "America/Regina",
+ "Canada/Yukon": "America/Whitehorse",
+ "Chile/Continental": "America/Santiago",
+ "Chile/EasterIsland": "Pacific/Easter",
+ "Cuba": "America/Havana",
+ "Egypt": "Africa/Cairo",
+ "Eire": "Europe/Dublin",
+ "GB": "Europe/London",
+ "GB-Eire": "Europe/London",
+ "GMT+0": "Etc/GMT",
+ "GMT-0": "Etc/GMT",
+ "GMT0": "Etc/GMT",
+ "Greenwich": "Etc/GMT",
+ "Hongkong": "Asia/Hong_Kong",
+ "Iceland": "Atlantic/Reykjavik",
+ "Iran": "Asia/Tehran",
+ "Israel": "Asia/Jerusalem",
+ "Jamaica": "America/Jamaica",
+ "Japan": "Asia/Tokyo",
+ "Kwajalein": "Pacific/Kwajalein",
+ "Libya": "Africa/Tripoli",
+ "Mexico/BajaNorte": "America/Tijuana",
+ "Mexico/BajaSur": "America/Mazatlan",
+ "Mexico/General": "America/Mexico_City",
+ "NZ": "Pacific/Auckland",
+ "NZ-CHAT": "Pacific/Chatham",
+ "Navajo": "America/Denver",
+ "PRC": "Asia/Shanghai",
+ "Pacific/Ponape": "Pacific/Pohnpei",
+ "Pacific/Samoa": "Pacific/Pago_Pago",
+ "Pacific/Truk": "Pacific/Chuuk",
+ "Pacific/Yap": "Pacific/Chuuk",
+ "Poland": "Europe/Warsaw",
+ "Portugal": "Europe/Lisbon",
+ "ROC": "Asia/Taipei",
+ "ROK": "Asia/Seoul",
+ "Singapore": "Asia/Singapore",
+ "Turkey": "Europe/Istanbul",
+ "UCT": "Etc/UCT",
+ "US/Alaska": "America/Anchorage",
+ "US/Aleutian": "America/Adak",
+ "US/Arizona": "America/Phoenix",
+ "US/Central": "America/Chicago",
+ "US/East-Indiana": "America/Indiana/Indianapolis",
+ "US/Eastern": "America/New_York",
+ "US/Hawaii": "Pacific/Honolulu",
+ "US/Indiana-Starke": "America/Indiana/Knox",
+ "US/Michigan": "America/Detroit",
+ "US/Mountain": "America/Denver",
+ "US/Pacific": "America/Los_Angeles",
+ "US/Samoa": "Pacific/Pago_Pago",
+ "UTC": "Etc/UTC",
+ "Universal": "Etc/UTC",
+ "W-SU": "Europe/Moscow",
+ "Zulu": "Etc/UTC",
+};
+
+for (let [linkName, target] of Object.entries(links)) {
+ if (target === "Etc/UTC" || target === "Etc/GMT")
+ target = "UTC";
+
+ for (let map of tzMapper) {
+ let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(linkName)});
+ let resolvedTimeZone = dtf.resolvedOptions().timeZone;
+ assertEq(resolvedTimeZone, target, `${linkName} -> ${target}`);
+ }
+}
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
+
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js
new file mode 100644
index 000000000..b9d957a82
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js
@@ -0,0 +1,115 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+// Generated by make_intl_data.py. DO NOT EDIT.
+// tzdata version = 2017c
+
+const tzMapper = [
+ x => x,
+ x => x.toUpperCase(),
+ x => x.toLowerCase(),
+];
+
+// This file was generated with historical, pre-1970 backzone information
+// respected. Therefore, every zone key listed below is its own Zone, not
+// a Link to a modern-day target as IANA ignoring backzones would say.
+
+// Backzone zones derived from IANA Time Zone Database.
+const links = {
+ "Africa/Addis_Ababa": "Africa/Addis_Ababa",
+ "Africa/Asmara": "Africa/Asmara",
+ "Africa/Bamako": "Africa/Bamako",
+ "Africa/Bangui": "Africa/Bangui",
+ "Africa/Banjul": "Africa/Banjul",
+ "Africa/Blantyre": "Africa/Blantyre",
+ "Africa/Brazzaville": "Africa/Brazzaville",
+ "Africa/Bujumbura": "Africa/Bujumbura",
+ "Africa/Conakry": "Africa/Conakry",
+ "Africa/Dakar": "Africa/Dakar",
+ "Africa/Dar_es_Salaam": "Africa/Dar_es_Salaam",
+ "Africa/Djibouti": "Africa/Djibouti",
+ "Africa/Douala": "Africa/Douala",
+ "Africa/Freetown": "Africa/Freetown",
+ "Africa/Gaborone": "Africa/Gaborone",
+ "Africa/Harare": "Africa/Harare",
+ "Africa/Kampala": "Africa/Kampala",
+ "Africa/Kigali": "Africa/Kigali",
+ "Africa/Kinshasa": "Africa/Kinshasa",
+ "Africa/Libreville": "Africa/Libreville",
+ "Africa/Lome": "Africa/Lome",
+ "Africa/Luanda": "Africa/Luanda",
+ "Africa/Lubumbashi": "Africa/Lubumbashi",
+ "Africa/Lusaka": "Africa/Lusaka",
+ "Africa/Malabo": "Africa/Malabo",
+ "Africa/Maseru": "Africa/Maseru",
+ "Africa/Mbabane": "Africa/Mbabane",
+ "Africa/Mogadishu": "Africa/Mogadishu",
+ "Africa/Niamey": "Africa/Niamey",
+ "Africa/Nouakchott": "Africa/Nouakchott",
+ "Africa/Ouagadougou": "Africa/Ouagadougou",
+ "Africa/Porto-Novo": "Africa/Porto-Novo",
+ "Africa/Sao_Tome": "Africa/Sao_Tome",
+ "Africa/Timbuktu": "Africa/Timbuktu",
+ "America/Anguilla": "America/Anguilla",
+ "America/Antigua": "America/Antigua",
+ "America/Argentina/ComodRivadavia": "America/Argentina/ComodRivadavia",
+ "America/Aruba": "America/Aruba",
+ "America/Cayman": "America/Cayman",
+ "America/Coral_Harbour": "America/Coral_Harbour",
+ "America/Dominica": "America/Dominica",
+ "America/Ensenada": "America/Ensenada",
+ "America/Grenada": "America/Grenada",
+ "America/Guadeloupe": "America/Guadeloupe",
+ "America/Montreal": "America/Montreal",
+ "America/Montserrat": "America/Montserrat",
+ "America/Rosario": "America/Rosario",
+ "America/St_Kitts": "America/St_Kitts",
+ "America/St_Lucia": "America/St_Lucia",
+ "America/St_Thomas": "America/St_Thomas",
+ "America/St_Vincent": "America/St_Vincent",
+ "America/Tortola": "America/Tortola",
+ "Antarctica/McMurdo": "Antarctica/McMurdo",
+ "Asia/Aden": "Asia/Aden",
+ "Asia/Bahrain": "Asia/Bahrain",
+ "Asia/Chongqing": "Asia/Chongqing",
+ "Asia/Harbin": "Asia/Harbin",
+ "Asia/Kashgar": "Asia/Kashgar",
+ "Asia/Kuwait": "Asia/Kuwait",
+ "Asia/Muscat": "Asia/Muscat",
+ "Asia/Phnom_Penh": "Asia/Phnom_Penh",
+ "Asia/Tel_Aviv": "Asia/Tel_Aviv",
+ "Asia/Vientiane": "Asia/Vientiane",
+ "Atlantic/Jan_Mayen": "Atlantic/Jan_Mayen",
+ "Atlantic/St_Helena": "Atlantic/St_Helena",
+ "Europe/Belfast": "Europe/Belfast",
+ "Europe/Guernsey": "Europe/Guernsey",
+ "Europe/Isle_of_Man": "Europe/Isle_of_Man",
+ "Europe/Jersey": "Europe/Jersey",
+ "Europe/Ljubljana": "Europe/Ljubljana",
+ "Europe/Sarajevo": "Europe/Sarajevo",
+ "Europe/Skopje": "Europe/Skopje",
+ "Europe/Tiraspol": "Europe/Tiraspol",
+ "Europe/Vaduz": "Europe/Vaduz",
+ "Europe/Zagreb": "Europe/Zagreb",
+ "Indian/Antananarivo": "Indian/Antananarivo",
+ "Indian/Comoro": "Indian/Comoro",
+ "Indian/Mayotte": "Indian/Mayotte",
+ "Pacific/Johnston": "Pacific/Johnston",
+ "Pacific/Midway": "Pacific/Midway",
+ "Pacific/Saipan": "Pacific/Saipan",
+};
+
+for (let [linkName, target] of Object.entries(links)) {
+ if (target === "Etc/UTC" || target === "Etc/GMT")
+ target = "UTC";
+
+ for (let map of tzMapper) {
+ let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(linkName)});
+ let resolvedTimeZone = dtf.resolvedOptions().timeZone;
+ assertEq(resolvedTimeZone, target, `${linkName} -> ${target}`);
+ }
+}
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
+
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js
new file mode 100644
index 000000000..7a358e997
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+// Generated by make_intl_data.py. DO NOT EDIT.
+// tzdata version = 2017c
+
+const tzMapper = [
+ x => x,
+ x => x.toUpperCase(),
+ x => x.toLowerCase(),
+];
+
+// This file was generated with historical, pre-1970 backzone information
+// respected. Therefore, every zone key listed below points to a target
+// in the backzone file and not to its modern-day target as IANA ignoring
+// backzones would say.
+
+// Backzone links derived from IANA Time Zone Database.
+const links = {
+ "Africa/Asmera": "Africa/Asmara",
+ "Antarctica/South_Pole": "Antarctica/McMurdo",
+ "Asia/Chungking": "Asia/Chongqing",
+};
+
+for (let [linkName, target] of Object.entries(links)) {
+ if (target === "Etc/UTC" || target === "Etc/GMT")
+ target = "UTC";
+
+ for (let map of tzMapper) {
+ let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(linkName)});
+ let resolvedTimeZone = dtf.resolvedOptions().timeZone;
+ assertEq(resolvedTimeZone, target, `${linkName} -> ${target}`);
+ }
+}
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
+
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js
new file mode 100644
index 000000000..9515cb394
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+// Generated by make_intl_data.py. DO NOT EDIT.
+// tzdata version = 2017c
+
+const tzMapper = [
+ x => x,
+ x => x.toUpperCase(),
+ x => x.toLowerCase(),
+];
+
+// Link names derived from IANA Time Zone Database, excluding backward file.
+const links = {
+ "America/Kralendijk": "America/Curacao",
+ "America/Lower_Princes": "America/Curacao",
+ "America/Marigot": "America/Port_of_Spain",
+ "America/St_Barthelemy": "America/Port_of_Spain",
+ "Arctic/Longyearbyen": "Europe/Oslo",
+ "Asia/Istanbul": "Europe/Istanbul",
+ "Etc/GMT+0": "Etc/GMT",
+ "Etc/GMT-0": "Etc/GMT",
+ "Etc/GMT0": "Etc/GMT",
+ "Etc/Greenwich": "Etc/GMT",
+ "Etc/Universal": "Etc/UTC",
+ "Etc/Zulu": "Etc/UTC",
+ "Europe/Bratislava": "Europe/Prague",
+ "Europe/Busingen": "Europe/Zurich",
+ "Europe/Mariehamn": "Europe/Helsinki",
+ "Europe/Nicosia": "Asia/Nicosia",
+ "Europe/Podgorica": "Europe/Belgrade",
+ "Europe/San_Marino": "Europe/Rome",
+ "Europe/Vatican": "Europe/Rome",
+ "GMT": "Etc/GMT",
+ "US/Pacific-New": "America/Los_Angeles",
+};
+
+for (let [linkName, target] of Object.entries(links)) {
+ if (target === "Etc/UTC" || target === "Etc/GMT")
+ target = "UTC";
+
+ for (let map of tzMapper) {
+ let dtf = new Intl.DateTimeFormat(undefined, {timeZone: map(linkName)});
+ let resolvedTimeZone = dtf.resolvedOptions().timeZone;
+ assertEq(resolvedTimeZone, target, `${linkName} -> ${target}`);
+ }
+}
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
+
diff --git a/js/src/tests/Intl/NumberFormat/StringBuffer.js b/js/src/tests/Intl/NumberFormat/StringBuffer.js
new file mode 100644
index 000000000..ab72e21d4
--- /dev/null
+++ b/js/src/tests/Intl/NumberFormat/StringBuffer.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// The implementation of the format function uses the C++ StringBuffer class,
+// which changes its storage model at 32 characters, and uses it in a way which
+// also means that there's no room for null-termination at this limit.
+// This test makes sure that none of this affects the output.
+
+var format = new Intl.NumberFormat("it-IT", {minimumFractionDigits: 1});
+
+assertEq(format.format(1123123123123123123123.1), "1.123.123.123.123.120.000.000,0");
+assertEq(format.format(12123123123123123123123.1), "12.123.123.123.123.100.000.000,0");
+assertEq(format.format(123123123123123123123123.1), "123.123.123.123.123.000.000.000,0");
+
+reportCompare(0, 0, "ok");
diff --git a/js/src/tests/Intl/NumberFormat/browser.js b/js/src/tests/Intl/NumberFormat/browser.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/NumberFormat/browser.js
diff --git a/js/src/tests/Intl/NumberFormat/duplicate-singleton-variant.js b/js/src/tests/Intl/NumberFormat/duplicate-singleton-variant.js
new file mode 100644
index 000000000..61fdf3b7d
--- /dev/null
+++ b/js/src/tests/Intl/NumberFormat/duplicate-singleton-variant.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+/* 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/. */
+
+// Check for duplicate variants and singletons case-insensitively, but don't
+// check in privateuse components.
+
+function checkInvalidLocale(locale)
+{
+ try
+ {
+ new Intl.NumberFormat(locale);
+ throw new Error("didn't throw");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof RangeError, true,
+ "expected RangeError for locale '" + locale + "', got " + e);
+ }
+}
+
+var badLocales =
+ [
+ "en-u-foo-U-foo",
+ "en-tester-Tester",
+ "en-tesTER-TESter",
+ "de-DE-u-kn-true-U-kn-true",
+ "ar-u-foo-q-bar-u-baz",
+ "ar-z-moo-u-foo-q-bar-z-eit-u-baz",
+ ];
+
+for (var locale of badLocales)
+ checkInvalidLocale(locale);
+
+// Fully-privateuse locales are okay.
+for (var locale of badLocales)
+ new Intl.NumberFormat("x-" + locale).format(5);
+
+// Locales with trailing privateuse also okay.
+for (var locale of badLocales)
+{
+ new Intl.NumberFormat("en-x-" + locale).format(5);
+ new Intl.NumberFormat("en-u-foo-x-u-" + locale).format(5);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/Intl/NumberFormat/format-as-code-or-name.js b/js/src/tests/Intl/NumberFormat/format-as-code-or-name.js
new file mode 100644
index 000000000..ba257ecd7
--- /dev/null
+++ b/js/src/tests/Intl/NumberFormat/format-as-code-or-name.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1093421;
+var summary =
+ "new Intl.NumberFormat(..., { style: 'currency', currency: '...', " +
+ "currencyDisplay: 'name' or 'code' }) should have behavior other than " +
+ "throwing";
+
+print(BUGNUMBER + ": " + summary);
+
+//-----------------------------------------------------------------------------
+
+// Test that currencyDisplay: "code" behaves correctly and doesn't throw.
+
+var usdCodeOptions =
+ {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "code",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var usDollarsCode = new Intl.NumberFormat("en-US", usdCodeOptions);
+assertEq(/USD/.test(usDollarsCode.format(25)), true);
+
+// ISO 4217 currency codes are formed from an ISO 3166-1 alpha-2 country code
+// followed by a third letter. ISO 3166 guarantees that no country code
+// starting with "X" will ever be assigned. Stepping carefully around a few
+// 4217-designated special "currencies", XQQ will never have a representation.
+// Thus, yes: this really is specified to work, as unrecognized or unsupported
+// codes pass into the string unmodified.
+var xqqCodeOptions =
+ {
+ style: "currency",
+ currency: "XQQ",
+ currencyDisplay: "code",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var xqqMoneyCode = new Intl.NumberFormat("en-US", xqqCodeOptions);
+assertEq(/XQQ/.test(xqqMoneyCode.format(25)), true);
+
+// Test that currencyDisplay: "name" behaves without throwing. (Unlike the two
+// above tests, the results here aren't guaranteed as the name is
+// implementation-defined.)
+var usdNameOptions =
+ {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "name",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var usDollarsName = new Intl.NumberFormat("en-US", usdNameOptions);
+assertEq(usDollarsName.format(25), "25 US dollars");
+
+// But if the implementation doesn't recognize the currency, the provided code
+// is used in place of a proper name, unmolested.
+var xqqNameOptions =
+ {
+ style: "currency",
+ currency: "XQQ",
+ currencyDisplay: "name",
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ };
+var xqqMoneyName = new Intl.NumberFormat("en-US", xqqNameOptions);
+assertEq(/XQQ/.test(xqqMoneyName.format(25)), true);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/Intl/NumberFormat/format.js b/js/src/tests/Intl/NumberFormat/format.js
new file mode 100644
index 000000000..1f1dba87a
--- /dev/null
+++ b/js/src/tests/Intl/NumberFormat/format.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Tests the format function with a diverse set of locales and options.
+
+var format;
+
+// Locale en-US; default options.
+format = new Intl.NumberFormat("en-us");
+assertEq(format.format(0), "0");
+assertEq(format.format(-1), "-1");
+assertEq(format.format(123456789.123456789), "123,456,789.123");
+
+// Locale en-US; currency USD.
+// The US dollar uses two fractional digits, and negative values are commonly
+// parenthesized.
+format = new Intl.NumberFormat("en-us", {style: "currency", currency: "USD"});
+assertEq(format.format(0), "$0.00");
+assertEq(format.format(-1), "-$1.00");
+assertEq(format.format(123456789.123456789), "$123,456,789.12");
+
+// Locale ja-JP; currency JPY.
+// The Japanese yen has no subunit in real life.
+format = new Intl.NumberFormat("ja-jp", {style: "currency", currency: "JPY"});
+assertEq(format.format(0), "¥0");
+assertEq(format.format(-1), "-¥1");
+assertEq(format.format(123456789.123456789), "¥123,456,789");
+
+// Locale ar-JO; currency JOD.
+// The Jordanian Dinar divides into 1000 fils. Jordan uses (real) Arabic digits.
+format = new Intl.NumberFormat("ar-jo", {style: "currency", currency: "JOD"});
+assertEq(format.format(0), "٠٫٠٠٠ د.أ.‏");
+assertEq(format.format(-1), "؜-١٫٠٠٠ د.أ.‏");
+assertEq(format.format(123456789.123456789), "١٢٣٬٤٥٦٬٧٨٩٫١٢٣ د.أ.‏");
+
+// Locale th-TH; Thai digits, percent, two significant digits.
+format = new Intl.NumberFormat("th-th-u-nu-thai",
+ {style: "percent",
+ minimumSignificantDigits: 2,
+ maximumSignificantDigits: 2});
+assertEq(format.format(0), "๐.๐%");
+assertEq(format.format(-0.01), "-๑.๐%");
+assertEq(format.format(1.10), "๑๑๐%");
+
+reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/Intl/NumberFormat/remove-unicode-extensions.js b/js/src/tests/Intl/NumberFormat/remove-unicode-extensions.js
new file mode 100644
index 000000000..4d5d249ad
--- /dev/null
+++ b/js/src/tests/Intl/NumberFormat/remove-unicode-extensions.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+
+/* 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/. */
+
+// Locale processing is supposed to internally remove any Unicode extension
+// sequences in the locale. Test that various weird testcases invoking
+// algorithmic edge cases don't assert or throw exceptions.
+
+var weirdCases =
+ [
+ "x-u-foo",
+ "en-x-u-foo",
+ "en-a-bar-x-u-foo",
+ "en-x-u-foo-a-bar",
+ "en-a-bar-u-baz-x-u-foo",
+ ];
+
+for (var locale of weirdCases)
+ Intl.NumberFormat(locale).format(5);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/Intl/NumberFormat/shell.js b/js/src/tests/Intl/NumberFormat/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/NumberFormat/shell.js
diff --git a/js/src/tests/Intl/NumberFormat/significantDigitsOfZero.js b/js/src/tests/Intl/NumberFormat/significantDigitsOfZero.js
new file mode 100644
index 000000000..f2cd77cda
--- /dev/null
+++ b/js/src/tests/Intl/NumberFormat/significantDigitsOfZero.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+// -- test that NumberFormat correctly formats 0 with various numbers of significant digits
+
+/* 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/. */
+
+var testData = [
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 1, expected: "0"},
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 2, expected: "0"},
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 3, expected: "0"},
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 4, expected: "0"},
+ {minimumSignificantDigits: 1, maximumSignificantDigits: 5, expected: "0"},
+ {minimumSignificantDigits: 2, maximumSignificantDigits: 2, expected: "0.0"},
+ {minimumSignificantDigits: 2, maximumSignificantDigits: 3, expected: "0.0"},
+ {minimumSignificantDigits: 2, maximumSignificantDigits: 4, expected: "0.0"},
+ {minimumSignificantDigits: 2, maximumSignificantDigits: 5, expected: "0.0"},
+ {minimumSignificantDigits: 3, maximumSignificantDigits: 3, expected: "0.00"},
+ {minimumSignificantDigits: 3, maximumSignificantDigits: 4, expected: "0.00"},
+ {minimumSignificantDigits: 3, maximumSignificantDigits: 5, expected: "0.00"},
+];
+
+for (var i = 0; i < testData.length; i++) {
+ var min = testData[i].minimumSignificantDigits;
+ var max = testData[i].maximumSignificantDigits;
+ var options = {minimumSignificantDigits: min, maximumSignificantDigits: max};
+ var format = new Intl.NumberFormat("en-US", options);
+ assertEq(format.format(0), testData[i].expected,
+ "Wrong formatted string for 0 with " +
+ "minimumSignificantDigits " + min +
+ ", maximumSignificantDigits " + max +
+ ": expected \"" + expected +
+ "\", actual \"" + actual + "\"");
+}
+
+reportCompare(true, true);
diff --git a/js/src/tests/Intl/NumberFormat/supportedLocalesOf.js b/js/src/tests/Intl/NumberFormat/supportedLocalesOf.js
new file mode 100644
index 000000000..5ba4470a9
--- /dev/null
+++ b/js/src/tests/Intl/NumberFormat/supportedLocalesOf.js
@@ -0,0 +1,371 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl")||xulRuntime.shell)
+// -- test in browser only that ICU has locale data for all Mozilla languages
+
+/* 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/. */
+
+// This array contains the locales that ICU supports in
+// number formatting whose languages Mozilla localizes Firefox into.
+// Current as of ICU 50.1.2 and Firefox March 2013.
+var locales = [
+ "af",
+ "af-NA",
+ "af-ZA",
+ "ar",
+ "ar-001",
+ "ar-AE",
+ "ar-BH",
+ "ar-DJ",
+ "ar-DZ",
+ "ar-EG",
+ "ar-EH",
+ "ar-ER",
+ "ar-IL",
+ "ar-IQ",
+ "ar-JO",
+ "ar-KM",
+ "ar-KW",
+ "ar-LB",
+ "ar-LY",
+ "ar-MA",
+ "ar-MR",
+ "ar-OM",
+ "ar-PS",
+ "ar-QA",
+ "ar-SA",
+ "ar-SD",
+ "ar-SO",
+ "ar-SY",
+ "ar-TD",
+ "ar-TN",
+ "ar-YE",
+ "as",
+ "as-IN",
+ "be",
+ "be-BY",
+ "bg",
+ "bg-BG",
+ "bn",
+ "bn-BD",
+ "bn-IN",
+ "br",
+ "br-FR",
+ "bs",
+ "bs-Cyrl",
+ "bs-Cyrl-BA",
+ "bs-Latn",
+ "bs-Latn-BA",
+ "ca",
+ "ca-AD",
+ "ca-ES",
+ "cs",
+ "cs-CZ",
+ "cy",
+ "cy-GB",
+ "da",
+ "da-DK",
+ "de",
+ "de-AT",
+ "de-BE",
+ "de-CH",
+ "de-DE",
+ "de-LI",
+ "de-LU",
+ "el",
+ "el-CY",
+ "el-GR",
+ "en",
+ "en-150",
+ "en-AG",
+ "en-AS",
+ "en-AU",
+ "en-BB",
+ "en-BE",
+ "en-BM",
+ "en-BS",
+ "en-BW",
+ "en-BZ",
+ "en-CA",
+ "en-CM",
+ "en-DM",
+ "en-FJ",
+ "en-FM",
+ "en-GB",
+ "en-GD",
+ "en-GG",
+ "en-GH",
+ "en-GI",
+ "en-GM",
+ "en-GU",
+ "en-GY",
+ "en-HK",
+ "en-IE",
+ "en-IM",
+ "en-IN",
+ "en-JE",
+ "en-JM",
+ "en-KE",
+ "en-KI",
+ "en-KN",
+ "en-KY",
+ "en-LC",
+ "en-LR",
+ "en-LS",
+ "en-MG",
+ "en-MH",
+ "en-MP",
+ "en-MT",
+ "en-MU",
+ "en-MW",
+ "en-NA",
+ "en-NG",
+ "en-NZ",
+ "en-PG",
+ "en-PH",
+ "en-PK",
+ "en-PR",
+ "en-PW",
+ "en-SB",
+ "en-SC",
+ "en-SG",
+ "en-SL",
+ "en-SS",
+ "en-SZ",
+ "en-TC",
+ "en-TO",
+ "en-TT",
+ "en-TZ",
+ "en-UG",
+ "en-UM",
+ "en-US",
+ "en-US-POSIX",
+ "en-VC",
+ "en-VG",
+ "en-VI",
+ "en-VU",
+ "en-WS",
+ "en-ZA",
+ "en-ZM",
+ "en-ZW",
+ "eo",
+ "es",
+ "es-419",
+ "es-AR",
+ "es-BO",
+ "es-CL",
+ "es-CO",
+ "es-CR",
+ "es-CU",
+ "es-DO",
+ "es-EA",
+ "es-EC",
+ "es-ES",
+ "es-GQ",
+ "es-GT",
+ "es-HN",
+ "es-IC",
+ "es-MX",
+ "es-NI",
+ "es-PA",
+ "es-PE",
+ "es-PH",
+ "es-PR",
+ "es-PY",
+ "es-SV",
+ "es-US",
+ "es-UY",
+ "es-VE",
+ "et",
+ "et-EE",
+ "eu",
+ "eu-ES",
+ "fa",
+ "fa-AF",
+ "fa-IR",
+ "ff",
+ "ff-SN",
+ "fi",
+ "fi-FI",
+ "fr",
+ "fr-BE",
+ "fr-BF",
+ "fr-BI",
+ "fr-BJ",
+ "fr-BL",
+ "fr-CA",
+ "fr-CD",
+ "fr-CF",
+ "fr-CG",
+ "fr-CH",
+ "fr-CI",
+ "fr-CM",
+ "fr-DJ",
+ "fr-DZ",
+ "fr-FR",
+ "fr-GA",
+ "fr-GF",
+ "fr-GN",
+ "fr-GP",
+ "fr-GQ",
+ "fr-HT",
+ "fr-KM",
+ "fr-LU",
+ "fr-MA",
+ "fr-MC",
+ "fr-MF",
+ "fr-MG",
+ "fr-ML",
+ "fr-MQ",
+ "fr-MR",
+ "fr-MU",
+ "fr-NC",
+ "fr-NE",
+ "fr-PF",
+ "fr-RE",
+ "fr-RW",
+ "fr-SC",
+ "fr-SN",
+ "fr-SY",
+ "fr-TD",
+ "fr-TG",
+ "fr-TN",
+ "fr-VU",
+ "fr-YT",
+ "ga",
+ "ga-IE",
+ "gl",
+ "gl-ES",
+ "gu",
+ "gu-IN",
+ "he",
+ "he-IL",
+ "hi",
+ "hi-IN",
+ "hr",
+ "hr-BA",
+ "hr-HR",
+ "hu",
+ "hu-HU",
+ "hy",
+ "hy-AM",
+ "id",
+ "id-ID",
+ "is",
+ "is-IS",
+ "it",
+ "it-CH",
+ "it-IT",
+ "it-SM",
+ "ja",
+ "ja-JP",
+ "kk",
+ "kk-Cyrl",
+ "kk-Cyrl-KZ",
+ "km",
+ "km-KH",
+ "kn",
+ "kn-IN",
+ "ko",
+ "ko-KP",
+ "ko-KR",
+ "lt",
+ "lt-LT",
+ "lv",
+ "lv-LV",
+ "mk",
+ "mk-MK",
+ "ml",
+ "ml-IN",
+ "mr",
+ "mr-IN",
+ "nb",
+ "nb-NO",
+ "nl",
+ "nl-AW",
+ "nl-BE",
+ "nl-CW",
+ "nl-NL",
+ "nl-SR",
+ "nl-SX",
+ "nn",
+ "nn-NO",
+ "or",
+ "or-IN",
+ "pa",
+ "pa-Arab",
+ "pa-Arab-PK",
+ "pa-Guru",
+ "pa-Guru-IN",
+ "pl",
+ "pl-PL",
+ "pt",
+ "pt-AO",
+ "pt-BR",
+ "pt-CV",
+ "pt-GW",
+ "pt-MO",
+ "pt-MZ",
+ "pt-PT",
+ "pt-ST",
+ "pt-TL",
+ "rm",
+ "rm-CH",
+ "ro",
+ "ro-MD",
+ "ro-RO",
+ "ru",
+ "ru-BY",
+ "ru-KG",
+ "ru-KZ",
+ "ru-MD",
+ "ru-RU",
+ "ru-UA",
+ "si",
+ "si-LK",
+ "sk",
+ "sk-SK",
+ "sl",
+ "sl-SI",
+ "sq",
+ "sq-AL",
+ "sq-MK",
+ "sr",
+ "sr-Cyrl",
+ "sr-Cyrl-BA",
+ "sr-Cyrl-ME",
+ "sr-Cyrl-RS",
+ "sr-Latn",
+ "sr-Latn-BA",
+ "sr-Latn-ME",
+ "sr-Latn-RS",
+ "sv",
+ "sv-AX",
+ "sv-FI",
+ "sv-SE",
+ "te",
+ "te-IN",
+ "th",
+ "th-TH",
+ "tr",
+ "tr-CY",
+ "tr-TR",
+ "uk",
+ "uk-UA",
+ "vi",
+ "vi-VN",
+ "zh",
+ "zh-Hans",
+ "zh-Hans-CN",
+ "zh-Hans-HK",
+ "zh-Hans-MO",
+ "zh-Hans-SG",
+ "zh-Hant",
+ "zh-Hant-HK",
+ "zh-Hant-MO",
+ "zh-Hant-TW",
+];
+
+var count = Intl.NumberFormat.supportedLocalesOf(locales).length;
+
+reportCompare(locales.length, count, "Number of supported locales in Intl.NumberFormat");
diff --git a/js/src/tests/Intl/README.txt b/js/src/tests/Intl/README.txt
new file mode 100644
index 000000000..797b49ab5
--- /dev/null
+++ b/js/src/tests/Intl/README.txt
@@ -0,0 +1,27 @@
+Integration Tests for ECMAScript Internationalization API
+=========================================================
+
+The tests in this directory test the integration of the ICU library
+(Internationalization Components for Unicode) into the implementation of the
+ECMAScript Internationalization API in SpiderMonkey.
+
+These integration tests are complementary to:
+
+- The Test402 test suite maintained by Ecma TC39, which tests conformance of
+ an implementation to standard ECMA-402, ECMAScript Internationalization API
+ Specification. Test402 is currently maintained as part of Test262, the overall
+ conformance test suite for ECMAScript; for more information, see
+ http://wiki.ecmascript.org/doku.php?id=test262:test262
+
+- The test suite of the ICU library, which tests the implementation of ICU
+ itself and correct interpretation of the locale data it obtains from CLDR
+ (Common Locale Data Repository). For information on ICU, see
+ http://site.icu-project.org
+
+The integration tests check for a variety of locales and options whether the
+results are localized in a way that indicates correct integration with ICU.
+Such tests are somewhat fragile because the underlying locale data reflects
+real world usage and is therefore subject to change. When the ICU library used
+by Mozilla is upgraded, it is likely that some of the integration tests will
+fail because of locale data changes; however, others might fail because of
+actual software bugs. Failures therefore have to be examined carefully.
diff --git a/js/src/tests/Intl/TypedArray/shell.js b/js/src/tests/Intl/TypedArray/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/TypedArray/shell.js
diff --git a/js/src/tests/Intl/TypedArray/toLocaleString.js b/js/src/tests/Intl/TypedArray/toLocaleString.js
new file mode 100644
index 000000000..7a5d0be30
--- /dev/null
+++ b/js/src/tests/Intl/TypedArray/toLocaleString.js
@@ -0,0 +1,103 @@
+if (typeof Intl === "object") {
+ const constructors = [
+ Int8Array,
+ Uint8Array,
+ Uint8ClampedArray,
+ Int16Array,
+ Uint16Array,
+ Int32Array,
+ Uint32Array,
+ Float32Array,
+ Float64Array,
+ ];
+
+ const localeSep = [,,].toLocaleString();
+
+ const originalNumberToLocaleString = Number.prototype.toLocaleString;
+
+ // Missing arguments are passed as |undefined|.
+ for (let constructor of constructors) {
+ Number.prototype.toLocaleString = function() {
+ assertEq(arguments.length, 2);
+ assertEq(arguments[0], undefined);
+ assertEq(arguments[1], undefined);
+ return "pass";
+ };
+
+ // Single element case.
+ assertEq(new constructor(1).toLocaleString(), "pass");
+
+ // More than one element.
+ assertEq(new constructor(2).toLocaleString(), "pass" + localeSep + "pass");
+ }
+ Number.prototype.toLocaleString = originalNumberToLocaleString;
+
+ // Missing options is passed as |undefined|.
+ for (let constructor of constructors) {
+ Number.prototype.toLocaleString = function() {
+ assertEq(arguments.length, 2);
+ assertEq(arguments[0], locales);
+ assertEq(arguments[1], undefined);
+ return "pass";
+ };
+ let locales = {};
+
+ // Single element case.
+ assertEq(new constructor(1).toLocaleString(locales), "pass");
+
+ // More than one element.
+ assertEq(new constructor(2).toLocaleString(locales), "pass" + localeSep + "pass");
+ }
+ Number.prototype.toLocaleString = originalNumberToLocaleString;
+
+ // Ensure "locales" and "options" arguments are passed to the array elements.
+ for (let constructor of constructors) {
+ Number.prototype.toLocaleString = function() {
+ assertEq(arguments.length, 2);
+ assertEq(arguments[0], locales);
+ assertEq(arguments[1], options);
+ return "pass";
+ };
+ let locales = {};
+ let options = {};
+
+ // Single element case.
+ assertEq(new constructor(1).toLocaleString(locales, options), "pass");
+
+ // More than one element.
+ assertEq(new constructor(2).toLocaleString(locales, options), "pass" + localeSep + "pass");
+ }
+ Number.prototype.toLocaleString = originalNumberToLocaleString;
+
+ assertEq(new Float32Array([NaN]).toLocaleString("ar"), "ليس رقم");
+ assertEq(new Float64Array([NaN]).toLocaleString(["zh-hant", "ar"]), "非數值");
+ assertEq(new Float32Array([Infinity]).toLocaleString(["dz"]), "གྲངས་མེད");
+ assertEq(new Float64Array([-Infinity]).toLocaleString(["fr", "en"]), "-∞");
+
+ const sampleValues = [-0, +0, -1, +1, -2, +2, -0.5, +0.5];
+ const sampleLocales = [
+ void 0,
+ "en",
+ "th-th-u-nu-thai",
+ ["tlh", "de"],
+ ];
+ const sampleOptions = [
+ void 0,
+ {},
+ {style: "percent"},
+ {style: "currency", currency: "USD", minimumIntegerDigits: 4},
+ ];
+ for (let locale of sampleLocales) {
+ for (let options of sampleOptions) {
+ let nf = new Intl.NumberFormat(locale, options);
+ for (let constructor of constructors) {
+ let typedArray = new constructor(sampleValues);
+ let expected = [].map.call(typedArray, nf.format).join(localeSep);
+ assertEq(typedArray.toLocaleString(locale, options), expected);
+ }
+ }
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/Intl/browser.js b/js/src/tests/Intl/browser.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/browser.js
diff --git a/js/src/tests/Intl/extensions/browser.js b/js/src/tests/Intl/extensions/browser.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/extensions/browser.js
diff --git a/js/src/tests/Intl/extensions/options-value-emulates-undefined.js b/js/src/tests/Intl/extensions/options-value-emulates-undefined.js
new file mode 100644
index 000000000..a154e0c37
--- /dev/null
+++ b/js/src/tests/Intl/extensions/options-value-emulates-undefined.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!xulRuntime.shell)
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 843004;
+var summary =
+ "Use of an object that emulates |undefined| as the sole option must " +
+ "preclude imputing default values";
+
+print(BUGNUMBER + ": " + summary);
+
+if (typeof Intl !== 'object' && typeof quit == 'function') {
+ print("Test skipped");
+ reportCompare(true, true);
+ quit(0);
+}
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var opt = objectEmulatingUndefined();
+opt.toString = function() { return "long"; };
+
+var str = new Date(2013, 12 - 1, 14).toLocaleString("en-US", { weekday: opt });
+
+// Because "weekday" was present and not undefined (stringifying to "long"),
+// this must be a string like "Saturday" (in this implementation, that is).
+assertEq(str, "Saturday");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/Intl/extensions/shell.js b/js/src/tests/Intl/extensions/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/extensions/shell.js
diff --git a/js/src/tests/Intl/four-letter-language-codes.js b/js/src/tests/Intl/four-letter-language-codes.js
new file mode 100644
index 000000000..b92b4c3e1
--- /dev/null
+++ b/js/src/tests/Intl/four-letter-language-codes.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// So many non-existent four letter language codes to pick from.
+const languageTags = {
+ "Flob": "flob",
+ "ZORK": "zork",
+ "Blah-latn": "blah-Latn",
+ "QuuX-latn-us": "quux-Latn-US",
+ "SPAM-gb-x-Sausages-BACON-eggs": "spam-GB-x-sausages-bacon-eggs",
+};
+
+for (let [tag, canonical] of Object.entries(languageTags)) {
+ assertEq(Intl.getCanonicalLocales(tag)[0], canonical);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/Intl/getCalendarInfo.js b/js/src/tests/Intl/getCalendarInfo.js
new file mode 100644
index 000000000..951fd820e
--- /dev/null
+++ b/js/src/tests/Intl/getCalendarInfo.js
@@ -0,0 +1,83 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
+/* 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/. */
+
+// Tests the getCalendarInfo function with a diverse set of arguments.
+
+function checkCalendarInfo(info, expected)
+{
+ assertEq(Object.getPrototypeOf(info), Object.prototype);
+
+ assertEq(info.firstDayOfWeek, expected.firstDayOfWeek);
+ assertEq(info.minDays, expected.minDays);
+ assertEq(info.weekendStart, expected.weekendStart);
+ assertEq(info.weekendEnd, expected.weekendEnd);
+ assertEq(info.calendar, expected.calendar);
+ assertEq(info.locale, expected.locale);
+}
+
+addIntlExtras(Intl);
+
+let gCI = Intl.getCalendarInfo;
+
+assertEq(gCI.length, 1);
+
+checkCalendarInfo(gCI('en-US'), {
+ firstDayOfWeek: 1,
+ minDays: 1,
+ weekendStart: 7,
+ weekendEnd: 1,
+ calendar: "gregory",
+ locale: "en-US"
+});
+
+checkCalendarInfo(gCI('en-IL'), {
+ firstDayOfWeek: 1,
+ minDays: 1,
+ weekendStart: 6,
+ weekendEnd: 7,
+ calendar: "gregory",
+ locale: "en-IL"
+});
+
+
+checkCalendarInfo(gCI('en-GB'), {
+ firstDayOfWeek: 2,
+ minDays: 4,
+ weekendStart: 7,
+ weekendEnd: 1,
+ calendar: "gregory",
+ locale: "en-GB"
+});
+
+
+checkCalendarInfo(gCI('pl'), {
+ firstDayOfWeek: 2,
+ minDays: 4,
+ weekendStart: 7,
+ weekendEnd: 1,
+ calendar: "gregory",
+ locale: "pl"
+});
+
+checkCalendarInfo(gCI('ar-IQ'), {
+ firstDayOfWeek: 7,
+ minDays: 1,
+ weekendStart: 6,
+ weekendEnd: 7,
+ calendar: "gregory",
+ locale: "ar-IQ"
+});
+
+checkCalendarInfo(gCI('fa-IR'), {
+ firstDayOfWeek: 7,
+ minDays: 1,
+ weekendStart: 6,
+ weekendEnd: 6,
+ calendar: "persian",
+ locale: "fa-IR"
+});
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
diff --git a/js/src/tests/Intl/getCanonicalLocales-overridden-arg-length.js b/js/src/tests/Intl/getCanonicalLocales-overridden-arg-length.js
new file mode 100644
index 000000000..f0cb3d4ee
--- /dev/null
+++ b/js/src/tests/Intl/getCanonicalLocales-overridden-arg-length.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Tests the getCanonicalLocales function for overridden argument's length.
+
+var count = 0;
+var locs = { get length() { if (count++ > 0) throw 42; return 0; } };
+var locales = Intl.getCanonicalLocales(locs); // shouldn't throw 42
+assertEq(locales.length, 0);
+
+
+var obj = { get 0() { throw new Error("must not be gotten!"); },
+ length: -Math.pow(2, 32) + 1 };
+
+assertEq(Intl.getCanonicalLocales(obj).length, 0);
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
diff --git a/js/src/tests/Intl/getCanonicalLocales-overridden-push.js b/js/src/tests/Intl/getCanonicalLocales-overridden-push.js
new file mode 100644
index 000000000..36f53ebf0
--- /dev/null
+++ b/js/src/tests/Intl/getCanonicalLocales-overridden-push.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Tests the getCanonicalLocales function for overriden Array.push.
+
+Array.prototype.push = () => { throw 42; };
+
+// must not throw 42, might if push is used
+var arr = Intl.getCanonicalLocales(["en-US"]);
+
+assertEqArray(arr, ["en-US"]);
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
diff --git a/js/src/tests/Intl/getCanonicalLocales-overridden-set.js b/js/src/tests/Intl/getCanonicalLocales-overridden-set.js
new file mode 100644
index 000000000..396f3a948
--- /dev/null
+++ b/js/src/tests/Intl/getCanonicalLocales-overridden-set.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Tests the getCanonicalLocales function for overridden set().
+
+Object.defineProperty(Array.prototype, 0, { set() { throw 42; } });
+
+// must not throw 42, might if push is used
+var arr = Intl.getCanonicalLocales(["en-US"]);
+
+assertEqArray(arr, ["en-US"]);
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
diff --git a/js/src/tests/Intl/getCanonicalLocales-weird-cases.js b/js/src/tests/Intl/getCanonicalLocales-weird-cases.js
new file mode 100644
index 000000000..b933eb030
--- /dev/null
+++ b/js/src/tests/Intl/getCanonicalLocales-weird-cases.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Locale processing is supposed to internally remove any Unicode extension
+// sequences in the locale. Test that various weird testcases invoking
+// algorithmic edge cases don't assert or throw exceptions.
+
+var weirdCases =
+ [
+ "x-u-foo",
+ "en-x-u-foo",
+ "en-a-bar-x-u-foo",
+ "en-x-u-foo-a-bar",
+ "en-a-bar-u-baz-x-u-foo",
+ ];
+
+for (let weird of weirdCases)
+ assertEqArray(Intl.getCanonicalLocales(weird), [weird]);
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
+
diff --git a/js/src/tests/Intl/getCanonicalLocales-with-duplicates.js b/js/src/tests/Intl/getCanonicalLocales-with-duplicates.js
new file mode 100644
index 000000000..c02956948
--- /dev/null
+++ b/js/src/tests/Intl/getCanonicalLocales-with-duplicates.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Tests the getCanonicalLocales function for duplicate locales scenario.
+
+assertEqArray(Intl.getCanonicalLocales(['ab-cd', 'ff', 'de-rt', 'ab-Cd']),
+ ['ab-CD', 'ff', 'de-RT']);
+
+var locales = Intl.getCanonicalLocales(["en-US", "en-US"]);
+assertEqArray(locales, ['en-US']);
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
diff --git a/js/src/tests/Intl/getCanonicalLocales.js b/js/src/tests/Intl/getCanonicalLocales.js
new file mode 100644
index 000000000..153188160
--- /dev/null
+++ b/js/src/tests/Intl/getCanonicalLocales.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
+/* 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/. */
+
+// Tests the getCanonicalLocales function with a diverse set of arguments.
+
+let gCL = Intl.getCanonicalLocales;
+
+assertEq(Intl.getCanonicalLocales.length, 1);
+
+assertEqArray(gCL(), []);
+
+assertEqArray(gCL('ab-cd'), ['ab-CD']);
+
+assertEqArray(gCL(['ab-cd']), ['ab-CD']);
+
+assertEqArray(gCL(['ab-cd', 'FF']), ['ab-CD', 'ff']);
+
+assertEqArray(gCL({'a': 0}), []);
+
+assertEqArray(gCL({}), []);
+
+assertEqArray(gCL(['ar-ma-u-ca-islamicc']), ['ar-MA-u-ca-islamicc']);
+
+assertEqArray(gCL(['th-th-u-nu-thai']), ['th-TH-u-nu-thai']);
+
+assertEqArray(gCL(NaN), []);
+
+assertEqArray(gCL(1), []);
+
+Number.prototype[0] = "en-US";
+Number.prototype.length = 1;
+assertEqArray(gCL(NaN), ["en-US"]);
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
diff --git a/js/src/tests/Intl/shell.js b/js/src/tests/Intl/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/Intl/shell.js