Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DateTimeFormat fractionalSecondDigits: conflict between MDN and spec #590

Open
justingrant opened this issue Jul 13, 2021 · 11 comments
Open
Assignees
Labels
c: datetime Component: dates, times, timezones s: help wanted Status: help wanted; needs proposal champion
Milestone

Comments

@justingrant
Copy link
Contributor

MDN shows that 0 is a valid value for the fractionalSecondDigits. The spec (and Chrome) throws when zero is passed for this option. Here's the docs:

image

I assume this was thought through and there's a good reason for zero being excluded in the 402 spec. Why was the reason?

FWIW, Temporal allows zero for this option, so regardless of whether zero is allowed or not, we should probably align across Temporal and 402. AFAIK, there's already a plan to expand this option to allow for nanoseconds precision, so as part of those changes I'd vote for allowing zero because it seems like a valid choice, but if you disagree then let's discuss. There's also an 'auto' option allowed on the Temporal side.

Here's the TS declaration of this on in Temporal:

fractionalSecondDigits?: 'auto' | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

Here's a repro to see the error

new Intl.DateTimeFormat(
  'en-us', 
  { hour: 'numeric', minute: 'numeric', second: 'numeric', fractionalSecondDigits: 0 }
).format(new Date())
// MDN: OK
// Spec and Chrome: RangeError: fractionalSecondDigits value is out of range.
@sffc sffc added c: datetime Component: dates, times, timezones s: discuss Status: TG2 must discuss to move forward labels Jul 13, 2021
@anba
Copy link
Contributor

anba commented Jul 13, 2021

#347 initially always set fractionalSecondDigits, but I proposed to omit it when it's not present in the resolved date-time pattern. Then there was some confusion about how to handle undefined vs. 0, but that was resolved eventually. Later on Frank proposed to remove 0 from the set of allowed options altogether, presumably because that would have avoided this confusion about undefined vs. 0 in the first place.


If we were to allow 0, I strongly propose to implement this by setting 0 to undefined after reading "fractionalSecondDigits" from the options object. That means to specify this as:

Let value be ? GetNumberOption(options, "fractionalSecondDigits", 0, 3, undefined).
If value is 0, set value to undefined.

@ryzokuken
Copy link
Member

This should be a simple normative PR, right? @justingrant would you like to take a stab at this?

@coreysf coreysf closed this as completed Sep 9, 2021
@coreysf coreysf reopened this Sep 9, 2021
sffc added a commit that referenced this issue Sep 9, 2021
# 2021-09-09 ECMA-402 Meeting

## Logistics

### Attendees

- Shane Carr - Google i18n (SFC), Co-Moderator
- Corey Roy - Salesforce (CJR)
- Romulo Cintra - Igalia (RCA), MessageFormat Working Group Liaison
- Thomas Steiner - Google (TOM)
- Frank Yung-Fong Tang - Google i18n, V8 (FYT)
- Long Ho - (LHO)
- Zibi Braniecki - Mozilla (ZB)
- Eemeli Aro - Mozilla (EAO)
- Greg Tatum - Mozilla (GPT)
- Yusuke Suzuki - Apple (YSZ)
- Louis-Aimé de Fouquières - Invited Expert (LAF)
- Richard Gibson - OpenJS Foundation (RGN)
- Myles C. Maxfield - Apple (MCM)

### Standing items

