Stage 3 Draft / August 3, 2022

Intl.DurationFormat

1 DurationFormat Objects

1.1 Abstract Operations for DurationFormat Objects

Several DurationFormat algorithms use values from the following table, which provides internal slots and property names for Duration instances, and which is also used to key Records of templates to format individual Duration components:

Table 1: Components of Duration Instances
Value Slot Style Slot Display Slot Unit Values Digital Default
[[Years]] [[YearsStyle]] [[YearsDisplay]] "years" « "long", "short", "narrow" » "narrow"
[[Months]] [[MonthsStyle]] [[MonthsDisplay]] "months" « "long", "short", "narrow" » "narrow"
[[Weeks]] [[WeeksStyle]] [[WeeksDisplay]] "weeks" « "long", "short", "narrow" » "narrow"
[[Days]] [[DaysStyle]] [[DaysDisplay]] "days" « "long", "short", "narrow" » "narrow"
[[Hours]] [[HoursStyle]] [[HoursDisplay]] "hours" « "long", "short", "narrow", "numeric", "2-digit" » "numeric"
[[Minutes]] [[MinutesStyle]] [[MinutesDisplay]] "minutes" « "long", "short", "narrow", "numeric", "2-digit" » "numeric"
[[Seconds]] [[SecondsStyle]] [[SecondsDisplay]] "seconds" « "long", "short", "narrow", "numeric", "2-digit" » "numeric"
[[Milliseconds]] [[MillisecondsStyle]] [[MillisecondsDisplay]] "milliseconds" « "long", "short", "narrow", "numeric" » "numeric"
[[Microseconds]] [[MicrosecondsStyle]] [[MicrosecondsDisplay]] "microseconds" « "long", "short", "narrow", "numeric" » "numeric"
[[Nanoseconds]] [[NanosecondsStyle]] [[NanosecondsDisplay]] "nanoseconds" « "long", "short", "narrow", "numeric" » "numeric"

1.1.1 Duration Records

A Duration Record is a Record value used to represent a Duration.

Duration Records have the fields listed in the Value Slot column of Table 1

1.1.2 ToIntegerWithoutRounding ( argument )

The abstract operation ToIntegerWithoutRounding takes argument argument (an ECMAScript language value). It converts argument to an integer, throwing if doing so would require rounding. It performs the following steps when called:

  1. Let number be ? ToNumber(argument).
  2. If number is NaN, +0𝔽, or -0𝔽, return 0.
  3. If IsIntegralNumber(number) is false, throw a RangeError exception.
  4. Return (number).

1.1.3 ToDurationRecord ( input )

The abstract operation ToDurationRecord takes argument input (an ECMAScript language value) and returns either a normal completion containing a Duration Record, or an abrupt completion. It converts a given object that represents a Duration into a Duration Record. It performs the following steps when called:

  1. If Type(input) is not Object, throw a TypeError exception.
  2. Let result be a new Duration Record with each field set to 0.
  3. Let any be false.
  4. For each row of Table 1, except the header row, in table order, do
    1. Let valueSlot be the Value Slot value of the current row.
    2. Let unit be the Unit value of the current row.
    3. Let value be ? Get(input, unit).
    4. If value is not undefined, then
      1. Set any to true.
      2. Set value to ? ToIntegerWithoutRounding(value).
      3. Set result.[[<valueSlot>]] to value.
  5. If any is false, throw a TypeError exception.
  6. Return result.

1.1.4 DurationRecordSign ( record )

The abstract operation DurationRecordSign takes argument record (a Duration Record) and returns an integer. It returns 1 if the most significant non-zero field in record is positive, and -1 if the most significant non-zero field is negative. If all the fields are zero, it returns 0. It performs the following steps when called:

  1. For each row of Table 1, except the header row, in table order, do
    1. Let valueSlot be the Value Slot value of the current row.
    2. Let v be record.[[<valueSlot>]].
    3. If v < 0, return -1.
    4. If v > 0, return 1.
  2. Return 0.

1.1.5 IsValidDurationRecord ( record )

The abstract operation IsValidDurationRecord takes argument record (a Duration Record). It returns true if record represents a valid duration, and false otherwise. It performs the following steps when called:

  1. Let sign be ! DurationRecordSign(record).
  2. For each row of Table 1, except the header row, in table order, do
    1. Let valueSlot be the Value Slot value of the current row.
    2. Let v be record.[[<valueSlot>]].
    3. Assert: 𝔽(v) is finite.
    4. If v < 0 and sign > 0, return false.
    5. If v > 0 and sign < 0, return false.
  3. Return true.

