Stage 3 Draft / February 2, 2021

Add formatRange and formatRangeToParts to DateTimeFormat

1DateTimeFormat Objects

1.1Abstract 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"
[[DayPeriod]] "dayPeriod" "narrow", "short", "long"
[[Hour]] "hour" "2-digit", "numeric"
[[Minute]] "minute" "2-digit", "numeric"
[[Second]] "second" "2-digit", "numeric"
[[FractionalSecondDigits]] "fractionalSecondDigits" 1, 2, 3
[[TimeZoneName]] "timeZoneName" "short", "long"

1.1.1InitializeDateTimeFormat ( 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:

The following algorithm refers to the type nonterminal from UTS 35's Unicode Locale Identifier grammar.

  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 calendar be ? GetOption(options, "calendar", "string", undefined, undefined).
  7. If calendar is not undefined, then
    1. If calendar does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
  8. Set opt.[[ca]] to calendar.
  9. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined).
  10. If numberingSystem is not undefined, then
    1. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
  11. Set opt.[[nu]] to numberingSystem.
  12. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined, undefined).
  13. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11", "h12", "h23", "h24" », undefined).
  14. If hour12 is not undefined, then
    1. Let hourCycle be null.
  15. Set opt.[[hc]] to hourCycle.
  16. Let localeData be %DateTimeFormat%.[[LocaleData]].
  17. Let r be ResolveLocale(%DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]], localeData).
  18. Set dateTimeFormat.[[Locale]] to r.[[locale]].
  19. Let calendar be r.[[ca]].
  20. Set dateTimeFormat.[[Calendar]] to calendar.
  21. Set dateTimeFormat.[[HourCycle]] to r.[[hc]].
  22. Set dateTimeFormat.[[NumberingSystem]] to r.[[nu]].
  23. Let dataLocale be r.[[dataLocale]].
  24. Let timeZone be ? Get(options, "timeZone").
  25. If timeZone is undefined, then
    1. Let timeZone be DefaultTimeZone().
  26. Else,
    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).
  27. Set dateTimeFormat.[[TimeZone]] to timeZone.
  28. Let opt be a new Record.
  29. For each row of Table 1, except the header row, in table order, do
    1. Let prop be the name given in the Property column of the row.
    2. If prop is "fractionalSecondDigits", then
      1. Let value be ? GetNumberOption(options, "fractionalSecondDigits", 1, 3, undefined).
    3. Else,
      1. Let value be ? GetOption(options, prop, "string", « the strings given in the Values column of the row », undefined).
    4. Set opt.[[<prop>]] to value.
  30. Let dataLocaleData be localeData.[[<dataLocale>]].
  31. Let matcher be ? GetOption(options, "formatMatcher", "string", « "basic", "best fit" », "best fit").
  32. Let dateStyle be ? GetOption(options, "dateStyle", "string", « "full", "long", "medium", "short" », undefined).
  33. Set dateTimeFormat.[[DateStyle]] to dateStyle.
  34. Let timeStyle be ? GetOption(options, "timeStyle", "string", « "full", "long", "medium", "short" », undefined).
  35. Set dateTimeFormat.[[TimeStyle]] to timeStyle.
  36. If dateStyle is not undefined or timeStyle is not undefined, then
    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).
  37. Else,
    1. Let formats be dataLocaleData.[[formats]].[[<calendar>]].
    2. If matcher is "basic", then
      1. Let bestFormat be BasicFormatMatcher(opt, formats).
    3. Else,
      1. Let bestFormat be BestFitFormatMatcher(opt, formats).
  38. For each row in Table 1, except the header row, in table order, 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.
  39. If dateTimeFormat.[[Hour]] is undefined, then
    1. Set dateTimeFormat.[[HourCycle]] to undefined.
    2. Let pattern be bestFormat.[[pattern]].
    3. Let rangePatterns be bestFormat.[[rangePatterns]].
  40. Else,
    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]].
      2. Let rangePatterns be bestFormat.[[rangePatterns12]].
    7. Else,
      1. Let pattern be bestFormat.[[pattern]].
      2. Let rangePatterns be bestFormat.[[rangePatterns]].
  41. Set dateTimeFormat.[[Pattern]] to pattern.
  42. Set dateTimeFormat.[[RangePatterns]] to rangePatterns.
  43. Return dateTimeFormat.

