From f62aa26bd51d09e2e313e2a9c239c8ad884229d3 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 3 Oct 2024 17:11:54 -0700 Subject: [PATCH] Avoid formatting irrelevant parts of dateStyle/timeStyle for Temporal types timeStyle: 'long' and timeStyle: 'full' include the time zone name for legacy Date formatting. However, PlainTime and PlainDateTime don't include a time zone in their data model, so they shouldn't format a time zone name when using timeStyle. Similarly, PlainYearMonth and PlainMonthDay shouldn't format a day or year respectively when using dateStyle. This was already the consensus but somewhere along the line the spec text got out of sync, probably in a rebase on latest ECMA-402. In the polyfill, we already handled dateStyle for PlainYearMonth and PlainMonthDay. Try to hack around this the same way for timeStyle. Closes: #2795 --- polyfill/lib/intl.mjs | 10 ++++++++++ spec/intl.html | 42 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/polyfill/lib/intl.mjs b/polyfill/lib/intl.mjs index f4760c3d2..4fff61806 100644 --- a/polyfill/lib/intl.mjs +++ b/polyfill/lib/intl.mjs @@ -326,6 +326,11 @@ function timeAmend(originalOptions) { timeZoneName: false, dateStyle: false }); + if (options.timeStyle === 'long' || options.timeStyle === 'full') { + // Try to fake what timeStyle should do if not printing the time zone name + delete options.timeStyle; + ObjectAssign(options, { hour: 'numeric', minute: '2-digit', second: '2-digit' }); + } if (!hasTimeOptions(options)) { if (hasAnyDateTimeOptions(originalOptions)) { throw new TypeError(`cannot format Temporal.PlainTime with options [${ObjectKeys(originalOptions)}]`); @@ -428,6 +433,11 @@ function dateAmend(originalOptions) { function datetimeAmend(originalOptions) { const options = amend(originalOptions, { timeZoneName: false }); + if (options.timeStyle === 'long' || options.timeStyle === 'full') { + // Try to fake what timeStyle should do if not printing the time zone name + delete options.timeStyle; + ObjectAssign(options, { hour: 'numeric', minute: '2-digit', second: '2-digit' }); + } if (!hasTimeOptions(options) && !hasDateOptions(options)) { if (hasAnyDateTimeOptions(originalOptions)) { throw new TypeError(`cannot format PlainDateTime with options [${ObjectKeys(originalOptions)}]`); diff --git a/spec/intl.html b/spec/intl.html index ef93e2bcc..4cca78064 100644 --- a/spec/intl.html +++ b/spec/intl.html @@ -690,6 +690,7 @@

1. Set _dateTimeFormat_.[[DateStyle]] to _dateStyle_. 1. Let _timeStyle_ be ? GetOption(_options_, *"timeStyle"*, ~string~, « *"full"*, *"long"*, *"medium"*, *"short"* », *undefined*). 1. Set _dateTimeFormat_.[[TimeStyle]] to _timeStyle_. + 1. Let _formats_ be _resolvedLocaleData_.[[formats]].[[<_resolvedCalendar_>]]. 1. If _dateStyle_ is not *undefined* or _timeStyle_ is not *undefined*, then 1. If _hasExplicitFormatComponents_ is *true*, then 1. Throw a *TypeError* exception. @@ -700,18 +701,18 @@

1. Let _styles_ be _resolvedLocaleData_.[[styles]].[[<_resolvedCalendar_>]]. 1. Let _bestFormat_ be DateTimeStyleFormat(_dateStyle_, _timeStyle_, _styles_). 1. If _dateStyle_ is not *undefined*, then - 1. Set _dateTimeFormat_.[[TemporalPlainDateFormat]] to _bestFormat_. - 1. Set _dateTimeFormat_.[[TemporalPlainYearMonthFormat]] to _bestFormat_. - 1. Set _dateTimeFormat_.[[TemporalPlainMonthDayFormat]] to _bestFormat_. + 1. Set _dateTimeFormat_.[[TemporalPlainDateFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _matcher_, « [[weekday]], [[era]], [[year]], [[month]], [[day]] »). + 1. Set _dateTimeFormat_.[[TemporalPlainYearMonthFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _matcher_, « [[era]], [[year]], [[month]] »). + 1. Set _dateTimeFormat_.[[TemporalPlainMonthDayFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _matcher_, « [[month]], [[day]] »). 1. Else, 1. Set _dateTimeFormat_.[[TemporalPlainDateFormat]] to *null*. 1. Set _dateTimeFormat_.[[TemporalPlainYearMonthFormat]] to *null*. 1. Set _dateTimeFormat_.[[TemporalPlainMonthDayFormat]] to *null*. 1. If _timeStyle_ is not *undefined*, then - 1. Set _dateTimeFormat_.[[TemporalPlainTimeFormat]] to _bestFormat_. + 1. Set _dateTimeFormat_.[[TemporalPlainTimeFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _matcher_, « [[dayPeriod]], [[hour]], [[minute]], [[second]], [[fractionalSecondDigits]] »). 1. Else, 1. Set _dateTimeFormat_.[[TemporalPlainTimeFormat]] to *null*. - 1. Set _dateTimeFormat_.[[TemporalPlainDateTimeFormat]] to _bestFormat_. + 1. Set _dateTimeFormat_.[[TemporalPlainDateTimeFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _matcher_, « [[weekday]], [[era]], [[year]], [[month]], [[day]], [[dayPeriod]], [[hour]], [[minute]], [[second]], [[fractionalSecondDigits]] »). 1. Set _dateTimeFormat_.[[TemporalInstantFormat]] to _bestFormat_. 1. Else, 1. Let _needDefaults_ be *true*. @@ -729,7 +730,7 @@

1. If _needDefaults_ is *true* and _defaults_ is either ~time~ or ~all~, then 1. For each property name _prop_ of « *"hour"*, *"minute"*, *"second"* », do 1. Set _formatOptions_.[[<_prop_>]] to *"numeric"*. - 1. Let _formats_ be _resolvedLocaleData_.[[formats]].[[<_resolvedCalendar_>]]. + 1. Let _formats_ be _resolvedLocaleData_.[[formats]].[[<_resolvedCalendar_>]]. 1. If _formatMatcher_ is *"basic"*, then 1. Let _bestFormat_ be BasicFormatMatcher(_formatOptions_, _formats_). 1. Else, @@ -876,6 +877,35 @@

1. Return _bestFormat_. + + +

+ AdjustDateTimeStyleFormat ( + _formats_: a List of DateTime Format Records, + _baseFormat_: a DateTime Format Record, + _matcher_: *"basic"* or *"best fit"*, + _allowedOptions_: a List of field names, + ): a DateTime Format Record +

+
+
description
+
+ It inspects _baseFormat_ and determines the closest format to it that only includes fields from _allowedOptions_. + This is used for determining the best format for Temporal objects with the `"dateStyle"` or `"timeStyle"` options. + For example, a locale's best format for `"dateStyle": "full"` might include the weekday, which is not applicable when formatting a `Temporal.PlainYearMonth` object. +
+
+ + 1. Let _formatOptions_ be a new Record. + 1. For each field name _fieldName_ of _allowedOptions_, do + 1. Set the field of _formatOptions_ whose name is _fieldName_ to the value of the field of _baseFormat_ whose name is _fieldName_. + 1. If _matcher_ is *"basic"*, then + 1. Let _bestFormat_ be BasicFormatMatcher(_formatOptions_, _formats_). + 1. Else, + 1. Let _bestFormat_ be BestFitFormatMatcher(_formatOptions_, _formats_). + 1. Return _bestFormat_. + +