1.1.6 GetDurationUnitOptions ( unit, options, baseStyle, stylesList, digitalBase, prevStyle )

The abstract operation GetDurationUnitOptions takes arguments unit (a String), options (an ECMAScript language value), baseStyle (a String), stylesList (a List of String), digitalBase (a String), and prevStyle (a String) and returns a Record with [[Style]] and [[Display]] fields. It extracts the relevant options for any given unit from an options bag and returns them as a Record. It performs the following steps when called:

  1. Let style be ? GetOption(options, unit, "string", stylesList, undefined).
  2. Let displayDefault be "always".
  3. If style is undefined, then
    1. Set displayDefault to "auto".
    2. If baseStyle is "digital", then
      1. Set style to digitalBase.
    3. Else if prevStyle is "numeric" or "2-digit", then
      1. Set style to "numeric".
    4. Else,
      1. Set style to baseStyle.
  4. Let displayField be the string-concatenation of unit and "Display".
  5. Let display be ? GetOption(options, displayField, "string", « "auto", "always" », displayDefault).
  6. If prevStyle is "numeric" or "2-digit", then
    1. If style is not "numeric" or "2-digit", then
      1. Throw a RangeError exception.
    2. Else if unit is "minutes" or "seconds", then
      1. Set style to "2-digit".
  7. Return the Record { [[Style]]: style, [[Display]]: display }.

1.1.7 PartitionDurationFormatPattern ( durationFormat, duration )

The abstract operation PartitionDurationFormatPattern takes arguments durationFormat (a DurationFormat) and duration (a Duration Record) and returns a List. It creates and returns a List with all the corresponding parts according to the effective locale and the formatting options of durationFormat. It performs the following steps when called:

  1. Let result be a new empty List.
  2. Let done be false.
  3. While done is false, repeat for each row in Table 1 in order, except the header row:
    1. Let styleSlot be the Style Slot value.
    2. Let displaySlot be the Display Slot value.
    3. Let valueSlot be the Value Slot value.
    4. Let unit be the Unit value.
    5. Let style be durationFormat.[[<styleSlot>]].
    6. Let display be durationFormat.[[<displaySlot>]].
    7. Let value be duration.[[<valueSlot>]].
    8. Let nfOpts be ! OrdinaryObjectCreate(null).
    9. If unit is "seconds", "milliseconds", or "microseconds", then
      1. If unit is "seconds", then
        1. Let nextStyle be durationFormat.[[MillisecondsStyle]].
      2. Else if unit is "milliseconds", then
        1. Let nextStyle be durationFormat.[[MicrosecondsStyle]].
      3. Else,
        1. Let nextStyle be durationFormat.[[NanosecondsStyle]].
      4. If nextStyle is "numeric", then
        1. If unit is "seconds", then
          1. Set value to value + duration.[[Milliseconds]] / 103 + duration.[[Microseconds]] / 106 + duration.[[Nanoseconds]] / 109.
        2. Else if unit is "milliseconds", then
          1. Set value to value + duration.[[Microseconds]] / 103 + duration.[[Nanoseconds]] / 106.
        3. Else,
          1. Set value to value + duration.[[Nanoseconds]] / 103.
        4. Perform ! CreateDataPropertyOrThrow(nfOpts, "maximumFractionDigits", durationFormat.[[FractionalDigits]]).
        5. Perform ! CreateDataPropertyOrThrow(nfOpts, "minimumFractionDigits", durationFormat.[[FractionalDigits]]).
        6. Set done to true.
    10. If style is "2-digit", then
      1. Perform ! CreateDataPropertyOrThrow(nfOpts, "minimumIntegerDigits", 2𝔽).
    11. If value is not 0 or display is not "auto", then
      1. Let nf be ! Construct(%NumberFormat%, « durationFormat.[[Locale]], nfOpts »).
      2. Let dataLocale be durationFormat.[[DataLocale]].
      3. Let dataLocaleData be %DurationFormat%.[[LocaleData]].[[<dataLocale>]].
      4. If style is "2-digit" or "numeric", then
        1. Let num be ! FormatNumeric(nf, 𝔽(value)).
        2. Append the new Record { [[Type]]: unit, [[Value]]: num} to the end of result.
        3. If unit is "hours" or "minutes", then
          1. If unit is "hours", then
            1. Let nextValue be duration.[[Minutes]].
            2. Let nextDisplay be durationFormat.[[MinutesDisplay]].
          2. Else,
            1. Let nextValue be duration.[[Seconds]].
            2. Let nextDisplay be durationFormat.[[SecondsDisplay]].
            3. If durationFormat.[[MillisecondsStyle]] is "numeric", then
              1. Set nextValue to nextValue + duration.[[Milliseconds]] / 103 + duration.[[Microseconds]] / 106 + duration.[[Nanoseconds]] / 109.
          3. If nextValue is not 0 or nextDisplay is not "auto", then
            1. Let separator be dataLocaleData.[[formats]].[[digital]].[[separator]].
            2. Append the new Record { [[Type]]: "literal", [[Value]]: separator} to the end of result.
      5. Else,
        1. Let num be ! PartitionNumberPattern(nf, 𝔽(value)).
        2. Let pr be ! Construct(%PluralRules%, « durationFormat.[[Locale]] »).
        3. Let prv be ! ResolvePlural(pr, 𝔽(value)).
        4. Let template be dataLocaleData.[[formats]].[[<style>]].[[<unit>]].[[<prv>]].
        5. Let parts be ! MakePartsList(template, unit, num).
        6. Let concat be an empty String.
        7. For each Record { [[Type]], [[Value]], [[Unit]] } part in parts, do
          1. Set concat to the string-concatenation of concat and part.[[Value]].
        8. Append the new Record { [[Type]]: unit, [[Value]]: concat } to the end of result.
  4. Let lf be ! Construct(%ListFormat%, « durationFormat.[[Locale]] »).
  5. Set result to ! CreatePartsFromList(lf, result).
  6. Return result.
