tc39 / ecma402

Status, process, and documents for ECMA 402
https://tc39.es/ecma402/
Other
537 stars 105 forks source link

How to show different language time formats in different countries #703

Open qcyblm opened 2 years ago

qcyblm commented 2 years ago

Such as new Date().toLocaleString()

new Date().toLocaleString("zh",{year:"numeric",month:"long",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",timeZoneName:"long"});
2022年8月9日 中国标准时间 09:37:52
new Date().toLocaleString("zh-u-ca-chinese-nu-hanidec", {year:"numeric",month:"long",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",timeZoneName:"long"});
二〇二二壬寅年七月一二 中国标准时间 〇九:三七:五二
new Date().toLocaleString("en",{year:"numeric",month:"long",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",timeZoneName:"long"});
August 9, 2022 at 9:37:52 AM China Standard Time
new Date().toLocaleString("ja",{year:"numeric",month:"long",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",timeZoneName:"long"});
2022年8月9日 9時37分52秒 中国標準時
qcyblm commented 2 years ago

When you specify a calendar, you can only see the actual calendar time if you write out the parameters separately


const data = Temporal.Now.zonedDateTimeISO('Asia/Shanghai').withCalendar("chinese")
console.log(data.toString());
// 2022-08-09T09:49:41.945660009+08:00[Asia/Shanghai][u-ca=chinese]
console.log( data.year,data.month,data.day );
// 2022 7 12
``` js
justingrant commented 2 years ago

You can also use toLocaleString() on any Temporal object. The parameters are the same as Date.prototype.toLocaleString. Example:

zdt = Temporal.Now.zonedDateTime('chinese', 'Asia/Shanghai');
zdt.toLocaleString(
  "zh-u-ca-chinese-nu-hanidec",
  {year:"numeric",month:"long",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",timeZoneName:"long"});
// '七月一二日 中国标准时间 一一:五二:一四'
qcyblm commented 2 years ago

@justingrant The lack of year

justingrant commented 2 years ago

@qcyblm The root cause is of the problem is that the Intl.DateTimeFormat.prototype.resolvedOptions() method drops the year. We rely on that method in the polyfill. Because the output is the same in Chrome and Firefox, I suspect that the problem is in the upstream CLDR data that Intl.DateTimeFormat uses to power its behavior.

@sffc, is this a CLDR bug, and if so could we get your help to file an issue in the right place(s) to fix it?


locale = 'zh-u-ca-chinese-nu-hanidec';
formattingProps = {year:"numeric",month:"long",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",timeZoneName:"long"};
date = new Date('2020-01-01T00:00:00Z');
dtf = new Intl.DateTimeFormat(locale, formattingProps);
original = dtf.format(date);
ro = dtf.resolvedOptions();
// {"locale":"zh-u-ca-chinese-nu-hanidec","calendar":"chinese","numberingSystem":"hanidec","timeZone":"America/Los_Angeles","hourCycle":"h23","hour12":false,"month":"long","day":"numeric","hour":"numeric","minute":"2-digit","second":"2-digit","timeZoneName":"long"}
// note that `ro.year` is missing, which causes the unexpected behavior below
dtfNew = new Intl.DateTimeFormat(locale, ro);
usingResolvedOptions = dtfNew.format(date);
console.log(JSON.stringify({original, usingResolvedOptions}));
// expected: both strings are the same
// actual: 
// {
//   "original":   "二〇一九己亥年腊月六 北美太平洋标准时间 一六:〇〇:〇〇",
//   "usingResolvedOptions": "腊月六日 北美太平洋标准时间 一六:〇〇:〇〇"
// }
``
sffc commented 2 years ago

In Temporal: .year returns the arithmetic year; .eraYear returns the year within the era.

In CLDR: Formatting of the chinese calendar uses the cyclic year field in CLDR patterns.

Does it work as expected if the DTF option is { dateStyle: "full" }? If it only breaks when { year: "numeric", month: "long", day: "numeric" } is used, then this would be (yet another) bug in the components bag resolution in either ICU or ECMA-402.

sffc commented 2 years ago

@qcyblm: Please post a test case with the expected result (a line of code that does not behave as expected, with how you expect it to behave).

justingrant commented 2 years ago

Does it work as expected if the DTF option is { dateStyle: "full" }?

The output using { dateStyle: "full" } is below. Both strings are different from the output when each component is listed separately in https://github.com/tc39/ecma402/issues/703.

{
  "original":            "二〇一九己亥年腊月初六星期二",
  "usingResolvedOptions":"二〇一九己亥年腊月初六星期二"
}

@qcyblm: Please post a test case with the expected result (a line of code that does not behave as expected, with how you expect it to behave).

Note that the code in the test case should not use Temporal but should use Intl.DateTimeFormat.prototype.format because that seems to be the underlying issue here. Using Temporal would just distract from the real bug.

anba commented 2 years ago

This sounds similar to https://bugzilla.mozilla.org/show_bug.cgi?id=1298794 (upstream: https://unicode-org.atlassian.net/browse/ICU-13518). But in addition to that round tripping issue, we also have a "related year" component here. I know that the SpiderMonkey implementation doesn't map the Date Field Symbol r when computing the resolved options, but I can't remember anymore if that's intentional or just an oversight.

anba commented 2 years ago

Okay, I've just tested this. Handling r fixes the output, now I have to somehow verify that that's actually spec-compliant and won't introduce any other bugs...

sffc commented 2 years ago

Yes, if the issue is only that resolvedOptions doesn't round-trip, then the bug is probably around failing to match alternative year fields in the resolved pattern. There are 5 year fields listed in UTS 35: YyUur. https://www.unicode.org/reports/tr35/tr35-dates.html#dfst-year

sffc commented 2 years ago

I transferred the issue to the ECMA-402 repository. I think we should at least add a test for the behavior, if not clarify the spec.

anba commented 2 years ago

There are 5 year fields listed in UTS 35: YyUur.

I wonder how to map standalone U, because the year property for resolvedOptions() can only be either "2-digit" or "numeric".

js> new Intl.DateTimeFormat("de-u-ca-chinese", {year:"numeric"}).format()
"ren-yin"
js> new Intl.DateTimeFormat("de-u-ca-chinese", {year:"numeric"}).resolvedOptions()
Object { locale: "de-u-ca-chinese", calendar: "chinese", numberingSystem: "latn", timeZone: "Europe/Berlin" }
sffc commented 6 months ago

2024-03-28 TG2 notes: https://github.com/tc39/ecma402/blob/main/meetings/notes-2024-03-28.md#how-to-show-different-language-time-formats-in-different-countries-703

sffc commented 1 month ago

Note that adding {year: "numeric"} to the resolvedOptions seems to make the pattern round-trip. I think we should just do that when relatedYear is in the output.

ben-allen commented 1 month ago

Writing short PR to do the above.