Stage 3 Draft / July 21, 2020

dateStyle and timeStyle options for DateTimeFormat

Introduction

This proposal adds two options to Intl.DateTimeFormat: dateStyle and timeStyle. These options give a compact way to request the appropriate, locale-specific way to ask for a date and time of given lengths. See the README for more context.

1 DateTimeFormat Objects

1.1 Abstract Operations For DateTimeFormat Objects

Several DateTimeFormat algorithms use values from the following table, which provides internal slots, property names and allowable values for the components of date and time formats:

Table 1: Components of date and time formats
Internal Slot Property Values
[[Weekday]] "weekday" "narrow", "short", "long"
[[Era]] "era" "narrow", "short", "long"
[[Year]] "year" "2-digit", "numeric"
[[Month]] "month" "2-digit", "numeric", "narrow", "short", "long"
[[Day]] "day" "2-digit", "numeric"
[[Hour]] "hour" "2-digit", "numeric"
[[Minute]] "minute" "2-digit", "numeric"
[[Second]] "second" "2-digit", "numeric"
[[TimeZoneName]] "timeZoneName" "short", "long"

1.1.1 InitializeDateTimeFormat ( dateTimeFormat, locales, options )

The abstract operation InitializeDateTimeFormat accepts the arguments dateTimeFormat (which must be an object), locales, and options. It initializes dateTimeFormat as a DateTimeFormat object. This abstract operation functions as follows:

  1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
  2. Let options be ? ToDateTimeOptions(options, "any", "date").
  3. Let opt be a new Record.
  4. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
  5. Set opt.[[localeMatcher]] to matcher.
  6. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined, undefined).
  7. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11", "h12", "h23", "h24" », undefined).
  8. If hour12 is not undefined, then
    1. Let hourCycle be null.
  9. Set opt.[[hc]] to hourCycle.
  10. Let localeData be %DateTimeFormat%.[[LocaleData]].
  11. Let r be ResolveLocale( %DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]], localeData).
  12. Set dateTimeFormat.[[Locale]] to r.[[locale]].
  13. Set dateTimeFormat.[[Calendar]] to r.[[ca]].
  14. Set dateTimeFormat.[[HourCycle]] to r.[[hc]].
  15. Set dateTimeFormat.[[NumberingSystem]] to r.[[nu]].
  16. Let dataLocale be r.[[dataLocale]].
  17. Let timeZone be ? Get(options, "timeZone").
  18. If timeZone is not undefined, then
    1. Let timeZone be ? ToString(timeZone).
    2. If the result of IsValidTimeZoneName(timeZone) is false, then
      1. Throw a RangeError exception.
    3. Let timeZone be CanonicalizeTimeZoneName(timeZone).
  19. Else,
    1. Let timeZone be DefaultTimeZone().
  20. Set dateTimeFormat.[[TimeZone]] to timeZone.
  21. Let opt be a new Record.
  22. For each row of Table 1, except the header row, do
    1. Let prop be the name given in the Property column of the row.
    2. Let value be ? GetOption(options, prop, "string", « the strings given in the Values column of the row », undefined).
    3. Set opt.[[<prop>]] to value.
  23. Let matcher be ? GetOption(options, "formatMatcher", "string", « "basic", "best fit" », "best fit").
  24. Let dataLocaleData be localeData.[[<dataLocale>]].
  25. Let formats be dataLocaleData.[[formats]].
  26. Let dateStyle be ? GetOption(options, "dateStyle", "string", « "full", "long", "medium", "short" », undefined).
  27. Set dateTimeFormat.[[DateStyle]] to dateStyle.
  28. Let timeStyle be ? GetOption(options, "timeStyle", "string", « "full", "long", "medium", "short" », undefined).
  29. Set dateTimeFormat.[[TimeStyle]] to timeStyle.
  30. If dateStyle is undefined and timeStyle is undefined, then
    1. Let formats be dataLocaleData.[[formats]].
    2. If matcher is "basic", then
      1. Let bestFormat be BasicFormatMatcher(opt, formats).
    3. Else,
      1. Let bestFormat be BestFitFormatMatcher(opt, formats).
  31. Else,
    1. For each row in Table 1, except the header row, do
      1. Let prop be the name given in the Property column of the row.
      2. Let p be opt.[[<prop>]].
      3. If p is not undefined, then
        1. Throw a TypeError exception.
    2. Let bestFormat be DateTimeStyleFormat(dateStyle, timeStyle, dataLocaleData).
  32. For each row in Table 1, except the header row, do
    1. Let prop be the name given in the Property column of the row.
    2. Let p be bestFormat.[[<prop>]].
    3. If p not undefined, then
      1. Set dateTimeFormat's internal slot whose name is the Internal Slot column of the row to p.
  33. If dateTimeFormat.[[Hour]] is not undefined, then
    1. Let hcDefault be dataLocaleData.[[hourCycle]].
    2. Let hc be dateTimeFormat.[[HourCycle]].
    3. If hc is null, then
      1. Set hc to hcDefault.
    4. If hour12 is not undefined, then
      1. If hour12 is true, then
        1. If hcDefault is "h11" or "h23", then
          1. Set hc to "h11".
        2. Else,
          1. Set hc to "h12".
      2. Else,
        1. Assert: hour12 is false.
        2. If hcDefault is "h11" or "h23", then
          1. Set hc to "h23".
        3. Else,
          1. Set hc to "h24".
    5. Set dateTimeFormat.[[HourCycle]] to hc.
    6. If dateTimeformat.[[HourCycle]] is "h11" or "h12", then
      1. Let pattern be bestFormat.[[pattern12]].
    7. Else,
      1. Let pattern be bestFormat.[[pattern]].
  34. Else,
    1. Set dateTimeFormat.[[HourCycle]] to undefined.
    2. Let pattern be bestFormat.[[pattern]].
  35. Set dateTimeFormat.[[Pattern]] to pattern.
  36. Return dateTimeFormat.