1.1.2ToDateTimeOptions ( 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 "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", 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, then
    1. Throw a TypeError exception.
  10. If required is "time" and dateStyle is not undefined, then
    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.3DateTimeStyleFormat ( 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]] and [[rangePatterns]].
    3. Add to format all fields from timeFormat except [[pattern]], [[rangePatterns]], [[pattern12]] and [[rangePatterns12]], 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. Let dateTimeRangeFormat be dataLocaleData.[[DateTimeRangeFormat]].[[<dateStyle>]].[[<timeStyle>]]
    9. Add to format all fields from dateTimeRangeFormat including [[rangePatterns]] and [[rangePatterns12]], if present.
    10. Return format.
  4. If timeStyle is not undefined, then
    1. Return timeFormat.
  5. Assert: dateStyle is not undefined.
  6. Return dateFormat.

1.1.4BasicFormatMatcher ( options, formats )

When the BasicFormatMatcher abstract operation is called with two arguments options and formats, the following steps are taken:

  1. Let removalPenalty be 120.
  2. Let additionPenalty be 20.
  3. Let longLessPenalty be 8.
  4. Let longMorePenalty be 6.
  5. Let shortLessPenalty be 6.
  6. Let shortMorePenalty be 3.
  7. Let bestScore be -Infinity.
  8. Let bestFormat be undefined.
  9. Assert: Type(formats) is List.
  10. For each element format of formats in List order, do
    1. Let score be 0.
    2. For each property name property shown in Table 1, do
      1. Let optionsProp be options.[[<property>]].
      2. Let formatProp be format.[[<property>]].
      3. If optionsProp is undefined and formatProp is not undefined, decrease score by additionPenalty.
      4. Else if optionsProp is not undefined and formatProp is undefined, decrease score by removalPenalty.
      5. Else if optionsPropformatProp, then
        1. If property is "fractionalSecondDigits", then
          1. Let values be « 1, 2, 3 ».
        2. Else,
          1. Let values be « "2-digit", "numeric", "narrow", "short", "long" ».
        3. Let optionsPropIndex be the index of optionsProp within values.
        4. Let formatPropIndex be the index of formatProp within values.
        5. Let delta be max(min(formatPropIndex - optionsPropIndex, 2), -2).
        6. If delta = 2, decrease score by longMorePenalty.
        7. Else if delta = 1, decrease score by shortMorePenalty.
        8. Else if delta = -1, decrease score by shortLessPenalty.
        9. Else if delta = -2, decrease score by longLessPenalty.
    3. If score > bestScore, then
      1. Let bestScore be score.
      2. Let bestFormat be format.
  11. Return bestFormat.

1.1.5BestFitFormatMatcher ( options, formats )

When the BestFitFormatMatcher abstract operation is called with two arguments options and formats, it performs implementation dependent steps, which should return a set of component representations that a typical user of the selected locale would perceive as at least as good as the one returned by BasicFormatMatcher.

1.1.6DateTime Format Functions

A DateTime format function is an anonymous built-in function that has a [[DateTimeFormat]] internal slot.

When a DateTime format function F is called with optional argument date, the following steps are taken:

  1. Let dtf be F.[[DateTimeFormat]].
  2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]] internal slot.
  3. If date is not provided or is undefined, then
    1. Let x be Call(%Date.now%, undefined).
  4. Else,
    1. Let x be ? ToNumber(date).
  5. Return ? FormatDateTime(dtf, x).

The "length" property of a DateTime format function is 1.

1.1.7FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions )

The FormatDateTimePattern abstract operation is called with arguments dateTimeFormat (which must be an object initialized as a DateTimeFormat), patternParts (which is a list of Records as returned by PartitionPattern), x (which must be a Number value), and rangeFormatOptions (which is a range pattern Record as used in [[rangePattern]] or undefined), interprets x as a time value as specified in ES2021, 20.3.1.1, and creates the corresponding parts according pattern and to the effective locale and the formatting options of dateTimeFormat and rangeFormatOptions. The following steps are taken:

  1. Let x be TimeClip(x).
  2. If x is NaN, throw a RangeError exception.
  3. Let locale be dateTimeFormat.[[Locale]].
  4. Let nfOptions be ObjectCreate(null).
  5. Perform ! CreateDataPropertyOrThrow(nfOptions, "useGrouping", false).
  6. Let nf be ? Construct(%NumberFormat%, « locale, nfOptions »).
  7. Let nf2Options be ObjectCreate(null).
  8. Perform ! CreateDataPropertyOrThrow(nf2Options, "minimumIntegerDigits", 2).
  9. Perform ! CreateDataPropertyOrThrow(nf2Options, "useGrouping", false).
  10. Let nf2 be ? Construct(%NumberFormat%, « locale, nf2Options »).
  11. Let fractionalSecondDigits be dateTimeFormat.[[FractionalSecondDigits]].
  12. If fractionalSecondDigits is not undefined, then
    1. Let nf3Options be ObjectCreate(null).
    2. Perform ! CreateDataPropertyOrThrow(nf3Options, "minimumIntegerDigits", fractionalSecondDigits).
    3. Perform ! CreateDataPropertyOrThrow(nf3Options, "useGrouping", false).
    4. Let nf3 be ? Construct(%NumberFormat%, « locale, nf3Options »).
  13. Let tm be ToLocalTime(x, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
  14. Let result be a new empty List.
  15. Let patternParts be PartitionPattern(dateTimeFormat.[[Pattern]]).
  16. For each element patternPart of patternParts, in List order, do
    1. Let p be patternPart.[[Type]].
    2. If p is "literal", then
      1. Append a new Record { [[Type]]: "literal", [[Value]]: patternPart.[[Value]] } as the last element of the list result.
    3. Else if p is equal to "fractionalSecondDigits", then
      1. Let v be tm.[[Millisecond]].
      2. Let v be floor(v × 10( fractionalSecondDigits - 3 )).
      3. Let fv be FormatNumeric(nf3, v).
      4. Append a new Record { [[Type]]: "fractionalSecond", [[Value]]: fv } as the last element of result.
    4. Else if p matches a Property column of the row in Table 1, then
      1. If rangeFormatOptions is not undefined, let f be the value of rangeFormatOptions's field whose name matches p.
      2. Else, let f be the value of dateTimeFormat's internal slot whose name is the Internal Slot column of the matching row.
      3. Let v be the value of tm's field whose name is the Internal Slot column of the matching row.
      4. If p is "year" and v ≤ 0, let v be 1 - v.
      5. If p is "month", increase v by 1.
      6. If p is "hour" and dateTimeFormat.[[HourCycle]] is "h11" or "h12", then
        1. Let v be v modulo 12.
        2. If v is 0 and dateTimeFormat.[[HourCycle]] is "h12", let v be 12.
      7. If p is "hour" and dateTimeFormat.[[HourCycle]] is "h24", then
        1. If v is 0, let v be 24.
      8. If f is "numeric", then
        1. Let fv be FormatNumeric(nf, v).
      9. Else if f is "2-digit", then
        1. Let fv be FormatNumeric(nf2, v).
        2. If the "length" property of fv is greater than 2, let fv be the substring of fv containing the last two characters.
      10. Else if f is "narrow", "short", or "long", then let fv be a String value representing v in the form given by f; the String value depends upon the implementation and the effective locale and calendar of dateTimeFormat. If p is "month" and rangeFormatOptions is undefined, then the String value may also depend on whether dateTimeFormat.[[Day]] is undefined. If p is "month" and rangeFormatOptions is not undefined, then the String value may also depend on whether rangeFormatOptions.[[day]] is undefined. If p is "timeZoneName", then the String value may also depend on the value of the [[InDST]] field of tm. If p is "era" and rangeFormatOptions is undefined, then the String value may also depend on whether dateTimeFormat.[[Era]] is undefined. If p is "era" and rangeFormatOptions is not undefined, then the String value may also depend on whether rangeFormatOptions.[[era]] is undefined. If the implementation does not have a localized representation of f, then use f itself.
      11. Append a new Record { [[Type]]: p, [[Value]]: fv } as the last element of the list result.
    5. Else if p is equal to "ampm", then
      1. Let v be tm.[[Hour]].
      2. If v is greater than 11, then
        1. Let fv be an implementation and locale dependent String value representing "post meridiem".
      3. Else,
        1. Let fv be an implementation and locale dependent String value representing "ante meridiem".
      4. Append a new Record { [[Type]]: "dayPeriod", [[Value]]: fv } as the last element of the list result.
    6. Else if p is equal to "relatedYear", then
      1. Let v be tm.[[RelatedYear]].
      2. Let fv be FormatNumeric(nf, v).
      3. Append a new Record { [[Type]]: "relatedYear", [[Value]]: fv } as the last element of the list result.
    7. Else if p is equal to "yearName", then
      1. Let v be tm.[[YearName]].
      2. Append a new Record { [[Type]]: "yearName", [[Value]]: v } as the last element of the list result.
    8. Else,
      1. Let unknown be an implementation-, locale-, and numbering system-dependent String based on x and p.
      2. Append a new Record { [[Type]]: "unknown", [[Value]]: unknown } as the last element of result.
  17. Return result.
Note 1
It is recommended that implementations use the locale and calendar dependent strings provided by the Common Locale Data Repository (available at http://cldr.unicode.org), and use CLDR "abbreviated" strings for DateTimeFormat "short" strings, and CLDR "wide" strings for DateTimeFormat "long" strings.
Note 2
It is recommended that implementations use the time zone information of the IANA Time Zone Database.

1.1.8PartitionDateTimePattern ( dateTimeFormat, x )

The PartitionDateTimePattern abstract operation is called with arguments dateTimeFormat (which must be an object initialized as a DateTimeFormat) and x (which must be a Number value), interprets x as a time value as specified in ES2021, 20.3.1.1, and creates the corresponding parts according to the effective locale and the formatting options of dateTimeFormat. The following steps are taken:

  1. Let patternParts be PartitionPattern(dateTimeFormat.[[Pattern]]).
  2. Let result be ? FormatDateTimePattern(dateTimeFormat, patternParts, x, undefined).
  3. Return result.

1.1.9FormatDateTime ( dateTimeFormat, x )

The FormatDateTime abstract operation is called with arguments dateTimeFormat (which must be an object initialized as a DateTimeFormat) and x (which must be a Number value), and performs the following steps:

  1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x).
  2. Let result be the empty String.
  3. For each Record part in parts, do
    1. Set result to the string-concatenation of result and part.[[Value]].
  4. Return result.

1.1.10FormatDateTimeToParts ( dateTimeFormat, x )

The FormatDateTimeToParts abstract operation is called with arguments dateTimeFormat (which must be an object initialized as a DateTimeFormat) and x (which must be a Number value), and performs the following steps:

  1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x).
  2. Let result be ArrayCreate(0).
  3. Let n be 0.
  4. For each Record part in parts, do
    1. Let O be ObjectCreate(%Object.prototype%).
    2. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
    3. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
    4. Perform ! CreateDataProperty(result, ! ToString(n), O).
    5. Increment n by 1.
  5. Return result.

