| Index: src/js/i18n.js
|
| diff --git a/src/js/i18n.js b/src/js/i18n.js
|
| index a859a9fbeb9f1028e0c6245364a800651d8e7f29..c32e8f18e6938a4f396350c3bef3181cfe14ee8a 100644
|
| --- a/src/js/i18n.js
|
| +++ b/src/js/i18n.js
|
| @@ -176,13 +176,26 @@ var TIMEZONE_NAME_CHECK_RE = UNDEFINED;
|
|
|
| function GetTimezoneNameCheckRE() {
|
| if (IS_UNDEFINED(TIMEZONE_NAME_CHECK_RE)) {
|
| - TIMEZONE_NAME_CHECK_RE =
|
| - new GlobalRegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$');
|
| + TIMEZONE_NAME_CHECK_RE = new GlobalRegExp(
|
| + '^([A-Za-z]+)/([A-Za-z_-]+)((?:\/[A-Za-z_-]+)+)*$');
|
| }
|
| return TIMEZONE_NAME_CHECK_RE;
|
| }
|
|
|
| /**
|
| + * Matches valid location parts of IANA time zone names.
|
| + */
|
| +var TIMEZONE_NAME_LOCATION_PART_RE = UNDEFINED;
|
| +
|
| +function GetTimezoneNameLocationPartRE() {
|
| + if (IS_UNDEFINED(TIMEZONE_NAME_LOCATION_PART_RE)) {
|
| + TIMEZONE_NAME_LOCATION_PART_RE =
|
| + new GlobalRegExp('^([A-Za-z]+)((?:[_-][A-Za-z]+)+)*$');
|
| + }
|
| + return TIMEZONE_NAME_LOCATION_PART_RE;
|
| +}
|
| +
|
| +/**
|
| * Adds bound method to the prototype of the given object.
|
| */
|
| function addBoundMethod(obj, methodName, implementation, length) {
|
| @@ -679,6 +692,34 @@ function toTitleCaseWord(word) {
|
| }
|
|
|
| /**
|
| + * Returns titlecased location, bueNos_airES -> Buenos_Aires
|
| + * 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.
|
| + */
|
| +function toTitleCaseTimezoneLocation(location) {
|
| + var match = %_Call(StringMatch, location, GetTimezoneNameLocationPartRE());
|
| + if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, location);
|
| +
|
| + var result = toTitleCaseWord(match[1]);
|
| + if (!IS_UNDEFINED(match[2]) && 2 < match.length) {
|
| + // The first character is a separator, '_' or '-'.
|
| + // None of IANA zone names has both '_' and '-'.
|
| + var separator = %_Call(StringSubstring, match[2], 0, 1);
|
| + var parts = %_Call(StringSplit, match[2], separator);
|
| + for (var i = 1; i < parts.length; i++) {
|
| + var part = parts[i]
|
| + var lowercasedPart = %StringToLowerCase(part);
|
| + result = result + separator +
|
| + ((lowercasedPart !== 'es' &&
|
| + lowercasedPart !== 'of' && lowercasedPart !== 'au') ?
|
| + toTitleCaseWord(part) : lowercasedPart);
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +/**
|
| * Canonicalizes the language tag, or throws in case the tag is invalid.
|
| */
|
| function canonicalizeLanguageTag(localeID) {
|
| @@ -1735,8 +1776,8 @@ addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1);
|
|
|
|
|
| /**
|
| - * Returns canonical Area/Location name, or throws an exception if the zone
|
| - * name is invalid IANA name.
|
| + * Returns canonical Area/Location(/Location) name, or throws an exception
|
| + * if the zone name is invalid IANA name.
|
| */
|
| function canonicalizeTimeZoneID(tzID) {
|
| // Skip undefined zones.
|
| @@ -1751,16 +1792,22 @@ function canonicalizeTimeZoneID(tzID) {
|
| return 'UTC';
|
| }
|
|
|
| - // We expect only _ and / beside ASCII letters.
|
| - // All inputs should conform to Area/Location from now on.
|
| + // TODO(jshin): Add support for Etc/GMT[+-]([1-9]|1[0-2])
|
| +
|
| + // We expect only _, '-' and / beside ASCII letters.
|
| + // All inputs should conform to Area/Location(/Location)* from now on.
|
| var match = %_Call(StringMatch, tzID, GetTimezoneNameCheckRE());
|
| - if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, tzID);
|
| + if (IS_NULL(match)) throw MakeRangeError(kExpectedTimezoneID, tzID);
|
| +
|
| + var result = toTitleCaseTimezoneLocation(match[1]) + '/' +
|
| + toTitleCaseTimezoneLocation(match[2]);
|
|
|
| - var result = toTitleCaseWord(match[1]) + '/' + toTitleCaseWord(match[2]);
|
| - var i = 3;
|
| - while (!IS_UNDEFINED(match[i]) && i < match.length) {
|
| - result = result + '_' + toTitleCaseWord(match[i]);
|
| - i++;
|
| + if (!IS_UNDEFINED(match[3]) && 3 < match.length) {
|
| + var locations = %_Call(StringSplit, match[3], '/');
|
| + // The 1st element is empty. Starts with i=1.
|
| + for (var i = 1; i < locations.length; i++) {
|
| + result = result + '/' + toTitleCaseTimezoneLocation(locations[i]);
|
| + }
|
| }
|
|
|
| return result;
|
|
|