Editor's Note
CreatePartsFromList will be changed to accept a List of Records in the form {[[Type]], [[Value]]}.
Editor's Note
Adding the time separator as a separate list element is wrong, because it will be formatted as a single entry through the list formatter resulting in strings like "12, :, 30, :, and 45".

1.2 The Intl.DurationFormat Constructor

The DurationFormat constructor is the %DurationFormat% 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.DurationFormat ( [ locales [ , options ] ] )

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

  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let durationFormat be ? OrdinaryCreateFromConstructor(NewTarget, "%DurationFormatPrototype%", « [[InitializedDurationFormat]], [[Locale]], [[DataLocale]], [[NumberingSystem]], [[Style]], [[YearsStyle]], [[YearsDisplay]], [[MonthsStyle]], [[MonthsDisplay]] , [[WeeksStyle]], [[WeeksDisplay]] , [[DaysStyle]], [[DaysDisplay]] , [[HoursStyle]], [[HoursDisplay]] , [[MinutesStyle]], [[MinutesDisplay]] , [[SecondsStyle]], [[SecondsDisplay]] , [[MillisecondsStyle]], [[MillisecondsDisplay]] , [[MicrosecondsStyle]], [[MicrosecondsDisplay]] , [[NanosecondsStyle]], [[NanosecondsDisplay]], [[FractionalDigits]] »).
  3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
  4. Let options be ? GetOptionsObject(options).
  5. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
  6. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined).
  7. If numberingSystem is not *undefined, then
    1. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
  8. Let opt be the Record { [[localeMatcher]]: matcher, [[nu]]: numberingSystem }.
  9. Let r be ResolveLocale(%DurationFormat%.[[AvailableLocales]], requestedLocales, opt, %DurationFormat%.[[RelevantExtensionKeys]], %DurationFormat%.[[LocaleData]]).
  10. Let locale be r.[[locale]].
  11. Set durationFormat.[[Locale]] to locale.
  12. Set durationFormat.[[NumberingSystem]] to r.[[nu]].
  13. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow", "digital" », "long").
  14. Set durationFormat.[[Style]] to style.
  15. Set durationFormat.[[DataLocale]] to r.[[dataLocale]].
  16. Let prevStyle be the empty String.
  17. For each row of Table 1, except the header row, in table order, do
    1. Let styleSlot be the Style Slot value of the current row.
    2. Let displaySlot be the Display Slot value of the current row.
    3. Let unit be the Unit value.
    4. Let valueList be the Values value.
    5. Let digitalBase be the Digital Default value.
    6. Let unitOptions be ? GetDurationUnitOptions(unit, options, style, valueList, digitalBase, prevStyle).
    7. Set the value of the styleSlot slot of durationFormat to unitOptions.[[Style]].
    8. Set the value of the displaySlot slot of durationFormat to unitOptions.[[Display]].
    9. If unit is one of "hours", "minutes", "seconds", "milliseconds", or "microseconds", then
      1. Set prevStyle to unitOptions.[[Style]].
  18. Set durationFormat.[[FractionalDigits]] to ? GetNumberOption(options, "fractionalDigits", 0, 9, undefined).
  19. Return durationFormat.