1.1.11PartitionDateTimeRangePattern ( dateTimeFormat, x, y )

The PartitionDateTimeRangePattern abstract operation is called with arguments dateTimeFormat (which must be an object initialized as a DateTimeFormat), x (which must be a Number value) and y (which must be a Number value), interprets x and y as time values as specified in ES2021, 20.3.1.1, and creates the corresponding parts according to the effective locale and the formatting options of dateTimeFormat. The following steps are taken:

  1. Let x be TimeClip(x).
  2. If x is NaN, throw a RangeError exception.
  3. Let y be TimeClip(y).
  4. If y is NaN, throw a RangeError exception.
  5. If x is greater than y, throw a RangeError exception.
  6. Let tm1 be ToLocalTime(x, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
  7. Let tm2 be ToLocalTime(y, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
  8. Let rangePatterns be dateTimeFormat.[[RangePatterns]].
  9. Let rangePattern be undefined.
  10. Let dateFieldsPracticallyEqual be true.
  11. Let patternContainsLargerDateField be false.
  12. While dateFieldsPracticallyEqual is true and patternContainsLargerDateField is false, repeat for each row of Table 3 in order, except the header row
    1. Let fieldName be the name given in the Range Pattern Field column of the row.
    2. If fieldName is equal to [[AmPm]], then
      1. Let rp be rangePatterns.[[AmPm]].
      2. If rangePattern is not undefined and rp is undefined, then
        1. Set patternContainsLargerDateField to true.
      3. Else,
        1. Let v1 be tm1.[[Hour]].
        2. Let v2 be tm2.[[Hour]].
        3. If v1 is greater than 11 and v2 less or equal than 11, or v1 is less or equal than 11 and v2 is greater than 11, then
          1. Set dateFieldsPracticallyEqual to false.
        4. Let rangePattern be rp.
    3. If fieldName is equal to [[FractionalSecondDigits]], then
      1. Let rp be rangePatterns.[[FractionalSecondDigits]].
      2. If rangePattern is not undefined and rp is undefined, then
        1. Set patternContainsLargerDateField to true.
      3. Else,
        1. Let fractionalSecondDigits be dateTimeFormat.[[FractionalSecondDigits]].
        2. If fractionalSecondDigits is undefined, then
          1. Set fractionalSecondDigits to 3.
        3. Let v1 be tm1.[[Millisecond]].
        4. Let v2 be tm2.[[Millisecond]].
        5. Let v1 be floor(v1 × 10( fractionalSecondDigits - 3 )).
        6. Let v2 be floor(v2 × 10( fractionalSecondDigits - 3 )).
        7. If v1 is not equal to v2, then
          1. Set dateFieldsPracticallyEqual to false.
        8. Let rangePattern be rp.
    4. Else,
      1. Let rp be rangePatterns.[[<fieldName>]].
      2. If rangePattern is not undefined and rp is undefined, then
        1. Set patternContainsLargerDateField to true.
      3. Else,
        1. Let v1 be tm1.[[<fieldName>]].
        2. Let v2 be tm2.[[<fieldName>]].
        3. If v1 is not equal to v2, then
          1. Set dateFieldsPracticallyEqual to false.
        4. Let rangePattern be rp.
  13. If dateFieldsPracticallyEqual is true, then
    1. Let pattern be dateTimeFormat.[[Pattern]].
    2. Let patternParts be PartitionPattern(pattern).
    3. Let result be ? FormatDateTimePattern(dateTimeFormat, patternParts, x, undefined).
    4. For each r in result do
      1. Set r.[[Source]] to "shared".
    5. Return result.
  14. Let result be a new empty List.
  15. If rangePattern is undefined, then
    1. Let rangePattern be rangePatterns.[[Default]].
  16. For each rangePatternPart in rangePattern.[[PatternParts]], do
    1. Let pattern be rangePatternPart.[[Pattern]].
    2. Let source be rangePatternPart.[[Source]]
    3. If source is "startRange" or "shared", then
      1. Let z be x.
    4. Else,
      1. Let z be y.
    5. Let patternParts be PartitionPattern(pattern).
    6. Let partResult be ? FormatDateTimePattern(dateTimeFormat, patternParts, z, rangePattern).
    7. For each r in partResult, do
      1. Set r.[[Source]] to source.
    8. Add all elements in partResult to result in order.
  17. Return result.

1.1.12FormatDateTimeRange( dateTimeFormat, x, y )

The FormatDateTimeRange abstract operation is called with arguments dateTimeFormat (which must be an object initialized as a DateTimeFormat), x (which must be a Number value) and y (which must be a Number value), and performs the following steps:

  1. Let parts be ? PartitionDateTimeRangePattern(dateTimeFormat, x, y).
  2. Let result be the empty String.
  3. For each Record part in parts, do
    1. Set result to a String value produced by concatenating result and part.[[Value]].
  4. Return result.

1.1.13FormatDateTimeRangeToParts ( dateTimeFormat, x, y )

The FormatDateTimeRangeToParts abstract operation is called with arguments dateTimeFormat (which must be an object initialized as a DateTimeFormat), x (which must be a Number value) and y (which must be a Number value), and performs the following steps:

  1. Let parts be ? PartitionDateTimeRangePattern(dateTimeFormat, x, y).
  2. Let result be ArrayCreate(0).
  3. Let n be 0.
  4. For each Record part in parts, do
    1. Let O be ObjectCreate(%ObjectPrototype%).
    2. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
    3. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
    4. Perform ! CreateDataPropertyOrThrow(O, "source", part.[[Source]]).
    5. Perform ! CreateDataProperty(result, ! ToString(n), O).
    6. Increment n by 1.
  5. Return result.

1.1.14ToLocalTime ( t, calendar, timeZone )

When the ToLocalTime abstract operation is called with arguments t, calendar, and timeZone, the following steps are taken:

  1. Assert: Type(t) is Number.
  2. If calendar is "gregory", then
    1. Let timeZoneOffset be the value calculated according to LocalTZA(t, true) where the local time zone is replaced with timezone timeZone.
    2. Let tz be the time value t + timeZoneOffset.
    3. Return a record with fields calculated from tz according to Table 2.
  3. Else,
    1. Return a record with the fields of Column 1 of Table 2 calculated from t for the given calendar and timeZone. The calculations should use best available information about the specified calendar and timeZone, including current and historical information about time zone offsets from UTC and daylight saving time rules.
Table 2: Record returned by ToLocalTime
Field Value Calculation for Gregorian Calendar
[[Weekday]] WeekDay(tz) specified in ES2021's Week Day
[[Era]] Let year be YearFromTime(tz) specified in ES2021's Year Number. If year is less than 0, return 'BC', else, return 'AD'.
[[Year]] YearFromTime(tz) specified in ES2021's Year Number
[[RelatedYear]] undefined
[[YearName]] undefined
[[Month]] MonthFromTime(tz) specified in ES2021's Month Number
[[Day]] DateFromTime(tz) specified in ES2021's Date Number
[[Hour]] HourFromTime(tz) specified in ES2021's Hours, Minutes, Second, and Milliseconds
[[Minute]] MinFromTime(tz) specified in ES2021's Hours, Minutes, Second, and Milliseconds
[[Second]] SecFromTime(tz) specified in ES2021's Hours, Minutes, Second, and Milliseconds
[[Millisecond]] msFromTime(tz) specified in ES2021's Hours, Minutes, Second, and Milliseconds
[[InDST]] Calculate true or false using the best available information about the specified calendar and timeZone, including current and historical information about time zone offsets from UTC and daylight saving time rules.
Note
It is recommended that implementations use the time zone information of the IANA Time Zone Database.

1.1.15UnwrapDateTimeFormat ( dtf )

The UnwrapDateTimeFormat abstract operation gets the underlying DateTimeFormat operation for various methods which implement ECMA-402 v1 semantics for supporting initializing existing Intl objects.

  1. Assert: Type(dtf) is Object.
  1. If dtf does not have an [[InitializedDateTimeFormat]] internal slot and ? InstanceofOperator(dtf, %DateTimeFormat%) is true, then
    1. Let dtf be ? Get(dtf, %Intl%.[[FallbackSymbol]]).
  1. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
  2. Return dtf.
Note
See for the motivation of the normative optional text.

1.2The 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 .

1.2.1Intl.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, "%DateTimeFormat.prototype%", « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[Weekday]], [[Era]], [[Year]], [[Month]], [[Day]], [[DayPeriod]], [[Hour]], [[Minute]], [[Second]], [[FractionalSecondDigits]], [[TimeZoneName]], [[HourCycle]], [[Pattern]], [[BoundFormat]] »).
  3. Perform ? InitializeDateTimeFormat(dateTimeFormat, locales, options).
  1. Let this be the this value.
  2. If NewTarget is undefined and Type(this) is Object and ? InstanceofOperator(this, %DateTimeFormat%) is true, then
    1. Perform ? DefinePropertyOrThrow(this, %Intl%.[[FallbackSymbol]], PropertyDescriptor{ [[Value]]: dateTimeFormat, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
    2. Return this.
  1. Return dateTimeFormat.
Note
See for the motivation of the normative optional text.

1.3Properties of the Intl.DateTimeFormat Constructor

The Intl.DateTimeFormat constructor has the following properties:

1.3.1Intl.DateTimeFormat.prototype

The value of Intl.DateTimeFormat.prototype is %DateTimeFormat.prototype%.

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

1.3.2Intl.DateTimeFormat.supportedLocalesOf ( locales [ , options ] )

When the supportedLocalesOf method is called with arguments locales and options, the following steps are taken:

  1. Let availableLocales be %DateTimeFormat%.[[AvailableLocales]].
  2. Let requestedLocales be ? CanonicalizeLocaleList(locales).
  3. Return ? SupportedLocales(availableLocales, requestedLocales, options).

The value of the "length" property of the supportedLocalesOf method is 1.

1.3.3Internal slots

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

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

Note 1
Unicode Technical Standard 35 describes four locale extension keys that are relevant to date and time formatting: "ca" for calendar, "hc" for hour cycle, "nu" for numbering system (of formatted numbers), and "tz" for time zone. 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 and the following additional constraints, for all locale values locale:

  • [[LocaleData]].[[<locale>]].[[nu]] must be a List that does not include the values "native", "traditio", or "finance".
  • [[LocaleData]].[[<locale>]].[[hc]] must be « null, "h11", "h12", "h23", "h24" ».
  • [[LocaleData]].[[<locale>]].[[hourCycle]] must be a String value equal to "h11", "h12", "h23", or "h24".
  • [[LocaleData]].[[<locale>]] must have a [[formats]] field. This formats field must have a [[<calendar>]] field for all calendar values calendar. 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, fractionalSecondDigits
    • weekday, year, month, day, hour, minute, second
    • weekday, year, month, day
    • year, month, day
    • year, month
    • month, day
    • hour, minute, second, fractionalSecondDigits
    • hour, minute, second
    • hour, minute
    • dayPeriod, hour
    • dayPeriod, hour, minute, second
    • dayPeriod, hour, minute
    Each of the records must also have the following fields:
    1. 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 "}".
    2. 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}".
    3. If the record has a [[year]] field, the [[pattern]] and [[pattern12]] values may contain the substrings "{yearName}" and "{relatedYear}"
    4. A [[rangePatterns]] field with a Record value
      • The [[rangePatterns]] record may have any of the fields in Table 3, where each field represents a range pattern and it's value is a Record.
        • The name of the field indicates the largest calendar element that must be different between the start and end dates in order to use this range pattern. For example, if the field name is Month, it contains the range pattern that should be used to format a date range where the Era and Year values are the same, but Month value is different.
        • The record will contain the following fields:
          • A subset of the fields shown in the Property column of Table 1, where each field must have one of the values specified for that field in the Values column of Table 1. Only the fields required to format a date with any of the PatternParts will be added.
          • A [[PatternParts]] field whose value is a list of Records each representing a part of the range pattern. Each record contains a [[Pattern]] field and a [[Source]] field. The [[Pattern]] field's value is a String of the same format as the regular date pattern String. The [[Source]] field must contain one of the following values "shared", "startRange" or "endRange" which indicates which of the range's dates should be formatted using the value of the Pattern field.
      • The [[rangePatterns]] record must have a [[Default]] field which contains the default range pattern used when the specific range pattern is not available. It's value is a list of records with the same structure as the other fields in the [[rangePatterns]] record.
    5. If the record has an hour field, it must also have a [[rangePatterns12]] field. It's value is similar to the Record in [[rangePatterns]], but it uses a String similar to [[pattern12]] for each part of the range pattern.
    6. If the record has a [[year]] field, the [[rangePatterns]] and [[rangePatterns12]] may contain range patterns where the [[Pattern]] values may contain the substrings "{yearName}" and "{relatedYear}"
  • [[LocaleData]][<locale>] must have a [[styles]] field. The styles field must have a [[<calendar>]] field for all calendar values calendar. The field must contain [[DateFormat]], [[TimeFormat]], [[DateTimeFormat]] and [[DateTimeRangeFormat]] 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 the following fields:
    1. 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 "}".
    2. 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}".
    3. A [[rangePatterns]] field that contains a record similar to the one described in the [[formats]] field.
    4. If the record has an hour field, it must also have a [[rangePatterns12]] field. It's value is similar to the record in [[rangePatterns]] but it uses a string similar to [[pattern12]] for each range pattern.
    For [[DateTimeFormat]], the field value must be a string pattern which contains the strings "{0}" and "{1}". For [[DateTimeRangeFormat]] the value of these fields must be a nested record which also has a [[full]], [[long]], [[medium]] and [[short]] fields. The [[full]], [[long]], [[medium]] and [[short]] fields in the enclosing record refer to the date style of the range pattern, while the fields in the nested record refers to the time style of the range pattern. The value of these fields in the nested record is a record with a [[rangePatterns]] field and a [[rangePatterns12]] which are similar to the [[rangePatterns]] and [rangePatterns12]] fields in [[DateFormat]] and [[TimeFormat]].
