tc39 / ecma402

Status, process, and documents for ECMA 402

Home Page:https://tc39.es/ecma402/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

short/medium/long/full style for Intl.DateTimeFormat

zbraniecki opened this issue · comments

I'm working on trying to design an alignment between how Intl API works and OS Intl preferences work.

An example of such task is how to incorporate the date/time formatting OS user preferences into DateTimeFormat API.

Currently, we expect users of our API to list the elements of the date/time format as options, or, if none are listed, we take:

  • for Intl.DateTimeFormat and Date.prototype.toLocaleString: hour/minute/second/day/month/year as numeric
  • for Date.prototype.toLocaleTimeString: hour/minute/second as numeric
  • for Date.prototype.toLocaleDateString: day/month/year as numeric

On the other hand, MacOS exposes four styles for date and time: short, medium, long and full.
Windows exposes short/long.

I see two solutions to bind those two APIs:

  1. Create OS Intl prefs API that returns Intl API options.
let opts = intlOSPrefs.DateFormat(intlOSPrefs.formatFull);
  --> returns {weekday: 'long', month: 'long', day: 'numeric', year: 'numeric'}
let dtf = new Intl.DateTimeFormat(navigator.locales, opts);
dtf.format(date);
  1. Add style option and change how we handle "defaults" in Intl.DateTimeFormat

In this approach, we would use the language in spec similar to one we use for timezones: if user did not specify a timezone, we try to retrieve it from the host OS and we land on the current defaults (all as numeric) only if we cannot get anything from the OS.

date.toLocaleString() == date.toLocaleString(navigator.locales, {
  style: 'medium'
}) == date.toLocaleString(navigator.locales, {
  day: 'numeric',
  month: 'short',
  year: 'numeric'
}); // In MacOS

date.toLocaleDateString(navigator.locales, {
  style: 'long'
}) == date.toLocaleString(navigator.locales, {
  day: 'numeric',
  month: 'long',
  year: 'numeric'
}); // default in MacOS


date.toLocaleDateString(navigator.locales, {
  style: 'full'
}) == date.toLocaleString(navigator.locales, {
  weekday: 'long',
  day: 'numeric',
  month: 'long',
  year: 'numeric'
}); // default in MacOS

That would require adding an additional step in 12.1.2 ToDateTimeOptions as 6.a and 7.a where the implementation could reach to host OS for the preferences and only fallback to the current 6.a and 7.a if those cannot be retrieved.

In this approach, style option would be mutually exclusive with any of the token options.

Opinions? @caridy , @rxaviers , @jungshik, @srl295 ?

(then we could do the same for Number.prototype.toLocaleString and follow the formatting OS preferences there)

  1. Add constants referencing the different formats

E.g. Intl.DateTimeFormat could have constants like SHORT, MEDIUM, LONG, FULL.

let dtf = new Intl.DateTimeFormat(navigator.locales, Intl.DateTimeFormat.MEDIUM);
dtf.format(date);
date.toLocaleString() == date.toLocaleString(navigator.locales, Intl.DateTimeFormat.MEDIUM) == date.toLocaleString(navigator.locales, {
  day: 'numeric',
  month: 'short',
  year: 'numeric'
}); // In MacOS

This is a bit shorter than both previous options, but requries toLocaleString() and Intl.DateTimeFormat() to take mixed types of arguments.

Sebastian

Well, the third one is really similar to (2) in that it's implicit - puts the "get the preference from host OS" on the Intl API (much like we do with timeZone now), rather than explicit (1) where we have separate API that retrieves OS preferences.

I separated out the proposal for retrieving user defined preferences from host environment into: #109

So this issue is only about style short/medium/long/full for DateTimeFormat.