1.3 Properties of the Intl.DurationFormat Constructor

The Intl.DurationFormat constructor has the following properties:

1.3.1 Intl.DurationFormat.prototype

The value of Intl.DurationFormat.prototype is %DurationFormatPrototype%.

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

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

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

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

1.3.3 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 « "nu" ».

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

  • [[LocaleData]].[[<locale>]].[[nu]] must be a List as specified in 12.3.3.
  • [[LocaleData]].[[<locale>]] must have a [[formats]] field. The value of this field must be a record, which must have fields with the names of the four formatting styles: long, short, and narrow. Each of first three fields must be records themselves, and each must have fields with the names of the supported units: nanoseconds, microseconds, milliseconds, seconds, minutes, hours, days, weeks, months, and years, which are Records with a field for each of the plural categories relevant for locale; the value corresponding to those fields is a pattern which may contain "{0}" to be replaced by a formatted number. The value of the fourth field, corresponding to the digital style, on the other hand, must be a pattern; i.e. a String that contains for an arbitrary subset of the Duration component fields a substring starting with "{", followed by the name of the field, followed by "}".
  • [[LocaleData]].[[<locale>]].[[formats]] should have an additional [[digital]] field which is a record with just one field, [[separator]], which contains the appropriate time unit separator for that locale.
Note
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.DurationFormat Prototype Object

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

1.4.1 Intl.DurationFormat.prototype.constructor

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

1.4.2 Intl.DurationFormat.prototype [ @@toStringTag ]

The initial value of the @@toStringTag property is the string value "Intl.DurationFormat".

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

1.4.3 Intl.DurationFormat.prototype.format ( duration )

When the format method is called with an argument duration, the following steps are taken:

  1. Let df be this value.
  2. Perform ? RequireInternalSlot(df, [[InitializedDurationFormat]]).
  3. Let record be ? ToDurationRecord(duration).
  4. If IsValidDurationRecord(record) is false, throw a RangeError exception.
  5. Let parts be ! PartitionDurationFormatPattern(df, record).
  6. Let result be a new empty String.
  7. For each Record { [[Type]], [[Value]] } part in parts, do
    1. Set result to the string-concatenation of result and part.[[Value]].
  8. Return result.

1.4.4 Intl.DurationFormat.prototype.formatToParts ( duration )

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

  1. Let df be this value.
  2. Perform ? RequireInternalSlot(df, [[InitializedDurationFormat]]).
  3. Let record be ? ToDurationRecord(duration).
  4. If IsValidDurationRecord(record) is false, throw a RangeError exception.
  5. Let parts be ! PartitionDurationFormatPattern(df, record).
  6. Let result be ! ArrayCreate(0).
  7. Let n be 0.
  8. For each { [[Type]], [[Value]] } part in parts, do
    1. Let obj be ! OrdinaryObjectCreate(%ObjectPrototype%).
    2. Perform ! CreateDataPropertyOrThrow(obj, "type", part.[[Type]]).
    3. Perform ! CreateDataPropertyOrThrow(obj, "value", part.[[Value]]).
    4. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), obj).
    5. Increment n by 1.
  9. Return result.

1.4.5 Intl.DurationFormat.prototype.resolvedOptions ( )

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

  1. Let df be the this value.
  2. Perform ? RequireInternalSlot(df, [[InitializedDurationFormat]]).
  3. Let options be ! OrdinaryObjectCreate(%ObjectPrototype%).
  4. 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. Let v be the value of df's internal slot whose name is the Internal Slot value of the current row.
    3. Assert: v is not undefined.
    4. Perform ! CreateDataPropertyOrThrow(options, p, v).
  5. Return options.