Note 2
For 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}"}.
  • [[hour]]: "numeric"
  • [[minute]]: "numeric"
  • [[pattern]]: "{hour}:{minute}"
  • [[pattern12]]: "{hour}:{minute} {ampm}"
  • [[rangePatterns]]:
    • [[Hour]]:
      • [[hour]]: "numeric"
      • [[minute]]: "numeric"
      • [[PatternParts]]:
        • {[[Source]]: "startRange", [[Pattern]]: "{hour}:{minute}"}
        • {[[Source]]: "shared", [[Pattern]]: " – "}
        • {[[Source]]: "endRange", [[Pattern]]: "{hour}:{minute}"}
    • [[Minute]]:
      • [[hour]]: "numeric"
      • [[minute]]: "numeric"
      • [[PatternParts]]:
        • {[[Source]]: "startRange", [[Pattern]]: "{hour}:{minute}"}
        • {[[Source]]: "shared", [[Pattern]]: " – "}
        • {[[Source]]: "endRange", [[Pattern]]: "{hour}:{minute}"}
    • [[Default]]:
      • [[year]]: "2-digit"
      • [[month]]: "numeric"
      • [[day]]: "numeric"
      • [[hour]]: "numeric"
      • [[minute]]: "numeric"
      • [[PatternParts]]:
        • {[[Source]]: "startRange", [[Pattern]]: "{day}/{month}/{year}, {hour}:{minute}"}
        • {[[Source]]: "shared", [[Pattern]]: " – "}
        • {[[Source]]: "endRange", [[Pattern]]: "{day}/{month}/{year}, {hour}:{minute}"}
  • [[rangePatterns12]]:
    • [[Hour]]:
      • [[hour]]: "numeric"
      • [[minute]]: "numeric"
      • [[PatternParts]]:
        • {[[Source]]: "startRange", [[Pattern]]: "{hour}:{minute}"}
        • {[[Source]]: "shared", [[Pattern]]: " – "}
        • {[[Source]]: "endRange", [[Pattern]]: "{hour}:{minute}"}
        • {[[Source]]: "shared", [[Pattern]]: " {ampm}"}
    • [[Minute]]:
      • [[hour]]: "numeric"
      • [[minute]]: "numeric"
      • [[PatternParts]]:
        • {[[Source]]: "startRange", [[Pattern]]: "{hour}:{minute}"}
        • {[[Source]]: "shared", [[Pattern]]: " – "}
        • {[[Source]]: "endRange", [[Pattern]]: "{hour}:{minute}"}
        • {[[Source]]: "shared", [[Pattern]]: " {ampm}"}
    • [[Default]]:
      • [[year]]: "2-digit"
      • [[month]]: "numeric"
      • [[day]]: "numeric"
      • [[hour]]: "numeric"
      • [[minute]]: "numeric"
      • [[PatternParts]]:
        • {[[Source]]: "startRange", [[Pattern]]: "{day}/{month}/{year}, {hour}:{minute} {ampm}"}
        • {[[Source]]: "shared", [[Pattern]]: " – "}
        • {[[Source]]: "endRange", [[Pattern]]: "{day}/{month}/{year}, {hour}:{minute} {ampm}"}
