Skip to content

Commit

Permalink
Bug 1670033 - Part 6: Implement "timeZone" support for the "Intl Enum…
Browse files Browse the repository at this point in the history
…eration API" proposal. r=tcampbell

Differential Revision: https://phabricator.services.mozilla.com/D120606
  • Loading branch information
anba committed Aug 27, 2021
1 parent 14a4fa8 commit 6978738
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 3 deletions.
1 change: 0 additions & 1 deletion js/src/builtin/intl/DateTimeFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,6 @@ bool js::intl_defaultTimeZone(JSContext* cx, unsigned argc, Value* vp) {
}

args.rval().setString(str);

return true;
}

Expand Down
2 changes: 1 addition & 1 deletion js/src/builtin/intl/FormatBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class FormatBuffer {
* errors. In this case it returns a nullptr that must be checked, but it may
* not be obvious.
*/
JSString* toString() const {
JSLinearString* toString() const {
if constexpr (std::is_same_v<CharT, uint8_t> ||
std::is_same_v<CharT, unsigned char> ||
std::is_same_v<CharT, char>) {
Expand Down
75 changes: 75 additions & 0 deletions js/src/builtin/intl/IntlObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "builtin/intl/IntlObject.h"

#include "mozilla/Assertions.h"
#include "mozilla/intl/Calendar.h"
#include "mozilla/Likely.h"
#include "mozilla/Range.h"

Expand All @@ -21,6 +22,7 @@
#include "builtin/intl/Collator.h"
#include "builtin/intl/CommonFunctions.h"
#include "builtin/intl/DateTimeFormat.h"
#include "builtin/intl/FormatBuffer.h"
#include "builtin/intl/LanguageTag.h"
#include "builtin/intl/NumberFormat.h"
#include "builtin/intl/NumberingSystemsGenerated.h"
Expand Down Expand Up @@ -1159,6 +1161,77 @@ static ArrayObject* AvailableNumberingSystems(JSContext* cx) {
return CreateArrayFromSortedList(cx, numberingSystems);
}

/**
* AvailableTimeZones ( )
*/
static ArrayObject* AvailableTimeZones(JSContext* cx) {
// Unsorted list of canonical time zone names, possibly containing duplicates.
Rooted<StringList> timeZones(cx, StringList(cx));

intl::SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
auto iterResult = sharedIntlData.availableTimeZonesIteration(cx);
if (iterResult.isErr()) {
return nullptr;
}
auto iter = iterResult.unwrap();

RootedAtom validatedTimeZone(cx);
RootedAtom ianaTimeZone(cx);
for (; !iter.done(); iter.next()) {
validatedTimeZone = iter.get();

// Canonicalize the time zone before adding it to the result array.

// Some time zone names are canonicalized differently by ICU -- handle those
// first.
ianaTimeZone.set(nullptr);
if (!sharedIntlData.tryCanonicalizeTimeZoneConsistentWithIANA(
cx, validatedTimeZone, &ianaTimeZone)) {
return nullptr;
}

JSLinearString* timeZone;
if (ianaTimeZone) {
cx->markAtom(ianaTimeZone);

timeZone = ianaTimeZone;
} else {
// Call into ICU to canonicalize the time zone.

JS::AutoStableStringChars stableChars(cx);
if (!stableChars.initTwoByte(cx, validatedTimeZone)) {
return nullptr;
}

intl::FormatBuffer<char16_t, intl::INITIAL_CHAR_BUFFER_SIZE>
canonicalTimeZone(cx);
auto result = mozilla::intl::Calendar::GetCanonicalTimeZoneID(
stableChars.twoByteRange(), canonicalTimeZone);
if (result.isErr()) {
intl::ReportInternalError(cx, result.unwrapErr());
return nullptr;
}

timeZone = canonicalTimeZone.toString();
if (!timeZone) {
return nullptr;
}

// Canonicalize both to "UTC" per CanonicalizeTimeZoneName().
if (StringEqualsLiteral(timeZone, "Etc/UTC") ||
StringEqualsLiteral(timeZone, "Etc/GMT")) {
timeZone = cx->names().UTC;
}
}

if (!timeZones.append(timeZone)) {
return nullptr;
}
}

return CreateArrayFromList(cx, &timeZones);
}

bool js::intl_SupportedValuesOf(JSContext* cx, unsigned argc, JS::Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
Expand All @@ -1178,6 +1251,8 @@ bool js::intl_SupportedValuesOf(JSContext* cx, unsigned argc, JS::Value* vp) {
list = AvailableCurrencies(cx);
} else if (StringEqualsLiteral(key, "numberingSystem")) {
list = AvailableNumberingSystems(cx);
} else if (StringEqualsLiteral(key, "timeZone")) {
list = AvailableTimeZones(cx);
} else {
ReportBadKey(cx, key);
return false;
Expand Down
8 changes: 8 additions & 0 deletions js/src/builtin/intl/SharedIntlData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,14 @@ bool js::intl::SharedIntlData::tryCanonicalizeTimeZoneConsistentWithIANA(
return true;
}

JS::Result<js::intl::SharedIntlData::TimeZoneSet::Iterator>
js::intl::SharedIntlData::availableTimeZonesIteration(JSContext* cx) {
if (!ensureTimeZones(cx)) {
return cx->alreadyReportedError();
}
return availableTimeZones.iter();
}

js::intl::SharedIntlData::LocaleHasher::Lookup::Lookup(JSLinearString* locale)
: js::intl::SharedIntlData::LinearStringLookup(locale) {
if (isLatin1) {
Expand Down
10 changes: 9 additions & 1 deletion js/src/builtin/intl/SharedIntlData.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "js/CharacterEncoding.h"
#include "js/GCAPI.h"
#include "js/GCHashTable.h"
#include "js/Result.h"
#include "js/RootingAPI.h"
#include "js/Utility.h"
#include "vm/StringType.h"
Expand Down Expand Up @@ -71,7 +72,7 @@ class SharedIntlData {
}
};

private:
public:
/**
* Information tracking the set of the supported time zone names, derived
* from the IANA time zone database <https://www.iana.org/time-zones>.
Expand Down Expand Up @@ -113,6 +114,7 @@ class SharedIntlData {
using TimeZoneMap =
GCHashMap<TimeZoneName, TimeZoneName, TimeZoneHasher, SystemAllocPolicy>;

private:
/**
* As a threshold matter, available time zones are those time zones ICU
* supports, via ucal_openTimeZones. But ICU supports additional non-IANA
Expand Down Expand Up @@ -177,6 +179,12 @@ class SharedIntlData {
JSContext* cx, JS::Handle<JSString*> timeZone,
JS::MutableHandle<JSAtom*> result);

/**
* Returns an iterator over all available time zones supported by ICU. The
* returned time zone names aren't canonicalized.
*/
JS::Result<TimeZoneSet::Iterator> availableTimeZonesIteration(JSContext* cx);

private:
using Locale = JSAtom*;

Expand Down
20 changes: 20 additions & 0 deletions js/src/tests/non262/Intl/supportedValuesOf-timeZones.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))

const timeZones = Intl.supportedValuesOf("timeZone");

assertEq(new Set(timeZones).size, timeZones.length, "No duplicates are present");
assertEqArray(timeZones, [...timeZones].sort(), "The list is sorted");

// The pattern doesn't cover the complete time zone syntax, but gives a good first approximation.
const timeZoneRE = /^[a-z0-9_+-]+(\/[a-z0-9_+-]+)*$/i;
for (let timeZone of timeZones) {
assertEq(timeZoneRE.test(timeZone), true, `${timeZone} is ASCII`);
}

for (let timeZone of timeZones) {
let obj = new Intl.DateTimeFormat("en", {timeZone});
assertEq(obj.resolvedOptions().timeZone, timeZone, `${timeZone} is supported by DateTimeFormat`);
}

if (typeof reportCompare === "function")
reportCompare(true, true);
1 change: 1 addition & 0 deletions js/src/vm/CommonPropertyNames.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@
MACRO_(useAsm, useAsm, "use asm") \
MACRO_(useGrouping, useGrouping, "useGrouping") \
MACRO_(useStrict, useStrict, "use strict") \
MACRO_(UTC, UTC, "UTC") \
MACRO_(void, void_, "void") \
MACRO_(value, value, "value") \
MACRO_(valueOf, valueOf, "valueOf") \
Expand Down

0 comments on commit 6978738

Please sign in to comment.