--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -650,16 +650,17 @@ enum UDateFormatField {
};
enum UDateFormatStyle {
UDAT_FULL,
UDAT_LONG,
UDAT_MEDIUM,
UDAT_SHORT,
UDAT_DEFAULT = UDAT_MEDIUM,
+ UDAT_NONE = -1,
UDAT_PATTERN = -2,
UDAT_IGNORE = UDAT_PATTERN
};
enum UDateFormatSymbolType {
UDAT_ERAS,
UDAT_MONTHS,
UDAT_SHORT_MONTHS,
@@ -691,16 +692,23 @@ enum UDateFormatSymbolType {
};
int32_t
udat_countAvailable()
{
MOZ_CRASH("udat_countAvailable: Intl API disabled");
}
+int32_t
+udat_toPattern(const UDateFormat* fmt, UBool localized, UChar* result,
+ int32_t resultLength, UErrorCode* status)
+{
+ MOZ_CRASH("udat_toPattern: Intl API disabled");
+}
+
const char*
udat_getAvailable(int32_t localeIndex)
{
MOZ_CRASH("udat_getAvailable: Intl API disabled");
}
UDateFormat*
udat_open(UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const char* locale,
@@ -816,27 +824,34 @@ IntlInitialize(JSContext* cx, HandleObje
if (!js::CallSelfHostedFunction(cx, initializer, thisv, args, &ignored))
return false;
MOZ_ASSERT(ignored.isUndefined(),
"Unexpected return value from non-legacy Intl object initializer");
return true;
}
+enum class DateTimeFormatOptions
+{
+ Standard,
+ EnableMozExtensions,
+};
+
static bool
LegacyIntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
HandleValue thisValue, HandleValue locales, HandleValue options,
- MutableHandleValue result)
+ DateTimeFormatOptions dtfOptions, MutableHandleValue result)
{
- FixedInvokeArgs<4> args(cx);
+ FixedInvokeArgs<5> args(cx);
args[0].setObject(*obj);
args[1].set(thisValue);
args[2].set(locales);
args[3].set(options);
+ args[4].setBoolean(dtfOptions == DateTimeFormatOptions::EnableMozExtensions);
RootedValue thisv(cx, NullValue());
if (!js::CallSelfHostedFunction(cx, initializer, thisv, args, result))
return false;
MOZ_ASSERT(result.isObject(), "Legacy Intl object initializer must return an object");
return true;
}
@@ -1486,17 +1501,17 @@ NumberFormat(JSContext* cx, const CallAr
numberFormat->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
RootedValue thisValue(cx, construct ? ObjectValue(*numberFormat) : args.thisv());
RootedValue locales(cx, args.get(0));
RootedValue options(cx, args.get(1));
// Step 3.
return LegacyIntlInitialize(cx, numberFormat, cx->names().InitializeNumberFormat, thisValue,
- locales, options, args.rval());
+ locales, options, DateTimeFormatOptions::Standard, args.rval());
}
static bool
NumberFormat(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return NumberFormat(cx, args, args.isConstructing());
}
@@ -2351,17 +2366,17 @@ static const JSPropertySpec dateTimeForm
};
/**
* 12.2.1 Intl.DateTimeFormat([ locales [, options]])
*
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
*/
static bool
-DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
+DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct, DateTimeFormatOptions dtfOptions)
{
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
@@ -2381,55 +2396,71 @@ DateTimeFormat(JSContext* cx, const Call
PrivateValue(nullptr));
RootedValue thisValue(cx, construct ? ObjectValue(*dateTimeFormat) : args.thisv());
RootedValue locales(cx, args.get(0));
RootedValue options(cx, args.get(1));
// Step 3.
return LegacyIntlInitialize(cx, dateTimeFormat, cx->names().InitializeDateTimeFormat,
- thisValue, locales, options, args.rval());
+ thisValue, locales, options, dtfOptions, args.rval());
}
static bool
DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- return DateTimeFormat(cx, args, args.isConstructing());
+ return DateTimeFormat(cx, args, args.isConstructing(), DateTimeFormatOptions::Standard);
+}
+
+static bool
+MozDateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Don't allow to call mozIntl.DateTimeFormat as a function. That way we
+ // don't need to worry how to handle the legacy initialization semantics
+ // when applied on mozIntl.DateTimeFormat.
+ if (!ThrowIfNotConstructing(cx, args, "mozIntl.DateTimeFormat"))
+ return false;
+
+ return DateTimeFormat(cx, args, true, DateTimeFormatOptions::EnableMozExtensions);
}
bool
js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(!args.isConstructing());
// intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it
// cannot be used with "new", but it still has to be treated as a
// constructor.
- return DateTimeFormat(cx, args, true);
+ return DateTimeFormat(cx, args, true, DateTimeFormatOptions::Standard);
}
void
DateTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
{
MOZ_ASSERT(fop->onActiveCooperatingThread());
const Value& slot =
obj->as<DateTimeFormatObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT);
if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
udat_close(df);
}
static JSObject*
CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
- MutableHandleObject constructor)
+ MutableHandleObject constructor, DateTimeFormatOptions dtfOptions)
{
RootedFunction ctor(cx);
- ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
+ ctor = dtfOptions == DateTimeFormatOptions::EnableMozExtensions
+ ? GlobalObject::createConstructor(cx, MozDateTimeFormat, cx->names().DateTimeFormat, 0)
+ : GlobalObject::createConstructor(cx, DateTimeFormat, cx->names().DateTimeFormat, 0);
if (!ctor)
return nullptr;
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return nullptr;
if (!LinkConstructorAndPrototype(cx, ctor, proto))
@@ -2452,16 +2483,27 @@ CreateDateTimeFormatPrototype(JSContext*
if (!DefineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, nullptr, nullptr, 0))
return nullptr;
constructor.set(ctor);
return proto;
}
bool
+js::AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
+{
+ Handle<GlobalObject*> global = cx->global();
+
+ RootedObject mozDateTimeFormat(cx);
+ JSObject* mozDateTimeFormatProto =
+ CreateDateTimeFormatPrototype(cx, intl, global, &mozDateTimeFormat, DateTimeFormatOptions::EnableMozExtensions);
+ return mozDateTimeFormatProto != nullptr;
+}
+
+bool
js::intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
RootedValue result(cx);
if (!intl_availableLocales(cx, udat_countAvailable, udat_getAvailable, &result))
return false;
@@ -2969,16 +3011,89 @@ js::intl_patternForSkeleton(JSContext* c
});
if (!str)
return false;
args.rval().setString(str);
return true;
}
+bool
+js::intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 4);
+ MOZ_ASSERT(args[0].isString());
+
+ JSAutoByteString locale(cx, args[0].toString());
+ if (!locale)
+ return false;
+
+ UDateFormatStyle dateStyle = UDAT_NONE;
+ UDateFormatStyle timeStyle = UDAT_NONE;
+
+ if (args[1].isString()) {
+ JSLinearString* dateStyleStr = args[1].toString()->ensureLinear(cx);
+ if (!dateStyleStr)
+ return false;
+
+ if (StringEqualsAscii(dateStyleStr, "full"))
+ dateStyle = UDAT_FULL;
+ else if (StringEqualsAscii(dateStyleStr, "long"))
+ dateStyle = UDAT_LONG;
+ else if (StringEqualsAscii(dateStyleStr, "medium"))
+ dateStyle = UDAT_MEDIUM;
+ else if (StringEqualsAscii(dateStyleStr, "short"))
+ dateStyle = UDAT_SHORT;
+ else
+ MOZ_ASSERT_UNREACHABLE("unexpected dateStyle");
+ }
+
+ if (args[2].isString()) {
+ JSLinearString* timeStyleStr = args[2].toString()->ensureLinear(cx);
+ if (!timeStyleStr)
+ return false;
+
+ if (StringEqualsAscii(timeStyleStr, "full"))
+ timeStyle = UDAT_FULL;
+ else if (StringEqualsAscii(timeStyleStr, "long"))
+ timeStyle = UDAT_LONG;
+ else if (StringEqualsAscii(timeStyleStr, "medium"))
+ timeStyle = UDAT_MEDIUM;
+ else if (StringEqualsAscii(timeStyleStr, "short"))
+ timeStyle = UDAT_SHORT;
+ else
+ MOZ_ASSERT_UNREACHABLE("unexpected timeStyle");
+ }
+
+ AutoStableStringChars timeZone(cx);
+ if (!timeZone.initTwoByte(cx, args[3].toString()))
+ return false;
+
+ mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
+
+ UErrorCode status = U_ZERO_ERROR;
+ UDateFormat* df = udat_open(timeStyle, dateStyle, icuLocale(locale.ptr()),
+ Char16ToUChar(timeZoneChars.begin().get()),
+ timeZoneChars.length(), nullptr, -1, &status);
+ if (U_FAILURE(status)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+ return false;
+ }
+ ScopedICUObject<UDateFormat, udat_close> toClose(df);
+
+ JSString* str = Call(cx, [df](UChar* chars, uint32_t size, UErrorCode* status) {
+ return udat_toPattern(df, false, chars, size, status);
+ });
+ if (!str)
+ return false;
+ args.rval().setString(str);
+ return true;
+}
+
/**
* Returns a new UDateFormat with the locale and date-time formatting options
* of the given DateTimeFormat.
*/
static UDateFormat*
NewUDateFormat(JSContext* cx, Handle<DateTimeFormatObject*> dateTimeFormat)
{
RootedValue value(cx);
@@ -4111,17 +4226,17 @@ GlobalObject::initIntlObject(JSContext*
return false;
// Add the constructor properties, computing and returning the relevant
// prototype objects needed below.
RootedObject collatorProto(cx, CreateCollatorPrototype(cx, intl, global));
if (!collatorProto)
return false;
RootedObject dateTimeFormatProto(cx), dateTimeFormat(cx);
- dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat);
+ dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat, DateTimeFormatOptions::Standard);
if (!dateTimeFormatProto)
return false;
RootedObject numberFormatProto(cx), numberFormat(cx);
numberFormatProto = CreateNumberFormatPrototype(cx, intl, global, &numberFormat);
if (!numberFormatProto)
return false;
// The |Intl| object is fully set up now, so define the global property.
--- a/js/src/builtin/Intl.h
+++ b/js/src/builtin/Intl.h
@@ -407,16 +407,45 @@ intl_defaultTimeZoneOffset(JSContext* cx
* given locale.
*
* Usage: pattern = intl_patternForSkeleton(locale, skeleton)
*/
extern MOZ_MUST_USE bool
intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp);
/**
+ * Return a pattern in the date-time format pattern language of Unicode
+ * Technical Standard 35, Unicode Locale Data Markup Language, for the
+ * best-fit date-time style for the given locale.
+ * The function takes four arguments:
+ *
+ * locale
+ * BCP47 compliant locale string
+ * dateStyle
+ * A string with values: full or long or medium or short, or `undefined`
+ * timeStyle
+ * A string with values: full or long or medium or short, or `undefined`
+ * timeZone
+ * IANA time zone name
+ *
+ * Date and time style categories map to CLDR time/date standard
+ * format patterns.
+ *
+ * For the definition of a pattern string, see LDML 4.8:
+ * http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
+ *
+ * If `undefined` is passed to `dateStyle` or `timeStyle`, the respective
+ * portions of the pattern will not be included in the result.
+ *
+ * Usage: pattern = intl_patternForStyle(locale, dateStyle, timeStyle, timeZone)
+ */
+extern MOZ_MUST_USE bool
+intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp);
+
+/**
* Returns a String value representing x (which must be a Number value)
* according to the effective locale and the formatting options of the
* given DateTimeFormat.
*
* Spec: ECMAScript Internationalization API Specification, 12.3.2.
*
* Usage: formatted = intl_FormatDateTime(dateTimeFormat, x, formatToParts)
*/
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -2192,16 +2192,28 @@ function resolveDateTimeFormatInternals(
//
// formatOpt: // *second* opt computed in InitializeDateTimeFormat
// {
// // all the properties/values listed in Table 3
// // (weekday, era, year, month, day, &c.)
// }
//
// formatMatcher: "basic" / "best fit",
+ //
+ // mozExtensions: true / false,
+ //
+ //
+ // // If mozExtensions is true:
+ //
+ // dateStyle: "full" / "long" / "medium" / "short" / undefined,
+ //
+ // timeStyle: "full" / "long" / "medium" / "short" / undefined,
+ //
+ // patternOption:
+ // String representing LDML Date Format pattern or undefined
// }
//
// Note that lazy data is only installed as a final step of initialization,
// so every DateTimeFormat lazy data object has *all* these properties,
// never a subset of them.
var internalProps = std_Object_create(null);
@@ -2230,17 +2242,36 @@ function resolveDateTimeFormatInternals(
// Steps 15-17.
internalProps.timeZone = lazyDateTimeFormatData.timeZone;
// Step 18.
var formatOpt = lazyDateTimeFormatData.formatOpt;
// Steps 27-28, more or less - see comment after this function.
- var pattern = toBestICUPattern(dataLocale, formatOpt);
+ var pattern;
+ if (lazyDateTimeFormatData.mozExtensions) {
+ if (lazyDateTimeFormatData.patternOption !== undefined) {
+ pattern = lazyDateTimeFormatData.patternOption;
+
+ internalProps.patternOption = lazyDateTimeFormatData.patternOption;
+ } else if (lazyDateTimeFormatData.dateStyle || lazyDateTimeFormatData.timeStyle) {
+ pattern = intl_patternForStyle(dataLocale,
+ lazyDateTimeFormatData.dateStyle, lazyDateTimeFormatData.timeStyle,
+ lazyDateTimeFormatData.timeZone);
+
+ internalProps.dateStyle = lazyDateTimeFormatData.dateStyle;
+ internalProps.timeStyle = lazyDateTimeFormatData.timeStyle;
+ } else {
+ pattern = toBestICUPattern(dataLocale, formatOpt);
+ }
+ internalProps.mozExtensions = true;
+ } else {
+ pattern = toBestICUPattern(dataLocale, formatOpt);
+ }
// Step 29.
internalProps.pattern = pattern;
// Step 30.
internalProps.boundFormat = undefined;
// The caller is responsible for associating |internalProps| with the right
@@ -2296,17 +2327,17 @@ function UnwrapDateTimeFormat(dtf, metho
* This method is complicated a moderate bit by its implementing initialization
* as a *lazy* concept. Everything that must happen now, does -- but we defer
* all the work we can until the object is actually used as a DateTimeFormat.
* This later work occurs in |resolveDateTimeFormatInternals|; steps not noted
* here occur there.
*
* Spec: ECMAScript Internationalization API Specification, 12.1.1.
*/
-function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options) {
+function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options, mozExtensions) {
assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat called with non-Object");
assert(IsDateTimeFormat(dateTimeFormat),
"InitializeDateTimeFormat called with non-DateTimeFormat");
// Steps 1-2 (These steps are no longer required and should be removed
// from the spec; https://github.com/tc39/ecma402/issues/115).
// Lazy DateTimeFormat data has the following structure:
@@ -2373,16 +2404,28 @@ function InitializeDateTimeFormat(dateTi
tz = DefaultTimeZone();
}
lazyDateTimeFormatData.timeZone = tz;
// Step 18.
var formatOpt = new Record();
lazyDateTimeFormatData.formatOpt = formatOpt;
+ lazyDateTimeFormatData.mozExtensions = mozExtensions;
+
+ if (mozExtensions) {
+ let pattern = GetOption(options, "pattern", "string", undefined, undefined);
+ lazyDateTimeFormatData.patternOption = pattern;
+
+ let dateStyle = GetOption(options, "dateStyle", "string", ["full", "long", "medium", "short"], undefined);
+ lazyDateTimeFormatData.dateStyle = dateStyle;
+ let timeStyle = GetOption(options, "timeStyle", "string", ["full", "long", "medium", "short"], undefined);
+ lazyDateTimeFormatData.timeStyle = timeStyle;
+ }
+
// Step 19.
// 12.1, Table 4: Components of date and time formats.
formatOpt.weekday = GetOption(options, "weekday", "string", ["narrow", "short", "long"],
undefined);
formatOpt.era = GetOption(options, "era", "string", ["narrow", "short", "long"], undefined);
formatOpt.year = GetOption(options, "year", "string", ["2-digit", "numeric"], undefined);
formatOpt.month = GetOption(options, "month", "string",
["2-digit", "numeric", "narrow", "short", "long"], undefined);
@@ -2788,18 +2831,28 @@ function Intl_DateTimeFormat_resolvedOpt
var dtf = UnwrapDateTimeFormat(this, "resolvedOptions");
var internals = getDateTimeFormatInternals(dtf);
var result = {
locale: internals.locale,
calendar: internals.calendar,
numberingSystem: internals.numberingSystem,
- timeZone: internals.timeZone
+ timeZone: internals.timeZone,
};
+
+ if (internals.mozExtensions) {
+ if (internals.patternOption !== undefined) {
+ result.pattern = internals.pattern;
+ } else if (internals.dateStyle || internals.timeStyle) {
+ result.dateStyle = internals.dateStyle;
+ result.timeStyle = internals.timeStyle;
+ }
+ }
+
resolveICUPattern(internals.pattern, result);
return result;
}
// Table mapping ICU pattern characters back to the corresponding date-time
// components of DateTimeFormat. See
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2905,16 +2905,31 @@ extern JS_FRIEND_API(JSObject*)
ToWindowIfWindowProxy(JSObject* obj);
// Create and add the Intl.PluralRules constructor function to the provided
// object. This function throws if called more than once per realm/global
// object.
extern bool
AddPluralRulesConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
+// Create and add the Intl.MozDateTimeFormat constructor function to the provided
+// object.
+//
+// This custom date/time formatter constructor gives users the ability
+// to specify a custom format pattern. This pattern is passed *directly*
+// to ICU with NO SYNTAX PARSING OR VALIDATION WHATSOEVER. ICU appears to
+// have a a modicum of testing of this, and it won't fall over completely
+// if passed bad input. But the current behavior is entirely under-specified
+// and emphatically not shippable on the web, and it *must* be fixed before
+// this functionality can be exposed in the real world. (There are also some
+// questions about whether the format exposed here is the *right* one to
+// standardize, that will also need to be resolved to ship this.)
+extern bool
+AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
+
class MOZ_STACK_CLASS JS_FRIEND_API(AutoAssertNoContentJS)
{
public:
explicit AutoAssertNoContentJS(JSContext* cx);
~AutoAssertNoContentJS();
private:
JSContext* context_;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -957,16 +957,19 @@ AddIntlExtras(JSContext* cx, unsigned ar
};
if (!JS_DefineFunctions(cx, intl, funcs))
return false;
if (!js::AddPluralRulesConstructor(cx, intl))
return false;
+ if (!js::AddMozDateTimeFormatConstructor(cx, intl))
+ return false;
+
args.rval().setUndefined();
return true;
}
#endif // ENABLE_INTL_API
static bool
EvalAndPrint(JSContext* cx, const char* bytes, size_t length,
int lineno, bool compileOnly)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/Intl/DateTimeFormat/mozExtensions.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Tests the format function with a diverse set of locales and options.
+// Always use UTC to avoid dependencies on test environment.
+
+let mozIntl = {};
+addIntlExtras(mozIntl);
+
+// Pattern
+
+var dtf = new Intl.DateTimeFormat("en-US", {pattern: "HH:mm MM/dd/YYYY"});
+var mozDtf = new mozIntl.DateTimeFormat("en-US", {pattern: "HH:mm MM/dd/YYYY"});
+
+assertEq(dtf.resolvedOptions().hasOwnProperty('pattern'), false);
+assertEq(mozDtf.resolvedOptions().pattern, "HH:mm MM/dd/YYYY");
+
+// Date style
+
+var dtf = new Intl.DateTimeFormat("en-US", {dateStyle: 'long'});
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('dateStyle'), false);
+
+var mozDtf = new mozIntl.DateTimeFormat("en-US", {dateStyle: 'long'});
+assertEq(mozDtf.resolvedOptions().dateStyle, 'long');
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('year'), true);
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('month'), true);
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('day'), true);
+
+// Time style
+
+var dtf = new Intl.DateTimeFormat("en-US", {timeStyle: 'long'});
+assertEq(dtf.resolvedOptions().hasOwnProperty('dateStyle'), false);
+
+var mozDtf = new mozIntl.DateTimeFormat("en-US", {timeStyle: 'long'});
+assertEq(mozDtf.resolvedOptions().timeStyle, 'long');
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('hour'), true);
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('minute'), true);
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('second'), true);
+
+// Date/Time style
+
+var dtf = new Intl.DateTimeFormat("en-US", {timeStyle: 'medium', dateStyle: 'medium'});
+assertEq(dtf.resolvedOptions().hasOwnProperty('dateStyle'), false);
+assertEq(dtf.resolvedOptions().hasOwnProperty('timeStyle'), false);
+
+var mozDtf = new mozIntl.DateTimeFormat("en-US", {dateStyle: 'medium', timeStyle: 'medium'});
+assertEq(mozDtf.resolvedOptions().timeStyle, 'medium');
+assertEq(mozDtf.resolvedOptions().dateStyle, 'medium');
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('hour'), true);
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('minute'), true);
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('second'), true);
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('year'), true);
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('month'), true);
+assertEq(mozDtf.resolvedOptions().hasOwnProperty('day'), true);
+
+reportCompare(0, 0, 'ok');
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2617,16 +2617,17 @@ static const JSFunctionSpec intrinsic_fu
JS_FN("intl_GetCalendarInfo", intl_GetCalendarInfo, 1,0),
JS_FN("intl_GetLocaleInfo", intl_GetLocaleInfo, 1,0),
JS_FN("intl_ComputeDisplayNames", intl_ComputeDisplayNames, 3,0),
JS_FN("intl_IsValidTimeZoneName", intl_IsValidTimeZoneName, 1,0),
JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0),
JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),
JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0),
JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0),
+ JS_FN("intl_patternForStyle", intl_patternForStyle, 3,0),
JS_FN("intl_PluralRules_availableLocales", intl_PluralRules_availableLocales, 0,0),
JS_FN("intl_GetPluralCategories", intl_GetPluralCategories, 2, 0),
JS_FN("intl_SelectPluralRule", intl_SelectPluralRule, 2,0),
JS_INLINABLE_FN("IsCollator",
intrinsic_IsInstanceOfBuiltin<CollatorObject>, 1,0,
IntlIsCollator),
JS_INLINABLE_FN("IsDateTimeFormat",