Note 3
It is recommended that implementations use the locale data provided by the Common Locale Data Repository (available at http://cldr.unicode.org).
Table 3: Range pattern fields
Range Pattern Field Pattern String Field
[[Era]] "era"
[[Year]] "year"
[[Month]] "month"
[[Day]] "day"
[[AmPm]] "ampm"
[[Hour]] "hour"
[[Minute]] "minute"
[[Second]] "second"
[[FractionalSecondDigits]] "fractionalSecondDigits"

1.4Properties of the Intl.DateTimeFormat Prototype Object

The Intl.DateTimeFormat prototype object is itself an ordinary object. %DateTimeFormat.prototype% is not an Intl.DateTimeFormat instance and does not have an [[InitializedDateTimeFormat]] internal slot or any of the other internal slots of Intl.DateTimeFormat instance objects.

1.4.1Intl.DateTimeFormat.prototype.constructor

The initial value of Intl.DateTimeFormat.prototype.constructor is the intrinsic object %DateTimeFormat%.

1.4.2Intl.DateTimeFormat.prototype [ @@toStringTag ]

The initial value of the @@toStringTag property is the String value "Intl.DateTimeFormat".

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

1.4.3get Intl.DateTimeFormat.prototype.format

Intl.DateTimeFormat.prototype.format is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let dtf be the this value.
  2. If Type(dtf) is not Object, throw a TypeError exception.
  3. Let dtf be ? UnwrapDateTimeFormat(dtf).
  4. If dtf.[[BoundFormat]] is undefined, then
    1. Let F be a new built-in function object as defined in DateTime Format Functions (1.1.6).
    2. Set F.[[DateTimeFormat]] to dtf.
    3. Set dtf.[[BoundFormat]] to F.
  5. Return dtf.[[BoundFormat]].
Note
The returned function is bound to dtf so that it can be passed directly to Array.prototype.map or other functions. This is considered a historical artefact, as part of a convention which is no longer followed for new features, but is preserved to maintain compatibility with existing programs.

1.4.4Intl.DateTimeFormat.prototype.formatToParts ( date )

When the formatToParts method is called with an argument date, the following steps are taken:

  1. Let dtf be the this value.
  2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
  3. If date is undefined, then
    1. Let x be Call(%Date.now%, undefined).
  4. Else,
    1. Let x be ? ToNumber(date).
  5. Return ? FormatDateTimeToParts(dtf, x).

1.4.5Intl.DateTimeFormat.prototype.formatRange ( startDate, endDate )

When the formatRange method is called with an arguments startDate and endDate, the following steps are taken:

  1. Let dtf be this value.
  2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
  3. If startDate is undefined or endDate is undefined, throw a TypeError exception.
  4. Let x be ? ToNumber(startDate).
  5. Let y be ? ToNumber(endDate).
  6. Return ? FormatDateTimeRange(dtf, x, y).

1.4.6Intl.DateTimeFormat.prototype.formatRangeToParts ( startDate, endDate )

When the formatRangeToParts method is called with an arguments startDate and endDate, the following steps are taken:

  1. Let dtf be this value.
  2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
  3. If startDate is undefined or endDate is undefined, throw a TypeError exception.
  4. Let x be ? ToNumber(startDate).
  5. Let y be ? ToNumber(endDate).
  6. Return ? FormatDateTimeRangeToParts(dtf, x, y).

1.4.7Intl.DateTimeFormat.prototype.resolvedOptions ( )

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

  1. Let dtf be the this value.
  2. If Type(dtf) is not Object, throw a TypeError exception.
  3. Let dtf be ? UnwrapDateTimeFormat(dtf).
  4. Let options be ! ObjectCreate(%Object.prototype%).
  5. For each row of Table 4, 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 4: 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"
[[DayPeriod]] "dayPeriod"
[[Hour]] "hour"
[[Minute]] "minute"
[[Second]] "second"
[[FractionalSecondDigits]] "fractionalSecondDigits"
[[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 2021 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.5Properties of Intl.DateTimeFormat Instances

Intl.DateTimeFormat instances are ordinary objects that inherit properties from %DateTimeFormat.prototype%.

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]], [[DayPeriod]], [[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.
  • [[FractionalSecondDigits]] is either undefined or a positive, non-negative integer Number value indicating the fraction digits to be used for fractional seconds. Numbers will be rounded or padded with trailing zeroes if necessary.
  • [[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.3.
  • [[RangePatterns]] is a Record as described in 1.3.3.

Finally, Intl.DateTimeFormat instances have a [[BoundFormat]] internal slot that caches the function returned by the format accessor (1.4.3).

ACopyright & Software License

Copyright Notice

© 2021 Google, Ecma International

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.