Fix special case timezone RangeError
Bug: chromium:364374 Change-Id: I361403b26529860c4240d06766a82937ee8ca115 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2150070 Commit-Queue: Frank Tang <ftang@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#67158}
This commit is contained in:
parent
63f5cecf18
commit
8ef3e03ea4
@ -261,7 +261,7 @@ const std::vector<PatternData>& GetPatternData(
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetGMTTzID(Isolate* isolate, const std::string& input) {
|
||||
std::string GetGMTTzID(const std::string& input) {
|
||||
std::string ret = "Etc/GMT";
|
||||
switch (input.length()) {
|
||||
case 8:
|
||||
@ -304,10 +304,8 @@ char LocaleIndependentAsciiToLower(char ch) {
|
||||
// or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only
|
||||
// deals with ASCII only characters.
|
||||
// 'of', 'au' and 'es' are special-cased and lowercased.
|
||||
// Also "Antarctica/DumontDUrville" is special case.
|
||||
// ICU's timezone parsing is case sensitive, but ECMAScript is case insensitive
|
||||
std::string ToTitleCaseTimezoneLocation(Isolate* isolate,
|
||||
const std::string& input) {
|
||||
std::string ToTitleCaseTimezoneLocation(const std::string& input) {
|
||||
std::string title_cased;
|
||||
int word_length = 0;
|
||||
for (char ch : input) {
|
||||
@ -332,34 +330,102 @@ std::string ToTitleCaseTimezoneLocation(Isolate* isolate,
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
// Special case
|
||||
if (title_cased == "Antarctica/Dumontdurville") {
|
||||
return "Antarctica/DumontDUrville";
|
||||
}
|
||||
|
||||
return title_cased;
|
||||
}
|
||||
|
||||
class SpecialTimeZoneMap {
|
||||
public:
|
||||
SpecialTimeZoneMap() {
|
||||
Add("America/Argentina/ComodRivadavia");
|
||||
Add("America/Knox_IN");
|
||||
Add("Antarctica/McMurdo");
|
||||
Add("Australia/ACT");
|
||||
Add("Australia/LHI");
|
||||
Add("Australia/NSW");
|
||||
Add("Antarctica/DumontDUrville");
|
||||
Add("Brazil/DeNoronha");
|
||||
Add("CET");
|
||||
Add("CST6CDT");
|
||||
Add("Chile/EasterIsland");
|
||||
Add("EET");
|
||||
Add("EST");
|
||||
Add("EST5EDT");
|
||||
Add("GB");
|
||||
Add("GB-Eire");
|
||||
Add("HST");
|
||||
Add("MET");
|
||||
Add("MST");
|
||||
Add("MST7MDT");
|
||||
Add("Mexico/BajaNorte");
|
||||
Add("Mexico/BajaSur");
|
||||
Add("NZ");
|
||||
Add("NZ-CHAT");
|
||||
Add("PRC");
|
||||
Add("PST8PDT");
|
||||
Add("ROC");
|
||||
Add("ROK");
|
||||
Add("UCT");
|
||||
Add("W-SU");
|
||||
Add("WET");
|
||||
}
|
||||
|
||||
std::string Find(const std::string& id) {
|
||||
auto it = map_.find(id);
|
||||
if (it != map_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
void Add(const char* id) {
|
||||
std::string upper(id);
|
||||
transform(upper.begin(), upper.end(), upper.begin(),
|
||||
LocaleIndependentAsciiToUpper);
|
||||
map_.insert({upper, id});
|
||||
}
|
||||
std::map<std::string, std::string> map_;
|
||||
};
|
||||
|
||||
// Return the time zone id which match ICU's expectation of title casing
|
||||
// return empty string when error.
|
||||
std::string CanonicalizeTimeZoneID(Isolate* isolate, const std::string& input) {
|
||||
std::string CanonicalizeTimeZoneID(const std::string& input) {
|
||||
std::string upper = input;
|
||||
transform(upper.begin(), upper.end(), upper.begin(),
|
||||
LocaleIndependentAsciiToUpper);
|
||||
if (upper == "UTC" || upper == "GMT" || upper == "ETC/UTC" ||
|
||||
upper == "ETC/GMT") {
|
||||
return "UTC";
|
||||
if (upper.length() >= 3) {
|
||||
if (memcmp(upper.c_str(), "ETC", 3) == 0) {
|
||||
if (upper == "ETC/UTC" || upper == "ETC/GMT" || upper == "ETC/UCT") {
|
||||
return "UTC";
|
||||
}
|
||||
if (strncmp(upper.c_str(), "ETC/GMT", 7) == 0) {
|
||||
return GetGMTTzID(input);
|
||||
}
|
||||
} else if (memcmp(upper.c_str(), "GMT", 3) == 0) {
|
||||
if (upper == "GMT" || upper == "GMT0" || upper == "GMT+0" ||
|
||||
upper == "GMT-0") {
|
||||
return "UTC";
|
||||
}
|
||||
} else if (memcmp(upper.c_str(), "US/", 3) == 0) {
|
||||
std::string title = ToTitleCaseTimezoneLocation(input);
|
||||
// Change "Us/" to "US/"
|
||||
title[1] = 'S';
|
||||
return title;
|
||||
} else if (upper == "UTC") {
|
||||
return "UTC";
|
||||
}
|
||||
}
|
||||
// We expect only _, '-' and / beside ASCII letters.
|
||||
// All inputs should conform to Area/Location(/Location)*, or Etc/GMT* .
|
||||
// TODO(jshin): 1. Support 'GB-Eire", 'EST5EDT", "ROK', 'US/*', 'NZ' and many
|
||||
// other aliases/linked names when moving timezone validation code to C++.
|
||||
// See crbug.com/364374 and crbug.com/v8/8007 .
|
||||
// 2. Resolve the difference betwee CLDR/ICU and IANA time zone db.
|
||||
// See http://unicode.org/cldr/trac/ticket/9892 and crbug.com/645807 .
|
||||
if (strncmp(upper.c_str(), "ETC/GMT", 7) == 0) {
|
||||
return GetGMTTzID(isolate, input);
|
||||
|
||||
static base::LazyInstance<SpecialTimeZoneMap>::type special_time_zone_map =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
std::string special_case = special_time_zone_map.Pointer()->Find(upper);
|
||||
if (!special_case.empty()) {
|
||||
return special_case;
|
||||
}
|
||||
return ToTitleCaseTimezoneLocation(isolate, input);
|
||||
return ToTitleCaseTimezoneLocation(input);
|
||||
}
|
||||
|
||||
Handle<String> DateTimeStyleAsString(Isolate* isolate,
|
||||
@ -868,15 +934,14 @@ bool IsValidTimeZoneName(const icu::TimeZone& tz) {
|
||||
canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV);
|
||||
}
|
||||
|
||||
std::unique_ptr<icu::TimeZone> CreateTimeZone(Isolate* isolate,
|
||||
const char* timezone) {
|
||||
std::unique_ptr<icu::TimeZone> CreateTimeZone(const char* timezone) {
|
||||
// Create time zone as specified by the user. We have to re-create time zone
|
||||
// since calendar takes ownership.
|
||||
if (timezone == nullptr) {
|
||||
// 19.a. Else / Let timeZone be DefaultTimeZone().
|
||||
return std::unique_ptr<icu::TimeZone>(icu::TimeZone::createDefault());
|
||||
}
|
||||
std::string canonicalized = CanonicalizeTimeZoneID(isolate, timezone);
|
||||
std::string canonicalized = CanonicalizeTimeZoneID(timezone);
|
||||
if (canonicalized.empty()) return std::unique_ptr<icu::TimeZone>();
|
||||
std::unique_ptr<icu::TimeZone> tz(
|
||||
icu::TimeZone::createTimeZone(canonicalized.c_str()));
|
||||
@ -1430,7 +1495,7 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::New(
|
||||
isolate, options, "timeZone", empty_values, service, &timezone);
|
||||
MAYBE_RETURN(maybe_timezone, Handle<JSDateTimeFormat>());
|
||||
|
||||
std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(isolate, timezone.get());
|
||||
std::unique_ptr<icu::TimeZone> tz = CreateTimeZone(timezone.get());
|
||||
if (tz.get() == nullptr) {
|
||||
THROW_NEW_ERROR(
|
||||
isolate,
|
||||
|
60
test/intl/regress-364374.js
Normal file
60
test/intl/regress-364374.js
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
const special_time_zones = [
|
||||
"America/Argentina/ComodRivadavia",
|
||||
"America/Knox_IN",
|
||||
"Antarctica/McMurdo",
|
||||
"Australia/ACT",
|
||||
"Australia/LHI",
|
||||
"Australia/NSW",
|
||||
"Brazil/DeNoronha",
|
||||
"CET",
|
||||
"CST6CDT",
|
||||
"Chile/EasterIsland",
|
||||
"Etc/UCT",
|
||||
"EET",
|
||||
"EST",
|
||||
"EST5EDT",
|
||||
"GB",
|
||||
"GB-Eire",
|
||||
"GMT+0",
|
||||
"GMT-0",
|
||||
"GMT0",
|
||||
"HST",
|
||||
"MET",
|
||||
"MST",
|
||||
"MST7MDT",
|
||||
"Mexico/BajaNorte",
|
||||
"Mexico/BajaSur",
|
||||
"NZ",
|
||||
"NZ-CHAT",
|
||||
"PRC",
|
||||
"PST8PDT",
|
||||
"ROC",
|
||||
"ROK",
|
||||
"UCT",
|
||||
"US/Alaska",
|
||||
"US/Aleutian",
|
||||
"US/Arizona",
|
||||
"US/Central",
|
||||
"US/East-Indiana",
|
||||
"US/Eastern",
|
||||
"US/Hawaii",
|
||||
"US/Indiana-Starke",
|
||||
"US/Michigan",
|
||||
"US/Mountain",
|
||||
"US/Pacific",
|
||||
"US/Pacific-New",
|
||||
"US/Samoa",
|
||||
"W-SU",
|
||||
"WET",
|
||||
];
|
||||
|
||||
special_time_zones.forEach(function(timeZone) {
|
||||
assertDoesNotThrow(() => {
|
||||
// Make sure the following wont throw RangeError exception
|
||||
df = new Intl.DateTimeFormat(undefined, {timeZone});
|
||||
});
|
||||
})
|
Loading…
Reference in New Issue
Block a user