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:
Frank Tang 2020-04-15 10:34:40 -07:00 committed by Commit Bot
parent 63f5cecf18
commit 8ef3e03ea4
2 changed files with 150 additions and 25 deletions

View File

@ -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,

View 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});
});
})