1.1.2 ToDateTimeOptions ( options, required, defaults )

When the ToDateTimeOptions abstract operation is called with arguments options, required, and defaults, the following steps are taken:

  1. If options is undefined, let options be null; otherwise let options be ? ToObject(options).
  2. Let options be ObjectCreate(options).
  3. Let needDefaults be true.
  4. If required is "date" or "any", then
    1. For each of the property names "weekday", "year", "month", "day", do
      1. Let prop be the property name.
      2. Let value be ? Get(options, prop).
      3. If value is not undefined, let needDefaults be false.
  5. If required is "time" or "any", then
    1. For each of the property names "hour", "minute", "second", do
      1. Let prop be the property name.
      2. Let value be ? Get(options, prop).
      3. If value is not undefined, let needDefaults be false.
  6. Let dateStyle be ? Get(options, "dateStyle").
  7. Let timeStyle be ? Get(options, "timeStyle").
  8. If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false.
  9. If required is "date" and timeStyle is not undefined,
    1. Throw a TypeError exception.
  10. If required is "time" and dateStyle is not undefined,
    1. Throw a TypeError exception.
  11. If needDefaults is true and defaults is either "date" or "all", then
    1. For each of the property names "year", "month", "day", do
      1. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
  12. If needDefaults is true and defaults is either "time" or "all", then
    1. For each of the property names "hour", "minute", "second", do
      1. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
  13. Return options.

1.1.3 DateTimeStyleFormat ( dateStyle, timeStyle, dataLocaleData )

The DateTimeStyleFormat abstract operation accepts arguments dateStyle and timeStyle, which are each either undefined, "full", "long", "medium", or "short", at least one of which is not undefined, and dataLocaleData, which is a record from %DateTimeFormat%.[[LocaleData]][locale] for some locale locale. It returns the appropriate format record for date time formatting based on the parameters.

  1. If timeStyle is not undefined, then
    1. Assert: timeStyle is one of "full", "long", "medium", or "short".
    2. Let timeFormat be dataLocaleData.[[TimeFormat]].[[<timeStyle>]].
  2. If dateStyle is not undefined, then
    1. Assert: dateStyle is one of "full", "long", "medium", or "short".
    2. Let dateFormat be dataLocaleData.[[DateFormat]].[[<dateStyle>]].
  3. If dateStyle is not undefined and timeStyle is not undefined, then
    1. Let format be a new Record.
    2. Add to format all fields from dateFormat except [[pattern]].
    3. Add to format all fields from timeFormat except [[pattern]] and [[pattern12]], if present.
    4. Let connector be dataLocaleData.[[DateTimeFormat]].[[<dateStyle>]].
    5. Let pattern be the string connector with the substring "{0}" replaced with timeFormat.[[pattern]] and the substring "{1}" replaced with dateFormat.[[pattern]].
    6. Set format.[[pattern]] to pattern.
    7. If timeFormat has a [[pattern12]] field, then
      1. Let pattern12 be the string connector with the substring "{0}" replaced with timeFormat.[[pattern12]] and the substring "{1}" replaced with dateFormat.[[pattern]].
      2. Set format.[[pattern12]] to pattern12.
    8. Return format.
  4. If timeStyle is not undefined, then
    1. Return timeFormat.
  5. Assert: dateStyle is not undefined.
  6. Return dateFormat.

