OLD | NEW |
1 // Copyright 2013 the V8 project authors. All rights reserved. | 1 // Copyright 2013 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // ECMAScript 402 API implementation. | 5 // ECMAScript 402 API implementation. |
6 | 6 |
7 /** | 7 /** |
8 * Intl object is a single object that has some named properties, | 8 * Intl object is a single object that has some named properties, |
9 * all of which are constructors. | 9 * all of which are constructors. |
10 */ | 10 */ |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 return LANGUAGE_SINGLETON_RE; | 169 return LANGUAGE_SINGLETON_RE; |
170 } | 170 } |
171 | 171 |
172 /** | 172 /** |
173 * Matches valid IANA time zone names. | 173 * Matches valid IANA time zone names. |
174 */ | 174 */ |
175 var TIMEZONE_NAME_CHECK_RE = UNDEFINED; | 175 var TIMEZONE_NAME_CHECK_RE = UNDEFINED; |
176 | 176 |
177 function GetTimezoneNameCheckRE() { | 177 function GetTimezoneNameCheckRE() { |
178 if (IS_UNDEFINED(TIMEZONE_NAME_CHECK_RE)) { | 178 if (IS_UNDEFINED(TIMEZONE_NAME_CHECK_RE)) { |
179 TIMEZONE_NAME_CHECK_RE = | 179 TIMEZONE_NAME_CHECK_RE = new GlobalRegExp( |
180 new GlobalRegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$'); | 180 '^([A-Za-z]+)/([A-Za-z_-]+)((?:\/[A-Za-z_-]+)+)*$'); |
181 } | 181 } |
182 return TIMEZONE_NAME_CHECK_RE; | 182 return TIMEZONE_NAME_CHECK_RE; |
183 } | 183 } |
184 | 184 |
185 /** | 185 /** |
| 186 * Matches valid location parts of IANA time zone names. |
| 187 */ |
| 188 var TIMEZONE_NAME_LOCATION_PART_RE = UNDEFINED; |
| 189 |
| 190 function GetTimezoneNameLocationPartRE() { |
| 191 if (IS_UNDEFINED(TIMEZONE_NAME_LOCATION_PART_RE)) { |
| 192 TIMEZONE_NAME_LOCATION_PART_RE = |
| 193 new GlobalRegExp('^([A-Za-z]+)((?:[_-][A-Za-z]+)+)*$'); |
| 194 } |
| 195 return TIMEZONE_NAME_LOCATION_PART_RE; |
| 196 } |
| 197 |
| 198 /** |
186 * Adds bound method to the prototype of the given object. | 199 * Adds bound method to the prototype of the given object. |
187 */ | 200 */ |
188 function addBoundMethod(obj, methodName, implementation, length) { | 201 function addBoundMethod(obj, methodName, implementation, length) { |
189 %CheckIsBootstrapping(); | 202 %CheckIsBootstrapping(); |
190 function getter() { | 203 function getter() { |
191 if (!%IsInitializedIntlObject(this)) { | 204 if (!%IsInitializedIntlObject(this)) { |
192 throw MakeTypeError(kMethodCalledOnWrongObject, methodName); | 205 throw MakeTypeError(kMethodCalledOnWrongObject, methodName); |
193 } | 206 } |
194 var internalName = '__bound' + methodName + '__'; | 207 var internalName = '__bound' + methodName + '__'; |
195 if (IS_UNDEFINED(this[internalName])) { | 208 if (IS_UNDEFINED(this[internalName])) { |
(...skipping 476 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
672 | 685 |
673 /** | 686 /** |
674 * Returns titlecased word, aMeRricA -> America. | 687 * Returns titlecased word, aMeRricA -> America. |
675 */ | 688 */ |
676 function toTitleCaseWord(word) { | 689 function toTitleCaseWord(word) { |
677 return %StringToUpperCase(%_Call(StringSubstr, word, 0, 1)) + | 690 return %StringToUpperCase(%_Call(StringSubstr, word, 0, 1)) + |
678 %StringToLowerCase(%_Call(StringSubstr, word, 1)); | 691 %StringToLowerCase(%_Call(StringSubstr, word, 1)); |
679 } | 692 } |
680 | 693 |
681 /** | 694 /** |
| 695 * Returns titlecased location, bueNos_airES -> Buenos_Aires |
| 696 * or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only |
| 697 * deals with ASCII only characters. |
| 698 * 'of', 'au' and 'es' are special-cased and lowercased. |
| 699 */ |
| 700 function toTitleCaseTimezoneLocation(location) { |
| 701 var match = %_Call(StringMatch, location, GetTimezoneNameLocationPartRE()); |
| 702 if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, location); |
| 703 |
| 704 var result = toTitleCaseWord(match[1]); |
| 705 if (!IS_UNDEFINED(match[2]) && 2 < match.length) { |
| 706 // The first character is a separator, '_' or '-'. |
| 707 // None of IANA zone names has both '_' and '-'. |
| 708 var separator = %_Call(StringSubstring, match[2], 0, 1); |
| 709 var parts = %_Call(StringSplit, match[2], separator); |
| 710 for (var i = 1; i < parts.length; i++) { |
| 711 var part = parts[i] |
| 712 var lowercasedPart = %StringToLowerCase(part); |
| 713 result = result + separator + |
| 714 ((lowercasedPart !== 'es' && |
| 715 lowercasedPart !== 'of' && lowercasedPart !== 'au') ? |
| 716 toTitleCaseWord(part) : lowercasedPart); |
| 717 } |
| 718 } |
| 719 return result; |
| 720 } |
| 721 |
| 722 /** |
682 * Canonicalizes the language tag, or throws in case the tag is invalid. | 723 * Canonicalizes the language tag, or throws in case the tag is invalid. |
683 */ | 724 */ |
684 function canonicalizeLanguageTag(localeID) { | 725 function canonicalizeLanguageTag(localeID) { |
685 // null is typeof 'object' so we have to do extra check. | 726 // null is typeof 'object' so we have to do extra check. |
686 if (typeof localeID !== 'string' && typeof localeID !== 'object' || | 727 if (typeof localeID !== 'string' && typeof localeID !== 'object' || |
687 IS_NULL(localeID)) { | 728 IS_NULL(localeID)) { |
688 throw MakeTypeError(kLanguageID); | 729 throw MakeTypeError(kLanguageID); |
689 } | 730 } |
690 | 731 |
691 var localeString = GlobalString(localeID); | 732 var localeString = GlobalString(localeID); |
(...skipping 1036 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1728 GlobalString(value)); | 1769 GlobalString(value)); |
1729 } | 1770 } |
1730 | 1771 |
1731 | 1772 |
1732 // 0 because date is optional argument. | 1773 // 0 because date is optional argument. |
1733 addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0); | 1774 addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0); |
1734 addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1); | 1775 addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1); |
1735 | 1776 |
1736 | 1777 |
1737 /** | 1778 /** |
1738 * Returns canonical Area/Location name, or throws an exception if the zone | 1779 * Returns canonical Area/Location(/Location) name, or throws an exception |
1739 * name is invalid IANA name. | 1780 * if the zone name is invalid IANA name. |
1740 */ | 1781 */ |
1741 function canonicalizeTimeZoneID(tzID) { | 1782 function canonicalizeTimeZoneID(tzID) { |
1742 // Skip undefined zones. | 1783 // Skip undefined zones. |
1743 if (IS_UNDEFINED(tzID)) { | 1784 if (IS_UNDEFINED(tzID)) { |
1744 return tzID; | 1785 return tzID; |
1745 } | 1786 } |
1746 | 1787 |
1747 // Special case handling (UTC, GMT). | 1788 // Special case handling (UTC, GMT). |
1748 var upperID = %StringToUpperCase(tzID); | 1789 var upperID = %StringToUpperCase(tzID); |
1749 if (upperID === 'UTC' || upperID === 'GMT' || | 1790 if (upperID === 'UTC' || upperID === 'GMT' || |
1750 upperID === 'ETC/UTC' || upperID === 'ETC/GMT') { | 1791 upperID === 'ETC/UTC' || upperID === 'ETC/GMT') { |
1751 return 'UTC'; | 1792 return 'UTC'; |
1752 } | 1793 } |
1753 | 1794 |
1754 // We expect only _ and / beside ASCII letters. | 1795 // TODO(jshin): Add support for Etc/GMT[+-]([1-9]|1[0-2]) |
1755 // All inputs should conform to Area/Location from now on. | 1796 |
| 1797 // We expect only _, '-' and / beside ASCII letters. |
| 1798 // All inputs should conform to Area/Location(/Location)* from now on. |
1756 var match = %_Call(StringMatch, tzID, GetTimezoneNameCheckRE()); | 1799 var match = %_Call(StringMatch, tzID, GetTimezoneNameCheckRE()); |
1757 if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, tzID); | 1800 if (IS_NULL(match)) throw MakeRangeError(kExpectedTimezoneID, tzID); |
1758 | 1801 |
1759 var result = toTitleCaseWord(match[1]) + '/' + toTitleCaseWord(match[2]); | 1802 var result = toTitleCaseTimezoneLocation(match[1]) + '/' + |
1760 var i = 3; | 1803 toTitleCaseTimezoneLocation(match[2]); |
1761 while (!IS_UNDEFINED(match[i]) && i < match.length) { | 1804 |
1762 result = result + '_' + toTitleCaseWord(match[i]); | 1805 if (!IS_UNDEFINED(match[3]) && 3 < match.length) { |
1763 i++; | 1806 var locations = %_Call(StringSplit, match[3], '/'); |
| 1807 // The 1st element is empty. Starts with i=1. |
| 1808 for (var i = 1; i < locations.length; i++) { |
| 1809 result = result + '/' + toTitleCaseTimezoneLocation(locations[i]); |
| 1810 } |
1764 } | 1811 } |
1765 | 1812 |
1766 return result; | 1813 return result; |
1767 } | 1814 } |
1768 | 1815 |
1769 /** | 1816 /** |
1770 * Initializes the given object so it's a valid BreakIterator instance. | 1817 * Initializes the given object so it's a valid BreakIterator instance. |
1771 * Useful for subclassing. | 1818 * Useful for subclassing. |
1772 */ | 1819 */ |
1773 function initializeBreakIterator(iterator, locales, options) { | 1820 function initializeBreakIterator(iterator, locales, options) { |
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2113 } | 2160 } |
2114 | 2161 |
2115 var locales = %_Arguments(0); | 2162 var locales = %_Arguments(0); |
2116 var options = %_Arguments(1); | 2163 var options = %_Arguments(1); |
2117 return toLocaleDateTime( | 2164 return toLocaleDateTime( |
2118 this, locales, options, 'time', 'time', 'dateformattime'); | 2165 this, locales, options, 'time', 'time', 'dateformattime'); |
2119 } | 2166 } |
2120 ); | 2167 ); |
2121 | 2168 |
2122 }) | 2169 }) |
OLD | NEW |