Table 2: Resolved Options of DurationFormat Instances
Internal Slot Property
[[Locale]] "locale"
[[Style]] "style"
[[YearsStyle]] "years"
[[YearsDisplay]] "yearsDisplay"
[[MonthsStyle]] "months"
[[MonthsDisplay]] "monthsDisplay"
[[WeeksStyle]] "weeks"
[[WeeksDisplay]] "weeksDisplay"
[[DaysStyle]] "days"
[[DaysDisplay]] "daysDisplay"
[[HoursStyle]] "hours"
[[HoursDisplay]] "hoursDisplay"
[[MinutesStyle]] "minutes"
[[MinutesDisplay]] "minutesDisplay"
[[SecondsStyle]] "seconds"
[[SecondsDisplay]] "secondsDisplay"
[[MillisecondsStyle]] "milliseconds"
[[MillisecondsDisplay]] "millisecondsDisplay"
[[MicrosecondsStyle]] "microseconds"
[[MicrosecondsDisplay]] "microsecondsDisplay"
[[NanosecondsStyle]] "nanoseconds"
[[NanosecondsDisplay]] "nanosecondsDisplay"
[[FractionalDigits]] "fractionalDigits"
[[NumberingSystem]] "numberingSystem"

1.5 Properties of Intl.DurationFormat Instances

Intl.DurationFormat instances inherit properties from %DurationFormatPrototype%.

Intl.DurationFormat instances have an [[InitializedDurationFormat]] internal slot.

Intl.DurationFormat 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.
  • [[DataLocale]] is a String value with the language tag of the nearest locale for which the implementation has data to perform the formatting operation. It will be a parent locale of [[Locale]].
  • [[NumberingSystem]] is a String value with the "type" given in Unicode Technical Standard 35 for the numbering system used for formatting.
  • [[Style]] is one of the String values "long", "short", "narrow", or "digital" identifying the duration formatting style used.
  • [[YearsStyle]] is one of the String values "long", "short", or "narrow" identifying the formatting style used for the years field.
  • [[YearsDisplay]] is one of the String values "auto" or "always" identifying when to display the years field.
  • [[MonthsStyle]] is one of the String values "long", "short", or "narrow" identifying the formatting style used for the months field.
  • [[MonthsDisplay]] is one of the String values "auto" or "always" identifying when to display the months field.
  • [[WeeksStyle]] is one of the String values "long", "short", or "narrow" identifying the formatting style used for the weeks field.
  • [[WeeksDisplay]] is one of the String values "auto" or "always" identifying when to display the weeks field.
  • [[DaysStyle]] is one of the String values "long", "short", or "narrow" identifying the formatting style used for the days field.
  • [[DaysDisplay]] is one of the String values "auto" or "always" identifying when to display the days field.
  • [[HoursStyle]] is one of the String values "long", "short", "narrow", "2-digit", or "numeric" identifying the formatting style used for the hours field.
  • [[HoursDisplay]] is one of the String values "auto" or "always" identifying when to display the hours field.
  • [[MinutesStyle]] is one of the String values "long", "short", "narrow", "2-digit", or "numeric" identifying the formatting style used for the minutes field.
  • [[MinutesDisplay]] is one of the String values "auto" or "always" identifying when to display the minutes field.
  • [[SecondsStyle]] is one of the String values "long", "short", "narrow", "2-digit", or "numeric" identifying the formatting style used for the seconds field.
  • [[SecondsDisplay]] is one of the String values "auto" or "always" identifying when to display the seconds field.
  • [[MillisecondsStyle]] is one of the String values "long", "short", "narrow", or "numeric" identifying the formatting style used for the milliseconds field.
  • [[MillisecondsDisplay]] is one of the String values "auto" or "always" identifying when to display the milliseconds field.
  • [[MicrosecondsStyle]] is one of the String values "long", "short", "narrow", or "numeric" identifying the formatting style used for the microseconds field.
  • [[MicrosecondsDisplay]] is one of the String values "auto" or "always" identifying when to display the microseconds field.
  • [[NanosecondsStyle]] is one of the String values "long", "short", "narrow", or "numeric" identifying the formatting style used for the nanoseconds field.
  • [[NanosecondsDisplay]] is one of the String values "auto" or "always" identifying when to display the nanoseconds field.
  • [[FractionalDigits]] is a Number value, identifying the number of fractional digits to be used with numeric styles.

A Copyright & Software License

Copyright Notice

© 2022 Ujjwal Sharma, Younies Mahmoud

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.