1.2 The Intl.DateTimeFormat Constructor

The Intl.DateTimeFormat constructor is the %DateTimeFormat% intrinsic object and a standard built-in property of the Intl object. Behaviour common to all service constructor properties of the Intl object is specified in 9.1.

1.2.1 Intl.DateTimeFormat ( [ locales [ , options ] ] )

When the Intl.DateTimeFormat function is called with optional arguments locales and options, the following steps are taken:

  1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
  2. Let dateTimeFormat be ? OrdinaryCreateFromConstructor(newTarget, "%DateTimeFormatPrototype%", « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[Weekday]], [[Era]], [[Year]], [[Month]], [[Day]], [[Hour]], [[Minute]], [[Second]], [[TimeZoneName]], [[HourCycle]], [[DateStyle]], [[TimeStyle]], [[Pattern]], [[BoundFormat]] »).
  3. Perform ? InitializeDateTimeFormat(dateTimeFormat, locales, options).
  1. Let this be the this value.
  2. If NewTarget is undefined and ? InstanceofOperator(this, %DateTimeFormat%), then
    1. Perform ? DefinePropertyOrThrow(this, Intl.[[FallbackSymbol]], { [[Value]]: dateTimeFormat, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
    2. Return this.
  1. Return dateTimeFormat.

1.3 Properties of the Intl.DateTimeFormat Constructor

1.3.1 Internal slots

The value of the [[AvailableLocales]] internal slot is implementation defined within the constraints described in 9.1.

The value of the [[RelevantExtensionKeys]] internal slot is « "ca", "nu", "hc" ».

Note 1
Unicode Technical Standard 35 describes three locale extension keys that are relevant to date and time formatting, "ca" for calendar, "tz" for time zone, "hc" for hour cycle, and implicitly "nu" for the numbering system of the number format used for numbers within the date format. DateTimeFormat, however, requires that the time zone is specified through the timeZone property in the options objects.

The value of the [[LocaleData]] internal slot is implementation defined within the constraints described in 9.1 and the following additional constraints:

  • The list that is the value of the "nu" field of any locale field of [[LocaleData]] must not include the values "native", "traditio", or "finance".
  • [[LocaleData]][locale].hc must be « null, "h11", "h12", "h23", "h24" » for all locale values.
  • [[LocaleData]][locale] must have a hourCycle field with a String value equal to "h11", "h12", "h23", or "h24" for all locale values.
  • [[LocaleData]][locale] must have a formats field for all locale values. The value of this field must be a list of records, each of which has a subset of the fields shown in Table 1, where each field must have one of the values specified for the field in Table 1. Multiple records in a list may use the same subset of the fields as long as they have different values for the fields. The following subsets must be available for each locale:
    • weekday, year, month, day, hour, minute, second
    • weekday, year, month, day
    • year, month, day
    • year, month
    • month, day
    • hour, minute, second
    • hour, minute
    Each of the records must also have a pattern field, whose value is a String value that contains for each of the date and time format component fields of the record a substring starting with "{", followed by the name of the field, followed by "}". If the record has an hour field, it must also have a pattern12 field, whose value is a String value that, in addition to the substrings of the pattern field, contains a substring "{ampm}".
  • [[LocaleData]][locale] must contain [[DateFormat]], [[TimeFormat]] and [[DateTimeFormat]] fields, the value of these fields are Records, where each of which has [[full]], [[long]], [[medium]] and [[short]] fields. For [[DateFormat]] and [[TimeFormat]], the value of these fields must be a record, which has a subset of the fields shown in Table 1, where each field must have one of the values specified for the field in Table 1. Each of the records must also have a pattern field, whose value is a String value that contains for each of the date and time format component fields of the record a substring starting with "{", followed by the name of the field, followed by "}". If the record has an hour field, it must also have a pattern12 field, whose value is a String value that, in addition to the substrings of the pattern field, contains a substring "{ampm}". For [[DateTimeFormat]], the field value must be a string pattern which contains the strings "{0}" and "{1}".

EXAMPLE An implementation might include the following record as part of its English locale data: {[[Hour]]: "numeric", [[Minute]]: "2-digit", [[Second]]: "2-digit", [[Pattern]]: "{hour}:{minute}:{second}", [[Pattern12]]: "{hour}:{minute}:{second} {ampm}"}.

Note 2
It is recommended that implementations use the locale data provided by the Common Locale Data Repository (available at http://cldr.unicode.org/).

1.4 Properties of the Intl.DateTimeFormat Prototype Object

1.4.1 Intl.DateTimeFormat.prototype.resolvedOptions ()

This function provides access to the locale and formatting options computed during initialization of the object.

  1. Let dtf be this value.
  2. If Type(dtf) is not Object, throw a TypeError exception.
  3. Let dtf be ? UnwrapDateTimeFormat(dtf).
  4. Let options be ! ObjectCreate(%ObjectPrototype%).
  5. For each row of Table 2, except the header row, in table order, do
    1. Let p be the Property value of the current row.
    2. If p is "hour12", then
      1. Let hc be dtf.[[HourCycle]].
      2. If hc is "h11" or "h12", let v be true.
      3. Else if, hc is "h23" or "h24", let v be false.
      4. Else, let v be undefined.
    3. Else,
      1. Let v be the value of dtf's internal slot whose name is the Internal Slot value of the current row.
    4. If the Internal Slot value of the current row is an Internal Slot value in Table 1, then
      1. If dtf.[[DateStyle]] is not undefined or dtf.[[TimeStyle]] is not undefined, then
        1. Let v be undefined.
    5. If v is not undefined, then
      1. Perform ! CreateDataPropertyOrThrow(options, p, v).
  6. Return options.
Table 2: Resolved Options of DateTimeFormat Instances
Internal Slot Property
[[Locale]] "locale"
[[Calendar]] "calendar"
[[NumberingSystem]] "numberingSystem"
[[TimeZone]] "timeZone"
[[HourCycle]] "hourCycle"
"hour12"
[[Weekday]] "weekday"
[[Era]] "era"
[[Year]] "year"
[[Month]] "month"
[[Day]] "day"
[[Hour]] "hour"
[[Minute]] "minute"
[[Second]] "second"
[[TimeZoneName]] "timeZoneName"
[[DateStyle]] "dateStyle"
[[TimeStyle]] "timeStyle"

For web compatibility reasons, if the property hourCycle is set, the hour12 property should be set to true when hourCycle is "h11" or "h12", or to false when hourCycle is "h23" or "h24".

Note 1
In this version of the ECMAScript 2020 Internationalization API, the timeZone property will be the name of the default time zone if no timeZone property was provided in the options object provided to the Intl.DateTimeFormat constructor. The first edition left the timeZone property undefined in this case.
Note 2
For compatibility with versions prior to the fifth edition, the "hour12" property is set in addition to the "hourCycle" property.

1.5 Properties of Intl.DateTimeFormat Instances

Intl.DateTimeFormat instances inherit properties from %DateTimeFormatPrototype%.

Intl.DateTimeFormat instances have an [[InitializedDateTimeFormat]] internal slot.

Intl.DateTimeFormat instances also have several internal slots that are computed by the constructor:

  • [[Locale]] is a String value with the language tag of the locale whose localization is used for formatting.
  • [[Calendar]] is a String value with the "type" given in Unicode Technical Standard 35 for the calendar used for formatting.
  • [[NumberingSystem]] is a String value with the "type" given in Unicode Technical Standard 35 for the numbering system used for formatting.
  • [[TimeZone]] is a String value with the IANA time zone name of the time zone used for formatting.
  • [[Weekday]], [[Era]], [[Year]], [[Month]], [[Day]], [[Hour]], [[Minute]], [[Second]], [[TimeZoneName]] are each either undefined, indicating that the component is not used for formatting, or one of the String values given in Table 1, indicating how the component should be presented in the formatted output.
  • [[HourCycle]] is a String value indicating whether the 12-hour format ("h11", "h12") or the 24-hour format ("h23", "h24") should be used. "h11" and "h23" start with hour 0 and go up to 11 and 23 respectively. "h12" and "h24" start with hour 1 and go up to 12 and 24. [[HourCycle]] is only used when [[Hour]] is not undefined.
  • [[DateStyle]], [[TimeStyle]] are each either undefined, or a String value with values "full", "long", "medium", or "short".
  • [[Pattern]] is a String value as described in 1.3.1.