- [Discussion Board](https://github.com/tc39/ecma402/projects/2)
- [Status Wiki](https://github.com/tc39/ecma402/wiki/Proposal-and-PR-Progress-Tracking) -- please update!
- [Abbreviations](https://github.com/tc39/notes/blob/master/delegates.txt)
- [MDN Tracking](https://github.com/tc39/ecma402-mdn)
- [Meeting Calendar](https://calendar.google.com/calendar/embed?src=unicode.org_nubvqveeeol570uuu7kri513vc%40group.calendar.google.com)
- [Matrix](https://matrix.to/#/#tc39-ecma402:matrix.org)

## Status Updates

### Editor's Update

RGN: No updates.

### MessageFormat Working Group

RCA: We are working on a middle-ground data model that I hope will unblock the situation.  EAO is focused on it, with Stas, Mihai, etc.  EAO also put together an initial spec proposal.

EAO: I put together a spec outline, not a specific proposal.  I think we will be able to merge it later this week.

### Proposal Status Changes

https://github.com/tc39/ecma402/wiki/Proposal-and-PR-Progress-Tracking

FYT: Some more Test262 coverage is done.  But we still need help.

RCA: I updated browser compat for locale info, documentation for hour cycle, etc.

FYT: Do we have an instruction guide about how to update MDN?

RCA: The process is moving quickly.  It will be easier, though: you can just edit a Markdown file.

## Pull Requests

### Add changes to Annex A Implementation Dependent Behaviour

tc39/proposal-intl-locale-info#43

FYT: We added some changes to Appendix A.  Does this look good?  Do we have consensus to report this to TC39?

SFC: +1

RGN: +1

LAF: +1

#### Conclusion

Approved

### Change weekInfo to express non-continouse [sic] weekend

tc39/proposal-intl-locale-info#44

FYT: Some regions have a non-contiguous weekend.  This PR changes the data model to reflect that.

LAF: I wonder how this should be understood for all countries. In certain countries, the two "out of business" days may be not contiguous.  Should we call it business day and non business day?  Because "weekend" might not be the correct terminology.

SFC: Is there precedent in CLDR for using "business day" instead of "weekend"?

EAO: A quick Google search suggests that Brunei calls these days "weekend".

SFC: LAF, please open an issue on the repository to discuss the option name change.

SFC: Do we have consensus on the change?

LAF: +1

SFC: +1

#### Conclusion

Approved

## Proposals and Discussion Topics

### CollationsOfLocale() order

tc39/proposal-intl-locale-info#33

SFC: I feel that lists should define their sort order.  This is similar to the plural rule strings discussion from a couple of months ago.

ZB: I represent the other side.  I think developers should not be depending on the order.

LAF: (inaudible)

RGN: There is guaranteed to be an observable order.  The question is whether that order is enforced across implementations, and if so, what should that order be?

FYT: Could we return a Set?

RGN: Sets also have observable order.

SFC: I propose we bring the meta question to TC39-TG1 as a change to the style guide.

LAF: +1 about order issue

FYT: OK

#### Conclusion

SFC to make a presentation to TC39-TG1 to establish a best practice in the style guide.

### Define if "ca" Unicode extensions have an effect on Intl.Locale.prototype.weekInfo

tc39/proposal-intl-locale-info#30

LAF: My opinion about ISO-8601 is that it is not connected to any locale.  Something like Gregorian is connected to a locale, and could carry week info.  But ISO-8601 is international.

SFC: I think we should consult with CLDR.

FYT: This is about the first day of the week and minimal days in the week, not the weekend days.  I personally believe that we shouldn't limit the extension; for example, a subdivision could have legislation to change this info.

LAF: In my opinion, the impact of saying whether Sunday or Monday is the first day of week, or on the minimal days, is to make a "week calendar": a calendar that lays out days in a week, dated by week number.  I can imagine that some countries would like to distribute their own calendar, but I feel that there is a need among people to have the same week numbers.  I don't know for sure where the correct place for this concept is.

ZB: This is inspired by the mozIntl API.  The reason I needed it was for a general calendrical widget, the HTML picker.  I think date pickers in general need this, not just calendar layout.  I think it is a high-importance API.

SFC: I think the calendar subtag, or other subtags like the subdivision, should be taken into account.

FYT: I think we should take the whole locale to influence the result.

FYT: Do we need to make any changes to the proposal, and if so, what changes are needed?

RCA: No strong opinion on that, but concerned by the possible conflict with Temporal 

#### Conclusion

SFC, FYT, and LAF agree that the whole locale (including extension subtags) should influence the weekInfo.  FYT to share these notes with Anba and wait for follow-up.

### JS Input Masking 🎭

- Presenter: TOM
- Slides: https://goo.gle/ecma-402-js-input-masking
- Explainer: https://github.com/tomayac/js-input-masking/blob/main/README.md
- JS polyfill: https://github.com/tomayac/js-input-masking-polyfill

FYT: Thanks for the discussion. (1) Some parts of what you proposed… if the formats are the same across different regions, it shouldn't be part of Intl.  For example, if the ISBN format is the same across regions, it shouldn't be in Intl.  (2) Is the name "input masking" correct?  (3) A new item to consider is the postcode.  That differs a lot around the world.  The US has 5-4, India has 6 digits, Canada has special alphabetic rules.  (4) It would be good to validate whether a string is a valid input.  For example, maybe 13 digits is a valid ISBN, but not 14 digits.  (5) A Googler on our team built libphonenumber, and it ended up being their full-time job for a while.

TOM: Postcodes are interesting.  For validation, that's interesting and useful.  Thanks for confirming that it is useful.  I think it would make sense to have it in the proposal.

EAO: (1) Having built a library like this in the past, you start facing the issue of how to report errors on the input.  So it becomes error reporting, but you need to do a best effort at the formatting while also reporting errors in a side channel. (2) Formatting while the string is being edited is just really hard; you should just wait until the field loses focus.

TOM: I agree that live updating the field is challenging.  What you said about error reporting is interesting.  Verification needs a lot of thought.  I think it's something most developers probably want.

EAO: The biggest question is, how does the side-channel error reporting happen?  Because that's an interesting question for a UI component like this.

TOM: It seems like it could hook into the mechanism for email verification that we already have.  And for on-the-fly formatting, hopefully you could write the formatter so that it can listen to whatever event the developer thinks is the right event.

EAO: It's not just about a binary error.  It's about providing more context to the error messages.

TOM: I think many things can be done.  I'm new to this area, so I don't know the precedent.  I'm looking for more experience.

ZB: Thanks TOM for the presentation.  I've worked in this area before.  I'm excited about the space, and I have a lot of questions.  (1) Parsing is hard. There are a lot of questions here.  What happens if they write LTR and RTL?  What happens if they type in Arabic numerals?  What if they use different kinds of separators?  You quickly get into an uncanny valley.  (2) You should also think about address formatting, which is like postcode and phone number.  Where do you stop?  (3) International placeholders is an interesting topic.  How do you present a placeholder for a phone number?  That really depends on the region.  (4) I'm not sure that adding ??? is good for the scope of the spec.  (5) About whether this belongs in a spec.  It seems like a lot of UX teams will want to customize exactly what the output looks like: they agree on most of the format, but want to change a couple things.  There's a good question about how much of this is i18n.  (6) And finally, and this is the strongest point, if we were to specify what you are specifying, we would need to back it with a strong library.  Because speccing it in ECMA-402 doesn't give us everything.  So why not start with writing the foundational library, maybe one that can be used in many different programming languages, and then once you have the library, come back to ECMA-402 and ask whether we should bake it into the browser?  That can then help us answer questions about whether the payload is sufficiently high such that it makes sense to ship it in the browser.  So basically, I think we should start with a library.  I think ECMA-402 is likely not the right place to start.

TOM: We could build a library, but we run the risk of making the "15th way of doing things" (in reference to the XKCD comic).  Temporal started by making a polyfill, and is now integrating it into the browser.  We already have a lot of input masking libraries.

RCA: I think this is really useful. (1) I'm concerned that the scope could be very large. (2) I'm concerned about what ZB said; organizations where I've worked have wanted to have their own way of doing things with slightly different interactions and so on.  That formatter could be a custom thing for that institution.  (3) Another thing is the interoperability with HTML.  You could have an input credit card, the pattern, the validation, etc.  (4) Highly interactive input fields could slow performance on low-resource devices.

TOM: For performance, the obvious tweak would be to do validation on the server.

YSZ: I think this is a super important part of the application. (1) Like FYT said, some of this data is not Intl data. (2) Phone validation is very complicated, like ZB said. We need to care about the UI; for example, inputting the credit card should trigger a numeric keyboard rather than an alphabetic keyboard.  So it seems like we need <input type="phonenumber"/>.  Did you consider starting there?

TOM: I thought about that, and I put it in the explainer as an alternative.  

SFC: In order to avoid the "15th standard" issue, you should approach the industry leader in i18n standards, the Unicode Consortium, about making a working group to establish the industry canonical solution.  ECMA-402 looks for prior art, and Unicode is the place we point to most often.  This is similar in a way to the MessageFormat Working Group, which was chartered to resolve the competing standards for MessageFormat by bringing all the authors together.

TOM: Yeah, reaching out to Unicode and seeing if this has come up before would be a good option.  As I've said, I had this in the String prototype, and then realized that this should maybe be Intl.  Credit card numbers are generally not Intl, but phone numbers are.  So creating that prior art makes sense.

ZB: I had discussed this a few years ago with Unicode.  But with what SFC said, where there are multiple competing libraries, it means that we don't know what the answer is yet.  Once we put it in ECMA-402, we won't be able to change it.  When writing a library, we can make it and discard it with something better later.  It makes sense that we need a place to assemble expertise from the many organizations.  Maybe Unicode is the place.  And only after we have that canonical implementation, we can evaluate whether it fits in ECMA-402.

MCM: The question about new input forms was raised earlier.  Did you list use cases where form input types would NOT be sufficient for, where you need the JS APIs?

TOM: In a Node.js server, and you have a CSV file of unformatted phone numbers, you might want to format on the server.  So it makes sense to have isomorphic Node and client-side behavior.

MCM: Has Node.js said that they need a standard for this?  Aren't there already Node modules for this?

TOM: Deno is an interesting case.  They've started implementing Web APIs like fetch.  Programmers are used to the way Web APIs work, and they use them in Deno the way they expect them to work.

### LookupMatcher should retain Unicode extension keywords in DefaultLocale

#608

GPT: Seems reasonable to me.

EAO: +1

CJR: +1

#### Conclusion

OK to move forward with this change; review the final spec text when ready.

### ships the entire payload requirement

#588

#### Conclusion

FYT to follow up with Anba's suggestions on the Intl Enumeration API to harden the locale data consistency.

### DateTimeFormat fractionalSecondDigits: conflict between MDN and spec

#590

GPT: It seems reasonable to match the Temporal behavior.

SFC: Do we want to add 4-9 now, or wait until Temporal is more stable?

#### Conclusion

Seems reasonable to move forward with a spec change.  Still some open questions from Anba and SFC.

### Presumptive incompatible change in future edition erroneuosly listed

#583

RGN: The spec version is immutable.

FYT: Is there a way to publish errata?

RGN: I don't think so… I do see some errata on ECMA International, but I don't see references to those errata.

SFC: The PR in question is #471.  It was merged in January.  I don't know why the change to Annex B made it into the edition, but not the normative change to numberformat.html.

FYT: The other issue is that we have long tables in the PDF that get cut off.

RGN: We're trying to raise funding to generate the PDF by a better mechanism.

#### Conclusion

Ujjwal to investigate.

### Accept plural forms of unit in Intl.NumberFormat

#564

CJR: If we accept the plurals in RelativeTimeFormat, I can see a case for doing that also in NumberFormat.

SFC: There are basically 3 approaches.  (1), we only accept singular units.  (2), we accept plural forms for all units… stripping off the "s"?  (3), only special-case duration units like days and hours.

EAO: Pluralization for all units is challenging.  "inches", "kilometers-per-hour"

CJR: Having listened to your explanation, SFC, I agree with your assessment.  Doing it on an ad-hoc basis is leading away from consistency.

RCA: +1 for not allowing plurals.

RGN: I share this opinion.  Is there already a reference to CLDR, to prevent this from coming up again?

#### Conclusion

Stay consistent with CLDR, and add a normative reference to CLDR if there isn't already one.
@sffc
Copy link
Contributor

sffc commented Sep 9, 2021

Discussion from 2021-09-09: https://github.com/tc39/ecma402/blob/master/meetings/notes-2021-09-09.md#datetimeformat-fractionalseconddigits-conflict-between-mdn-and-spec

Conclusion: Seems reasonable to move forward with a spec change. Still some open questions from Anba and SFC.

@sffc
Copy link
Contributor

sffc commented Sep 9, 2021

My question on this issue is the timing of when we add 4-9. Should we add it into the Temporal proposal, or should we make the change sooner?

@sffc sffc added s: help wanted Status: help wanted; needs proposal champion and removed s: discuss Status: TG2 must discuss to move forward labels Feb 10, 2022
@sffc sffc added this to the ES 2022 milestone Feb 10, 2022
mstssk added a commit to mstssk/content that referenced this issue May 2, 2022
`0` is not allowed in ECMAScript® 2021 Internationalization API Specification.
https://402.ecma-international.org/8.0/#sec-datetimeformat-abstracts

Actually web browsers does not implement to allow 0 for fractionalSecondDigits, and throws RangeError when using `fractionalSecondDigits: 0`.

ref tc39/ecma402#590
@sffc sffc modified the milestones: ES 2022, ES 2023 Jun 1, 2022
@romulocintra
Copy link
Member

@sffc Do we have a consensus on this? I see a few possibilities here:

  1. Should we accept the 0 and set it to undefined , so we can be aligned with MDN?

// Anba's suggestion
1. Let value be ? GetNumberOption(options, "fractionalSecondDigits", 0, 3, undefined).
1. If value is 0, set value to undefined.

  1. Should this support auto and 0 ?

  2. And how about 4-9, should they be included here or in Temporal?

@ray007
Copy link

ray007 commented Sep 23, 2022

0 for "none" sounds reasonable to me.

@sffc
Copy link
Contributor

sffc commented Sep 23, 2022

@sffc Do we have a consensus on this?

We discussed it in 2021-09 it appears, with some open questions. I would say we should do the following:

  1. Accept 0 to mean the same as undefined
  2. Accept 4 through 9 in preparation for Temporal; in the mean time, digits in those positions are always 0
  3. Do not add "auto"

@FrankYFTang
Copy link
Contributor

FrankYFTang commented Nov 28, 2022

I put in some comments into #715 but let me copy here too

I am very concerned about this PR and oppose this getting merged into ECMA402 as a ECMA402 PR. Instead, I propose these changes be merged into the Temporal proposal.

One thing I would like to point out clearly, according to TG2 meeting notes, we never discussed the details during the meeting. In both meeting, I was always direct to “ to discuss offline “

TG2 Oct meeting
https://github.com/tc39/ecma402/blob/master/meetings/notes-2022-10-06.md#normative-update-spec-to-align-with-mdn--preparation-for-temporal

TG2 Nov meeting
https://github.com/tc39/ecma402/blob/master/meetings/notes-2022-11-03.md#normative-update-spec-to-align-with-mdn--preparation-for-temporal-715

So here I am, discuss it offline with you.

We need to discuss this PR with two different issues:

  1. Dealing with 0
  2. Dealing with 4-9

First of all, in the current shape of this PR, we change 3 different places of in ECMA402
In InitializeDateTimeFormat This is needed for 1) and 2) above
Section “Abstract Operations for DateTimeFormat Objects” This is only needed for 2) above. (no impact to 1).
In BasicFormatMatcher This is only needed for 2) above. (no impact to 1) ).

For dealing with 0, the current shape of ECMA402 disallows 0. And from what I read in #590 there are two only reasons mentioned:
MDN states it accept 0 (which is a MDN bug which should be fixed in MDN)
“Temporal allows zero for this option, so regardless of whether zero is allowed or not, we should probably align across Temporal and 402. “ . This is a valid one, but the only changes needed for Temporal about this is in InitializeDateTimeFormat. But Temporal already have a section to modify InitializeDateTimeFormat in https://tc39.es/proposal-temporal/#sec-temporal-initializedatetimeformat I see no reason such change cannot be just amended to Temporal spec only, if that is the ONLY reason we want to accept 0. And I see no value to add 0 now into ECMA402 prior to that.

Now, let’s talk about 4-9. The change by itself, from my point of view, is not only unnecessary, but very confusing in this stage and should not be landed into ECMA402:

let x = 3;
let y = (new Intl.DateTimeFormat("en", {fractionalSecondDigits: x })).resolvedOptions().fractionalSecondDigits;
x == y 

Prior to PR 715, we got true here.

With this PR 715, it still get true. But now consider the following

x = 4
let y = (new Intl.DateTimeFormat("en", {fractionalSecondDigits: x })).resolvedOptions().fractionalSecondDigits;

without this PR, the new will simply throw RangeError because 4 is not an acceptable value

but with this PR, the new will success and y will be set to undefined.
and x == y will become false

But.... what will be the df format?
It will format without any fracitional second digits

and this is very confusing because the 4 didn't through, neither get honor in the format, it was just be treated as undefined.

now, you will say, Temporal need to support fractionalSecondDigits 0, and 4-9 so we need to change, it. ok where are that stated in Temporal spec now?

Let's see, the word "fractionalSecondDigits" currently appeared 26 times inside
https://tc39.es/proposal-temporal/

It appear twice in ToSecondsStringPrecision and that function impact the following functions:

  • Temporal.PlainTime.prototype.toString
  • Temporal.PlainDateTime.prototype.toString
  • Temporal.ZonedDateTime.prototype.toString
  • Temporal.Duration.prototype.toString
  • Temporal.Instant.prototype.toString

then the other 24 times which mentioned the word "fractionalSecondDigits" are all in Chapter 15 which amending ECMA402
Now, according to the Temporal spec which passed Stage 3 (and the current shape of the spec text), the fractionalSecondDigits is only needed to support 1-3 for toLocaleString and Intl.DateTimeFormat (see step 36-b-i in https://tc39.es/proposal-temporal/#sec-temporal-initializedatetimeformat ) . That is what have already reach consense. There were no consense reached to support toLocaleString or Intl.DateTimeFormat beyond 3 to reach nanosecond precision.

If we follow the Temporal spec text which reach Stage 3 (and what is on the spec today) we would throw RangeError with the following code:

let t = Temporal.Now.plainDateTimeISO();
t.toLocaleString("en", {fractionalSecondDigits:4});
// throw RangeError
t.toLocaleString("en", {fractionalSecondDigits:9});
// throw RangeError
which is the spec we all agreed in TC39 to support so far when it get Stage 3 advacement. I never oppose the Temporal reach Stage 3 because it only require 3 fractionalSecondDigits but not beyond it. If the spec text require more than 3 I would block it when it asked to advance to Stage 3. But since the champion didn't I didn't block.

But... why I would block? The reason is simple. We will have implementation difficult to support sub millisecond precision for these toLocaleString or Intl.DateTimeFormat.prototype.format . Currently, we are depending on ICU to do the format, but ICU cannot handle nanosecond precision yet. (at least not in 72.1)

Let me illusrate the difficulty of supporting nanosecond precision in Temporal.*.prototype.toLocaleString and Intl.DateTimeFormat.prototype.format(|ToParts)

Currently, all known browsers implement it by (either C API or C++ API) using ICU.

Here is the ICU documentation:

https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#a3118634b6b3042729ddfe2f2dbf7ea10#:~:text=Fractional%20Second

notice the text
"
Fractional Second - truncates (like other time fields) to the count of letters when formatting. Appends zeros if more than 3 letters specified.
'

Now, with the followng patch for v8, I can implement a BUGGY attempt to support Temporal to nanosecond precision, but this is only BUGGY attempt which prove the point it cannot be done with the current ICU API:

diff --git a/src/objects/js-date-time-format.cc b/src/objects/js-date-time-format.cc
index 6e6298e274..73269f6927 100644
--- a/src/objects/js-date-time-format.cc
+++ b/src/objects/js-date-time-format.cc
@@ -485,7 +485,7 @@ Handle<String> DateTimeStyleAsString(Isolate* isolate,
 
 int FractionalSecondDigitsFromPattern(const std::string& pattern) {
   int result = 0;
-  for (size_t i = 0; i < pattern.length() && result < 3; i++) {
+  for (size_t i = 0; i < pattern.length() && result < 9; i++) {
     if (pattern[i] == 'S') {
       result++;
     }
@@ -828,7 +828,13 @@ DateTimeValueRecord TemporalInstantToRecord(Isolate* isolate,
                      BigInt::FromInt64(isolate, 1000000))
           .ToHandleChecked()
           ->AsInt64();
-  return {milliseconds, kind};
+  double fraction =
+      BigInt::Remainder(isolate, Handle<BigInt>(instant->nanoseconds(), isolate),
+                     BigInt::FromInt64(isolate, 1000000))
+          .ToHandleChecked()
+          ->AsInt64();
+  printf("fraction %f\n", fraction);
+  return {milliseconds + fraction * 1e-6, kind};
 }
 
 Maybe<DateTimeValueRecord> TemporalPlainDateTimeToRecord(
@@ -1377,6 +1383,7 @@ icu::UnicodeString CallICUFormat(const icu::SimpleDateFormat& date_format,
   // For other Temporal objects, lazy generate a SimpleDateFormat for the kind.
   std::unique_ptr<icu::SimpleDateFormat> pattern(
       GetSimpleDateTimeForTemporal(date_format, kind));
+  printf("%.9f\n", time_in_milliseconds);
   pattern->format(time_in_milliseconds, result, fp_iter, status);
   return result;
 }
@@ -2430,7 +2437,7 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::New(
       MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(
           isolate, fsd,
           GetNumberOption(isolate, options,
-                          factory->fractionalSecondDigits_string(), 1, 3, 0),
+                          factory->fractionalSecondDigits_string(), 0, 9, 0),
           Handle<JSDateTimeFormat>());
       // Convert fractionalSecondDigits to skeleton.
       for (int i = 0; i < fsd; i++) {

The shortcoming is in two issues not just one

The ICU SimpleDateFormat::format only support to milisecond precision (which mean 1, 2 or 3 in fractionalSecondDigits) so there are 1,000,000 different value of nanoseconds of Instant will all formated to the same string, which they should not.
The ICU SimpleDateFormat::format take the Date (which is double) as the date type to represent millisecond, and the data type does not have the precision to encode up to nanosecond precision, even if we change ICU 73 to relax the restriction on 1 above.
Let me illustrate the issue with the following call (this is run on my local machine after I apply the patch above:

ftang@ftang4:~/v8/v8$ out/x64.release/d8 --harmony-temporal
V8 version 11.0.0 (candidate)
d8> let t = Temporal.Now.plainDateTimeISO();
undefined
d8> t
2022-11-28T14:18:35.661261056
d8> t.toLocaleString("en", {fractionalSecondDigits:9});
fraction 261056.000000
1669673915661.260986328
"11/28/2022, 2:18:35.661000000 PM"

Now, the fact it show
"11/28/2022, 2:18:35.661000000 PM"
instead of
"11/28/2022, 2:18:35.661261056 PM"
show the ICU API limitation mentioned in the API doc
but more interestingly is the debugging line I printted out

fraction 261056.000000
1669673915661.260986328
This show, my code did get the bigint as
1669673915661261056 nanosecond
but while I convert it to double, by taking
1669673915661 millisecond
and 261056 nanoseconds both as int and turn into a double, the result is
1669673915661.260986328
because double use only 53 bits to encode the digits, and
log(2^53)/log(10)= 15.9545897702
which mean we double can only encode the first 15 digits precisionly. and in this case, "1669673915661" is 13 digits, so two extra digits "26" is precious enough but it won't have the encodeing power to preiously encode the "1056", therefore what the ICU see from the double is the remaining 4 digits are "0986" instead.

Therefore, even if we fix the ICU SimpleDateFormat:format to format the rest of 6 digits from the double instead of "Appends zeros if more than 3 letters specified" , it will generate incorrect result, likely for the value fractionalSecondDigits> 5
Assuming the value is not represent not too far in the future or past. For value way in the future or in the past, the incorrect result will shown for a even smaller value in fractionalSecondDigits.

In summary, if we want to change ICU to support the formatting of nanosecond precision, not only it need to change the internal implementation of SimpleDateFomrat::format, it also need to add new API to take a new data type to encode the value which has nanosecond precision, which currently does not exist in ICU.

Accepting an behavior of 1 to 6 '0' as the format result in place with the real didigt stored in the Temporal object is a very very bad one because for anyone whe care about setting the fractionalSecondDigits > 3, we have to assume the information below millisecond is important enough for them to use such setting, and truncating it to millisecond then adding 0 will misguide the reader as a different value than what it actually is and could cause diaster for any application which care about sub millisecond precision. The users would either

  • they DO NOT care about sub millisecond precision so the API do not need to support it, OR
  • they DO care about sub millisecond precision so the API have to format it it precisously

and supporting an API which could set it to higher precision but allow the formatted output in lower precision (incorrect value in that precision, actually) is very dangerous. and I would argue no body in the world will need such hehavior.

@HolgerJeromin
Copy link

@FrankYFTang
Thanks for your work here.
I just want to mention that we indeed develop a hmi which should be able to handle nanosecond timestamps for sorting data but also for displaying them with high precision.
Showing all timestamps with filled zeros or wrong characters for the sub millisecond part would be a problem for us.

@FrankYFTang
Copy link
Contributor

Showing all timestamps with filled zeros or wrong characters for the sub millisecond part would be a problem for us.

I understand this desire. I just want to make sure the process of making that happen conduct in a manner which could be developed properly.

Here is what I see are needed

  1. if the data type is Date. the precision is only millisecond and there are no sub millisecond precision now. Is that ok?

Which means if we have

let ds = (new Date()).toLocaleString("en", { fractionalSecondDigits:x});
let df = new Intl.DateTimeFormat("en", { fractionalSecondDigits:x});
let ds2 = df.format(new Date());

because the Date() only hold the now in millisecond if x > 3, it could be either:
a. Throw RangeError in toLocaleString and Intl.DateTimeFormat constructor OR
b. padd '0' in the end

notice b. has nothing to do with ICU but what is required to be stored in Date
Currently https://tc39.es/ecma262/#sec-timeclip ensure the value stored are integer of millisecond so there are no sub millisecond information.

  1. In order for Temporal toLocaleString and Intl.DateTimeFormat for Temporal types, it requires two changes in ICU based implementation (which is all engines depended on)

a. the SimpleDateFormat need to take data type as input which can represent the precision down to nanoseconds. So far we do not have this feature in ICU
b. the SimpleDateFormat need to not truncate the digits after 3 digit and not generate only '0' but based on the nanosecond value instead.

These two dependency need to be first addressed in ICU project to address the "implementation difficulity" issue.

@FrankYFTang
Copy link
Contributor

File issue for ICU in https://unicode-org.atlassian.net/browse/ICU-22218

Josh-Cena pushed a commit to mdn/content that referenced this issue Apr 19, 2023
fractionalSecondDigits is just 1, 2 or 3.

`0` is not allowed in ECMAScript® 2021 Internationalization API Specification.
https://402.ecma-international.org/8.0/#sec-datetimeformat-abstracts

Actually web browsers does not implement to allow 0 for fractionalSecondDigits, and throws RangeError when using `fractionalSecondDigits: 0`.

ref tc39/ecma402#590
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: datetime Component: dates, times, timezones s: help wanted Status: help wanted; needs proposal champion
Projects
Archived in project
9 participants