Mapping preferences retrieved from the OS to values for the year/month/day/hour/minute/second/etc options is not sufficient to fully support the flexibility of OS formatting patterns (if that's a desired goal).

For example, in Mac OS X and Windows, users can customize the order of the components and the separators between them, as well as the format used for each individual components of the date/time. AFAICS, this is not exposed by Intl.DateTimeFormat in any way; the options can be used to control which components are present, and which format is used for each component, but not their order nor any literal separators used between them.

For example, in Mac OS X and Windows, users can customize the order of the components and the separators between them, as well as the format used for each individual components of the date/time. AFAICS, this is not exposed by Intl.DateTimeFormat in any way;

That's not true for windows. See:

On Mac OS it seems that users can do all of that:

and I think we could retrieve the pattern from the OS and then operate on it the way we would on a pattern retrieved from CLDR.

but, once again, that's more for #109. Here I'd like to talk about ability to do:

date.toLocaleTimeString(locales, {
  style: 'long'
};

date.toLocaleDateString(locales, {
  style: 'medium'
};

date.toLocaleString(locales, {
  style: 'full',
};

and initially have sane defaults for it irrelevant of the result of #109.

That's not true for windows. See:

https://bug1308329.bmoattachments.org/attachment.cgi?id=8801351
https://bug1308329.bmoattachments.org/attachment.cgi?id=8801352

Actually, I think it is; see https://bugzilla.mozilla.org/attachment.cgi?id=8802234.

oh, cool. Didn't find it. Ok, then Windows and MacOS are on par. Still, that's #109 :)

Here I'd like to talk about ability to do:

date.toLocaleTimeString(locales, {
  style: 'long'
};

date.toLocaleDateString(locales, {
  style: 'medium'
};

date.toLocaleString(locales, {
  style: 'full',
};

If we go this route, I think we'd want two style-type options for toLocaleString, one each for the date and time portions:

date.toLocaleString(locales, { dateStyle: 'full', timeStyle: 'short' });

And in that case, it'd probably make sense to use the same naming for toLocaleTimeString and toLocaleDateString, rather than a non-specific style option.

I like introducing {date,time}style = {full, long, medium short}. CLDR has the corresponding styles. It was discussed in v1 spec, but the current way trumped.

@jungshik What led to the current decision? I like the idea of introducing more things that have corresponding CLDR styles.

So, CLDR has styles for date, time, and combinations of date/time: http://www.unicode.org/cldr/charts/30/summary/en.html#1816

I see two ways to expose it:

  1. Via one param with 12 options:

style: 'full'|'long'|'medium'|'short'
style: 'date-full'|'date-long'|...
style: 'time-full|'time-long'|...

  1. Via two options: date-style and time-style

The problem with this is that not only date and time may have style, but also the pattern that joins those two. It would be awkward to have long date, short time and have to on top of that decide if the pattern that combines them into a single string is long, short or medium.

On the other hand, in the RelativeTimeFormat spec discussion we noticed that the same pattern that joins date and time, can be used to join relative date and time.
So, if the date+time pattern for full is "{0} at {1}" then both can work: "Monday, January 9th at 3:45 pm" and "Tomorrow at 3:45 pm".

If we go for style: "long" in DateTimeFormat, we'll get "Monday, January 9th at 3:45 pm", but I'm not sure how will we build the API for relativedate+time.

Any ideas? Opinions?

First, I'm curious, why are we trying to expose these particular things, and not other things that CLDR exposes, e.g., to take a random one, Hms time format? (Not saying we shouldn't, just wondering what went into it.)

If we do want to expose these combinations, I can think of a few goals in the resulting API:

  1. Don't break backwards compatibility for existing users, either in initializing or reading resolvedOptions()
  2. Avoid awkward string concatenation/parsing: we already have two structured formats--the options bag and BCP47. A third seems like overkill, and for this, I prefer your second option.

If we go with your option 2, with date-style and time-style, here's a suggestion for how we could square that with a style to preserve:

  • The internal slots are [[dateStyle]] and [[timeStyle]]
  • The style property as an input in the options bag is used as the "default" value for dateStyle and timeStyle, if either or both are missing
  • A style property is created in resolvedOptions if [[dateStyle]] === [[timeStyle]].

What do you think? If we end up permitting things like { timeStyle: "Hms" }, I think this extends pretty cleanly to that--there simply would not be a style property to the resolvedOptions(). Anyway, I don't feel qualified to evaluate the original motivation.

First, I'm curious, why are we trying to expose these particular things, and not other things that CLDR exposes, e.g., to take a random one, Hms time format?

It seems to me that we can start with allowing only descriptive formats and that matches what most OSes expose.

Adding another style like Hms could be done later if need arises.

Your solution looks sane and I can see us doing it. But it doesn't answer the question on how will we work with the date+time joining pattern which can also be full|long|medium|short and potentially work with Intl.RelativeTimeFormat.

I sense that there must be a good solution to that that will allow us solve both - date+time and relativetime+time cases, but I can't see it yet.

@jungshik What led to the current decision? I like the idea of introducing more things that have corresponding CLDR styles.

Well, both 'style' and 'pattern' were discussed, but in search of the largest common denominator across potential implementations, we ended up defining a rather small subset of patterns available via CLDR with a rather verbose way of specifying them ('year: numeric', 'day: ....', 'hour: ....', 'timeZoneName: short').

Adding more patterns like 'Hms', 'Hm', 'yMMM' , 'yMdjmsvvvv', and so forth was left for the future extension (IIRC). I can think of three ways to do that (now that there's been a convergence toward CLDR as the data source)

  1. Add a new 'pattern' option for patterns not covered currently via verbose options
  2. a) Make existing options (year, timeZoneName, day, year, etc) to have more values
    b) Add more options if there's currently no option corresponding to LDML 'format speifier'
  3. Overload 'style' for both 'convenience names' (long, short, full, medium) and patterns (Hm, yMMMd, jms, Hmsvvvv, etc)

Implementation-wise, one way to handle all three ways ('pattern', current verbose option bags, 'style') is convert them all to a 'pattern' which would be looked up in a given locale data to find the corresponding locale-specific skeleton. (for option bags, that's what v8 does now. It appears that Spidermonkey and JSC do the same).

A proposal put forward here is to overload 'style' with a human-friendly shortcut (full, long, etc) AND 'pattern'. ( third option above).

A couple of questions to answer before deciding what to do:

Which would be best in terms of making DateTimeFormat consistent (in construction and resolvedOptions)?
Which would be easiest and most flexible from the API users' pov?

@caridy , @ericf , @littledan , @rxaviers - opinions?

I'm happy to champion any consensus we can come up with. I mostly care for human-friendly style shortcuts, but I can extend them to cover a list of patterns as @jungshik suggests.

Also, we could do this gradually - get the human-friendly style - full|long|medium|short, and then talk about adding patterns.
Unless anyone has a current use case for the pattern styles (Hm, yMMMd etc.)

This proposal has received Stage 1 at today's TC39 meeting.

I asked the committee about style: "date-short" vs dateStyle: "short".

The only feedback was that the latter looks more like CSS which may be good, but it seems that there are no strong feelings one way or another and it's up to us to decide.

I'm happy to champion any consensus we can come up with. I mostly care for human-friendly style shortcuts

👍 on adding these human-friendly styles. options.style seems a good name to me.

What should happen if style and any verbose field is mutually present, e.g., {style: X, year: Y}. Should an error be thrown?

The problem with this is that not only date and time may have style, but also the pattern that joins those two. It would be awkward to have long date, short time and have to on top of that decide if the pattern that combines them into a single string is long, short or medium.

@zbraniecki, the combination pattern can be deduced from the date and time lengths according to CLDR (UTS#35) http://www.unicode.org/reports/tr35/tr35-dates.html#dateTimeFormat.

I asked the committee about style: "date-short" vs dateStyle: "short".

One benefit of overriding style with style: "date-short" is that we have less mutual exclusive options. For example, using dateStyle, timeStyle, and style would require additional checks to assert they are not being mutually used (in addition to them vs. the verbose options such as year, month, etc).

Via one param with 12 options.

@zbraniecki, in addition to these 12 options, there are the non-uniform datetimes, e.g.,date-short x (time-medium, time-long, time-full) + date-medium x (...) + .... At least, this seems expected according to ICU and CLDR (UTS#35) http://www.unicode.org/reports/tr35/tr35-dates.html#dateTimeFormat.


but I can extend them to cover a list of patterns as @jungshik suggests.

👍 too... CLDR calls them skeletons, which contains "only field information and in a canonical order". [1]

I personally like the brevity of skeletons compared to the verbose options year, month, etc. For example:

let fmt = new Intl.DateTimeFormat(locales, {style: "yMMMdjm"});

let equivalentFmt = new Intl.DateTimeFormat(locales, {
  year: 'numeric',
  month: 'short',
  day: 'numeric',
  hour: 'numeric',
  minute: 'numeric'
});
  1. a) Make existing options (year, timeZoneName, day, year, etc) to have more values
    b) Add more options if there's currently no option corresponding to LDML 'format speifier'

I agree with @jungshik's point.

  1. Overload 'style' for both 'convenience names' (long, short, full, medium) and patterns (Hm, yMMMd, jms, Hmsvvvv, etc)

I see two benefits of overriding options.style:

  1. Otherwise, we would have to discuss its name: options.pattern or options.skeleton (to be in line with CLDR?) or something else? 😊 Note CLDR refers to pattern as the format that a skeleton maps to.
  2. Less mutual exclusive options like mentioned above.
let dtf = new Intl.DateTimeFormat(navigator.locales, Intl.DateTimeFormat.MEDIUM);

@SebastianZ the downside of this approach is that it makes it hard to combine it with other options such as timeZone. For clarity, if we had consts, I would make them strings, not objects.


1: Skeleton is a more flexible formatting mechanism than the predefined list of short, medium, long, full formats.

The availableFormats element and its subelements provide a more flexible formatting mechanism than the predefined list of patterns represented by dateFormatLength, timeFormatLength, and dateTimeFormatLength. Instead, there is an open-ended list of patterns (represented by dateFormatItem elements as well as the predefined patterns mentioned above) that can be matched against a requested set of calendar fields and field lengths. Software can look through the list and find the pattern that best matches the original request, based on the desired calendar fields and lengths. For example, the full month and year may be needed for a calendar application; the request is MMMMyyyy, but the best match may be "y MMMM" or even "G yy MMMM", depending on the locale and calendar.

The id attribute is a so-called "skeleton", containing only field information, and in a canonical order.

http://www.unicode.org/reports/tr35/tr35-dates.html#availableFormats_appendItems

@zbraniecki, the combination pattern can be deduced from the date and time lengths according to CLDR (UTS#35) http://www.unicode.org/reports/tr35/tr35-dates.html#dateTimeFormat.

That's cool!
That means that the decision between date-style+time-style vs style is purely about whether we want to allow people to define different date and time styles in one formatter (say, short time but long date).

Otherwise, we would have to discuss its name: options.pattern or options.skeleton (to be in line with CLDR?) or something else?

Internally at this point, we still will introduce an option.pattern because we need to pass the OS date/time pattern to the mozIntl.DateTimeFormat.
So depending on how we'll solve the host env. preferences (implictly within the formatter, or explicitly as a separate API, and then will we try to pass everything via BCP47, or as options) we will need to figure out if we need options.pattern.

👍 too... CLDR calls them skeletons, which contains "only field information and in a canonical order". [1]

I like the skeletons, I'm just a bit worried about the scope of the proposal. Adding just short/medium/long/full will allow us to get something done and we can always extend it to support a selected list of skeletons later.

That means that the decision between date-style+time-style vs style is purely about whether we want to allow people to define different date and time styles in one formatter (say, short time but long date).

That only means we don't need to ask user to provide the length of the "gluer" format. :) I believe something like {style: 'date-short-time-long'} could still be an option?

Internally at this point, we still will introduce an option.pattern because we need to pass the OS date/time pattern to the mozIntl.DateTimeFormat.

... and by pattern you mean pattern right? For example, the "yMd" skeleton maps to the following patterns in these different locales:

locale pattern
en "M/d/y"
es "d/M/y"
de "d.M.y"

I believe that internally it would be ok. I want to emphasized that a possible options.pattern doesn't get exposed to users, because it can't be used for localization and it would lead into bad i18n practices. The possible options.style discussed here and the existing verbose options are still the right ways for users to provide such input.

I like the skeletons, I'm just a bit worried about the scope of the proposal. Adding just short/medium/long/full will allow us to get something done and we can always extend it to support a selected list of skeletons later.

Ok. No strong opinion from my side on this roadmap.

style seems like a half-baked solution compared with skeletons, we can see it as a more human friendly styling mechanism but one that is crippled since it does not allow you to do more advanced things. That might be ok, but at the same time, I think skeletons are almost ubiquitous for any internationalization platform, that we probably should lean toward that solution.

style seems like a half-baked solution compared with skeletons, we can see it as a more human friendly styling mechanism but one that is crippled since it does not allow you to do more advanced things.

One more thing is that human friendly styles is what host environments speak. So to enable developers to be able to say "style this date the way user wants a full date to be styles", we need this concept.

See all the screenshots from platforms in this bug https://bugzilla.mozilla.org/show_bug.cgi?id=1308329 for reference of what users are able to manually define.

Sorry for mixing up 'pattern' and 'skeleton' in my previous comment. From now on, I'll adhere to the CLDR terminology. skeleton is just a list of field specifiers (e.g. yMd, jmsv, MMMd) and locale-specific patterns can be derived from a given skeleton based on the locale data. (the pattern for yMd can be y. M. d. in 'ko', M/d/y for en-US and d/M/y for fr, en-GB, etc). And, some users may want to make up a pattern independent of a skeleton/locale (e.g, 'yyyy-MM-dd')

Otherwise, we would have to discuss its name: options.pattern or options.skeleton (to be in line with CLDR?) or something else?

Internally at this point, we still will introduce an option.pattern because we need to pass the OS date/time pattern to the mozIntl.DateTimeFormat.
So depending on how we'll solve the host env. preferences (implictly within the formatter, or explicitly as a separate API, and then will we try to pass everything via BCP47, or as options) we will need to figure out if we need options.pattern.

By options.pattern, which do you mean 'skeleton' or 'pattern' in the CLDR sense? It looks like you mean 'pattern' in the CLDR sense, but I think 'options.skeleton' (for skeleton in the CLDR sense) is a lot more useful than 'options.pattern'. There can be a use-case for 'options.pattern' (when a fixed format is required regardless of locale), but this use-case is rather limited than 'skeleton'.

Internally, options.style, options.skeleton and current verbose option bags (for year, month, day, hour, etc) should be resolved to a 'skeleton' with a well-defined priority (and conflict-resolution recipes) among them. And, this resolved skeleton can be a part of resolvedOptions as a succinct representation of date-time format (other than calendar, numberingSystem, timeZone).

Unless options.pattern is introduced as well, we can stop here in terms of what's available via resolvedOptions.

One more thing is that human friendly styles is what host environments speak. So to enable developers to be able to say "style this date the way user wants a full date to be styles", we need this concept.

I like the idea of adding a 'human-friendly' and 'succinct/less verbose' way to specify a format, but
I have a reservation about tying 'full, medium, short, long' styles to the host environment preference.

By options.pattern, which do you mean 'skeleton' or 'pattern' in the CLDR sense? It looks like you mean 'pattern' in the CLDR sense

Yes, I mean pattern in CLDR sense. That's exactly what Windows, Mac, Android and Linux can provide me.

I like the idea of adding a 'human-friendly' and 'succinct/less verbose' way to specify a format, but
I have a reservation about tying 'full, medium, short, long' styles to the host environment preference.

I don't see how you can advance the web as a platform without closing this gap. Currently, every native platform will allow you to format date and time according to user preferences.
Since we're trying to use ECMA402 API for Firefox UI, and we need Firefox UI to be able to follow user preferences, I need this feature badly.

I understand if we'll decide not to expose it to the Web, but I believe that over time there will be more platforms that will want to use JS based Intl API and will want this feature (Electron?).

For that reason, I believe that having style with those names will allow those platforms to easily tie into OS preferences, while for the Web it will just mean that users can easily get the simplest solution (most users of DateTimeFormat will not dwell into what's a skeleton and just want a long date).

Ok, I'm making progress getting the set of APIs to provide DateTimeFormat with data from the OS.

I think I'm ready to champion a proposal to add:

  • options.pattern
  • options.timeStyle
  • options.dateStyle

for DateTimeFormat.

Does it sound like a good start to everyone? If so, I'll draft the proposal spec.

SGTM.

From the above, I understand this:

  • options.skeleton - there's a big support for this option. It allows for "more advanced things", it is "almost ubiquitous for any internationalization platform".
  • options.pattern - there's an agreement above its use is very limited. Potentially, it could be used for non-i18n problems such as ISO parsing (which can be solved by regular new Date() or Date.parse). I recommend we don't expose this option to developers, this would be very confusing, especially if options.skeleton isn't available at the same time.
  • options.timeStyle + options.dateStyle (or options.style) - human friendly styles, they map to host environments presets. Although, there's a reservation about tying 'full, medium, short, long' styles to the host environment preference.

Suggestions:

  • How about we have different values for host environment presets? For example: 'os-full, os-medium, os-short, os-long' along with the non-attached to host environment 'full, medium, short, long'?
  • Do not expose options.pattern.
  • Eventually support options.skeleton.

ISTM that options.skeleton is essentially duplicating the functionality of the existing verbose options (year, month, day, etc.), just in a more compact (though cryptic) form.

(Currently, the verbose options don't expose as many possibilities as skeletons would, but this is a matter of degree rather than kind.)

options.pattern, OTOH, provides a capability that differs from both skeleton and verbose options, allowing the user to have precise control over the formatting rather than depending on a locale. Whether that's something worth exposing may be debatable; perhaps it is most useful as an internal implementation mechanism.

I think it's plausible, though, that there could be use cases requiring a specific fixed format (e.g. for compatibility with a legacy system) that can be expressed as a pattern, but might not match any known locale's behavior.

One problem I have with style names "full", "long", "medium", "short" is that they don't tell me what information is actually going to be provided to the user. Application developers often have quite precise requirements which information needs to be shown; even where they're willing to give up control over how the information is going to be presented.

Can the spec say for each of the styles which date or time components will be included? Can it say something about the range of years that can be unambiguously represented, even for calendars with short eras such as the Japanese imperial one?

For comparison, here's a proposal that I made early in the development of the first edition and which was informed by long discussions with a group of web application developers:
http://lindenbergsoftware.com/ecmascript/internationalization-formats.html#DateTimeFormat

This sounds like a use-case that can be addressed via skeleton (essentially equivalent to your dateStyle/timeStyle) or the existing model of verbose options.

@rxaviers - as @jfkthame said, I believe that skeleton is not very different from current verbose options. pattern is not only what OS gives us. It also contains more information (including any arbitrary literals).

@NorbertLindenberg

One problem I have with style names "full", "long", "medium", "short" is that they don't tell me what information is actually going to be provided to the user.

I believe it to be on purpose. If you ask for a "short time format" you hand over control over what bits exactly will be displayed to the API that will chose the best string that will represent this data.
What's more, it may actually differ between locales, and I also believe it to be a good thing.

The way I think about the API extension I'm proposing is that if you want to control what bits you return, that means you want to use verbose options. If you just want the short time, you use style.

When working with developers who will use the API at Mozilla, they don't want to have to decide each time between YMD-medium and WYMD-medium. They just want to display date, and they can tell us that they have medium amount of space for that in the UI.

On top of that experience, I believe it's worth looking at what OSes and ICU are doing because a) their UX teams put effort into figuring out the right balance between flexibility and user experience, and b) this is the level of granularity that OS UX speaks, so it makes any bindings easier.

To sum it up:

  • skeleton is just a compressed way to write the current options
  • Norbert's proposal is another level of granularity between style and skeleton.

In my opinion timeStyle, dateStyle are the most valuable next steps. pattern is just convenient and easy to add option that allows any user-level libraries to design their own patterns and use Intl.DateTimeFormat to format it (including formatToParts).
For those reasons, I'd like to focus on those two now, but I'll refer to the editor for decision. @caridy ?

options.skeleton is essentially duplicating the functionality of the existing verbose options (year, month, day, etc.), just in a more compact (though cryptic) form.

(Currently, the verbose options don't expose as many possibilities as skeletons would, but this is a matter of degree rather than kind.)

Agreed. It's a matter of degree.

With respect to options.timeStyle and options.dateStyle, any thoughts about the 'os-full', 'os-medium', 'os-short', 'os-long' along with the non-attached to host environment 'full', 'medium', 'short', 'long' values? (cc @zbraniecki)

With respect to options.pattern, what really bothers me about this is that it has nothing to do with i18n, i.e., a formatter would return the same output regardless of the locale it was created with. It would be better suited as an static helper function like Intl.DateTimeFormat.formatPattern()??

With respect to options.timeStyle and options.dateStyle, any thoughts about the 'os-full', 'os-medium', 'os-short', 'os-long' along with the non-attached to host environment 'full', 'medium', 'short', 'long' values?

From purely aesthetical POV I think it would be easier on the eyes to do:

let dtf = new Intl.DateTimeFormat(locale, {
  timeStyle: 'long',
  includeOS: true
});

With respect to options.pattern, what really bothers me about this is that it has nothing to do with i18n, i.e., a formatter would return the same output regardless of the locale it was created with. It would be better suited as an static helper function like Intl.DateTimeFormat.formatPattern()??

Not sure if I follow. month names, numerals, hourCycle tokens will be localized in the process of formatting the pattern, no?

Also, the includeOS would also solve the case where you ask for verbose options like hour and minute but let the API determine hourCycle.

With includeOS we could take OS pref into account if the value is true.

From purely aesthetical POV I think it would be easier on the eyes to do:

let dtf = new Intl.DateTimeFormat(locale, {
  timeStyle: 'long',
  includeOS: true
});

Sure, this direction is definitely better. Perhaps the option name can be polished.

Not sure if I follow. month names, numerals, hourCycle tokens will be localized in the process of formatting the pattern, no?

My message above was premature and incorrect. It will indeed localize any names and numerals, but I guess that is it. It won't help with hourCycle (see below) and it won't help with the literals and the order of the fields.

A pattern like {pattern: 'M/d/y'} won't make any sense for en-GB, or for Chinese, and for probably a lot of locales, e.g., new Intl.DateTimeFormat('en-GB', {pattern: 'M/d/y'}).format(new Date()) would be misleading. Similarly, {pattern: "d 'de' MMM"} won't make any sense for most of the locales except for Brazilian Portuguese.

It's not clear to me what the benefit is of exposing this option. Could it help library developers? They still need access to the i18n data to get the actual patterns. I understand this is a good fit for formatting the patterns-from-OS-settings case you exemplified, but this is handled internally in the engine and don't require patterns exposed (please correct me if I'm wrong).

For developers that are not in the i18n field, they usually don't know the difference between skeletons and patterns. I'm afraid that exposing options.pattern would make them think they could use it for i18n, which would lead into bad i18n practices.

hourCycle: at least for implementations that follow CLDR, the different hour ranges are defined by the pattern itself, i.e., hHKk. Note j and J are reserved to skeleton passed to an API, i.e., at the "pattern level" it's expected to be one of hHKk.

A pattern like {pattern: 'M/d/y'} won't make any sense for en-GB, or for Chinese, and for probably a lot of locales, e.g., new Intl.DateTimeFormat('en-GB', {pattern: 'M/d/y'}).format(new Date()) would be misleading. Similarly, {pattern: "d 'de' MMM"} won't make any sense for most of the locales except for Brazilian Portuguese.

Ok, I see where we disagree.

My point on this is that the only way for someone to get en-GB with pattern "M/d/y" is if he explicitly asked for it.
And in that case, it's not misleading.

The only scenario in which someone passes a pattern, is if they got it either from some source (maybe from CLDR, maybe from Windows API, maybe from moment.js, maybe directly written/constructed by the user) and in that scenario they want to format the pattern using the locale we provided.

So, I do believe that we do localize it, so I feel like it should be on an instance, and I believe that there's no difference between verbose options, styles or pattern in almost all of the logic, except of the piece where for verbose options and for styles we need to construct the pattern ourselves.

Does it make sense?

cc @maggiepint who has mentioned patterns being useful for some other cases.

So, there'a s lot going on in this thread and I'm trying to break it apart. What I think though is that you are wondering whether the supporting the combination of a format like M/d/y with a locale makes sense.

IMO it does because people do crazy things with dates, and we see it all the time in Moment. One great example of a non-standard format that one frequently sees is the right sidebar of gmail:

screen shot 2017-02-16 at 10 11 40 am

This particular format is not supported in Moment's locale based formats, nor is it common, but it would be unconstructable without being able to specify pattern and locale.

My personal opinion on the matter - for your standard locale-based formats like "long", stick to CLDR if at all possible.

If people are doing token formatting though, given them enough rope to hang themselves. The bottom lines is that a lot of user experiences do unusual things with dates and we can't predict them all.

Maggie, "Feb 15" is standard. It's the {day: 'numeric', month: 'short'}) style (equivalent to the "MMMd" skeleton). It just seems to me that moment misses that in terms of i18n [1].

Anyway, if you suggest users to use something like {pattern: "MMM d"} themselves, you would need to tell them to actually find the right pattern for each locale. Just to give an idea of how the equivalent pattern changes from locale to locale:

locale skeleton pattern
en MMMd "MMM d"
pt MMMd "d 'de' MMM"
ar MMMd "d MMM"
zh MMMd "M月d日"

The developer would need access to all that data.

Instead, you could recommended them to use the below, which would do the right thing for them.

new Intl.DateTimeFormat('en', {day: 'numeric', month: 'short'}).format(new Date());
// > 'Feb 16'

1: By the way, it would be great if moment supported CLDR (for sure an independent discussion that don't belong here).

Okay, I'm now more clear on everything you have going on here - sorry - didn't know the full scope of the spec, in that it would handle things like that Portuguese locale for you when invoked that way. My bad. I am aware of the fact that that format will look different in different locales, as you showed.

Give me a little more information. If a developer wants to say produce the date string Updated 02:10pm EST, February 16 (which I just took from CNN.com), how does that play out?

Keep in mind, this string probably isn't meant to be displayed in languages other than english in an automagical way - CNN.com's Spanish and Arabic layouts clearly have different designs that don't use this format - but it doesn't prevent the developer from needing to use the Intl API to access the month name (and the time zone abbreviation for that matter).

@maggiepint - so, this is also covered by the current DateTimeFormat spec. The user would probably ask for

new Intl.DateTimeFormat('en', {hour: 'numeric', minute: 'numeric', timeZoneName: 'short',
month: 'long', day: 'numeric'}).format(new Date());

Notice, this would order elements differently depending on locale. For en-US, in CLDR driven implementations, it would not order it the way the CNN does. I believe it to be the proper behavior, as CLDR is just better at knowing what pattern works for which language.

If CNN want to have more control, it could separately format date and time and then join them:

let time = new Intl.DateTimeFormat('en', {hour: 'numeric', minute: 'numeric',
  timeZoneName: 'short'}).format(new Date());
let date = new Intl.DateTimeFormat('en', {month: 'long', day: 'numeric'}).format(new Date());
let str = new Intl.ListFormat('en', {type: 'numeric'}).format(time, date);

My argument is that CNN should not attempt to manually control the order, as they are unlikely to pick the right order for all locales, and hardcoding it because it works in one is rather a bad choice.

Alternatively yet, if they want to control the order directly, they could use some third party library to construct a pattern, and with the proposed here pattern option they could build something that perfectly matches what they want.

The value of discussed here pattern option is exactly to use DateTimeFormat logic of resolving the pattern, but allowing a third-party to design the pattern.
One use case, is for someone to use external library for designing, another is to use OS-provided pattern.

The other discussed here feature is style (timeStyle and dateStyle) which aim to simplify the most common cases, where the user does not want to dwell in details, and just wants a short time, or long date.

Other proposed ideas are to add skeleton option, or some more granular variant of style. I tried to explain my reason behind not pursuing those options above.

I pulled the trigger and created https://github.com/zbraniecki/proposal-ecma402-datetime-style

I'll be seeking Stage 1 status at the next TC39 meeting unless anyone raises objections here.

The date/time style proposal has reached stage 1 as of today.
The committee asked us to look more into the question of whether this should be a "high level" API or should we instead try to enable lower level building blocks.
If I understand TC39 feedback, an idea might be to do sth like this:

let options = Intl.DateTimeFormat.getOptionsForStyle('date', 'short');
let dtf = new Intl.DateTimeFormat('en', options);

which would cover similar features, but on the more granular level.

@maggiepint said she'll try to help us get feedback from moment.js, and @rxaviers is looking at it from the perspective of Globalize.js

@zbraniecki I don't see the proposal linked from the README for stage 1, can you send a PR for that?

I filed #189 because I don't see any issue filed on options.skeleton.

thanks @zbraniecki, let's transfer that repo to TC39 as well.

Should we close this issue since we already have dateStyle and timeStyle proposal reach stage 3?

We can close this when the corresponding proposal reaches Stage 4 and is merged. This issue has the tag "active proposal".

The number of remaining active items is as follows:

The first one is an editorial bug that I'm working on.
The latter two are decisions for us to make and I'd like to ask for your input.

Assuming we can come up with a consensus on the latter two over the upcoming ECMA402 call, I plan to propose this for Stage 4 advancement soon.

dateStyle/timeStyle proposal has been approved for Stage 4 at today's TC39 meeting!

Why do you like to make such a big mess? I think it would be a little easier to kind of

(new Date())->format('d-m-Y'); // 24-07-2020

new Intl.DateTimeFormat("es",{ISO:'d-m-Y'}).format(Date.now()); // 24-07-2020

I apologize if this doesn't go here, but I don't know where I can make a request or show my disagreement with Javascript and the dates.

@chiqui3d in regards to:

(new Date())->format('d-m-Y'); // 24/07/2020
new Intl.DateTimeFormat("es",{ISO:'d-m-Y'}).format(Date.now()); // 24/07/2020

It doesn't go here because it goes here: #190 - please follow up there, thanks!

I apologize if this doesn't go here, but I don't know where I can make a request or show my disagreement with Javascript and the dates.

What you're suggesting is not internationalization date pattern. ISO is universal and you can format Date to it without Intl.

That's how I do it in PHP, it's simple and easy, in Javascript I don't understand how it's so complicated.

https://www.php.net/manual/en/function.date.php
https://www.php.net/manual/en/datetime.format.php#refsect1-datetime.format-examples

That's how I do it in PHP, it's simple and easy, in Javascript I don't understand how it's so complicated.

https://www.php.net/manual/en/function.date.php
https://www.php.net/manual/en/datetime.format.php#refsect1-datetime.format-examples

That means, if your caller need to support 240 locales, the engineer need to code 240 patterns, one for each locale, in order to use your system to support that 240 locales properly. While it might be easy for you, or any engineer to figure out the first 2-5 patterns, it will be hard to find out the 6th to 240th.

(new Date())->format('d-m-Y'); // 24/07/2020

Looking at this again: why are the hyphens there? Just have "dmY" => 24/07/2020 and it's just a skeleton, as discussed earlier. (dmY=mYd etc; order does not matter)

That's how I do it in PHP, it's simple and easy, in Javascript I don't understand how it's so complicated.

https://www.php.net/manual/en/function.date.php

https://www.php.net/manual/en/datetime.format.php#refsect1-datetime.format-examples

The php docs say "This method does not use locales. All output is in English." So as frank said, this has little to do with internationalization.

I set the wrong example, it should be // 24-07-2020

But if it can't be, then nothing will continue to use third party Javascript libraries like

https://date-fns.org/v2.15.0/docs/format

var result = format(Date.now(), 'dd-MM-yyyy')
//=> 24-07-2020

https://momentjs.com/

moment().format('DD-MM-YYYY'); // 24-07-2020

Everyone has their differences, so I'm looking for something native.

I set the wrong example, it should be // 24-07-2020

But if it can't be, then nothing will continue to use third party Javascript libraries like

https://date-fns.org/v2.15.0/docs/format

var result = format(Date.now(), 'dd-MM-yyyy')
//=> 24-07-2020

https://momentjs.com/

moment().format('DD-MM-YYYY'); // 24-07-2020

Everyone has their differences, so I'm looking for something native.

What you propose, is to let the library to allow the programmer to control exactly what the pattern is. The problem is, the pattern is different, depending on each locale, so, by providing such "low level" API, it encourage the developer to program their code based on lower level construct, and make the software much harder to extend to locales/countries, because, under such paradigm, the developer need to know the pattern for ALL the pattern of the locales they need to support, therefore, it push the responsibility of figuring out that cultural information to the developers, instead of depending on what the cultural information the library collected from many linguists. For example, could you name the date patterns of the top 80 locales your software are going to support? and how long do you think it will take for you, personally, not some magic guru you believe your boss is going to hire to help you, to find these 80 patterns? I am sure you can easily list the first 3-7 (say US, UK, Canada, AU, NZ, and maybe even France and Germany). But it will be very difficult after that, right? How about in Thailand, China, Japan, Armenia? If you think it is easy to figure out, try some of them I listed above and tell me how long it take for you to be sure and how many Unit test you need to write to verify that.