LibUnicode+LibJS: Normalize spaces in formatted date-time strings

ICU 72 began using non-ASCII spaces in some formatted date-time strings.
Every major browser has found that this introduced major breakage in web
compatibility, as many sites and tools expect ASCII spaces. This patch
removes these non-ASCII spaces in the same manner as the major engines.
Such behavior is also tested by WPT.
This commit is contained in:
Timothy Flynn 2024-08-01 11:30:17 -04:00 committed by Andreas Kling
parent 9e22233be9
commit ee00730225
Notes: github-actions[bot] 2024-08-02 06:08:16 +00:00
7 changed files with 112 additions and 97 deletions

View file

@ -31,8 +31,8 @@ describe("correct behavior", () => {
const d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
test("defaults to date and time", () => {
expect(d0.toLocaleString("en", { timeZone: "UTC" })).toBe("12/7/2021, 5:40:50\u202fPM");
expect(d1.toLocaleString("en", { timeZone: "UTC" })).toBe("1/23/1989, 7:08:09\u202fAM");
expect(d0.toLocaleString("en", { timeZone: "UTC" })).toBe("12/7/2021, 5:40:50 PM");
expect(d1.toLocaleString("en", { timeZone: "UTC" })).toBe("1/23/1989, 7:08:09 AM");
expect(d0.toLocaleString("ar", { timeZone: "UTC" })).toBe("٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م");
expect(d1.toLocaleString("ar", { timeZone: "UTC" })).toBe("٢٣‏/١/١٩٨٩، ٧:٠٨:٠٩ ص");
@ -51,12 +51,8 @@ describe("correct behavior", () => {
});
test("timeStyle may be set", () => {
expect(d0.toLocaleString("en", { timeStyle: "short", timeZone: "UTC" })).toBe(
"5:40\u202fPM"
);
expect(d1.toLocaleString("en", { timeStyle: "short", timeZone: "UTC" })).toBe(
"7:08\u202fAM"
);
expect(d0.toLocaleString("en", { timeStyle: "short", timeZone: "UTC" })).toBe("5:40 PM");
expect(d1.toLocaleString("en", { timeStyle: "short", timeZone: "UTC" })).toBe("7:08 AM");
expect(d0.toLocaleString("ar", { timeStyle: "short", timeZone: "UTC" })).toBe("٥:٤٠ م");
expect(d1.toLocaleString("ar", { timeStyle: "short", timeZone: "UTC" })).toBe("٧:٠٨ ص");

View file

@ -37,8 +37,8 @@ describe("correct behavior", () => {
const d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
test("defaults to time", () => {
expect(d0.toLocaleTimeString("en", { timeZone: "UTC" })).toBe("5:40:50\u202fPM");
expect(d1.toLocaleTimeString("en", { timeZone: "UTC" })).toBe("7:08:09\u202fAM");
expect(d0.toLocaleTimeString("en", { timeZone: "UTC" })).toBe("5:40:50 PM");
expect(d1.toLocaleTimeString("en", { timeZone: "UTC" })).toBe("7:08:09 AM");
expect(d0.toLocaleTimeString("ar", { timeZone: "UTC" })).toBe("٥:٤٠:٥٠ م");
expect(d1.toLocaleTimeString("ar", { timeZone: "UTC" })).toBe("٧:٠٨:٠٩ ص");
@ -46,10 +46,10 @@ describe("correct behavior", () => {
test("timeStyle may be set", () => {
expect(d0.toLocaleTimeString("en", { timeStyle: "long", timeZone: "UTC" })).toBe(
"5:40:50\u202fPM UTC"
"5:40:50 PM UTC"
);
expect(d1.toLocaleTimeString("en", { timeStyle: "long", timeZone: "UTC" })).toBe(
"7:08:09\u202fAM UTC"
"7:08:09 AM UTC"
);
expect(d0.toLocaleTimeString("ar", { timeStyle: "long", timeZone: "UTC" })).toBe(

View file

@ -62,10 +62,10 @@ describe("dateStyle", () => {
describe("timeStyle", () => {
// prettier-ignore
const data = [
{ time: "full", en0: "5:40:50\u202fPM Coordinated Universal Time", en1: "7:08:09\u202fAM Coordinated Universal Time", ar0: "٥:٤٠:٥٠ م التوقيت العالمي المنسق", ar1: "٧:٠٨:٠٩ ص التوقيت العالمي المنسق" },
{ time: "long", en0: "5:40:50\u202fPM UTC", en1: "7:08:09\u202fAM UTC", ar0: "٥:٤٠:٥٠ م UTC", ar1: "٧:٠٨:٠٩ ص UTC" },
{ time: "medium", en0: "5:40:50\u202fPM", en1: "7:08:09\u202fAM", ar0: "٥:٤٠:٥٠ م", ar1: "٧:٠٨:٠٩ ص" },
{ time: "short", en0: "5:40\u202fPM", en1: "7:08\u202fAM", ar0: "٥:٤٠ م", ar1: "٧:٠٨ ص" },
{ time: "full", en0: "5:40:50 PM Coordinated Universal Time", en1: "7:08:09 AM Coordinated Universal Time", ar0: "٥:٤٠:٥٠ م التوقيت العالمي المنسق", ar1: "٧:٠٨:٠٩ ص التوقيت العالمي المنسق" },
{ time: "long", en0: "5:40:50 PM UTC", en1: "7:08:09 AM UTC", ar0: "٥:٤٠:٥٠ م UTC", ar1: "٧:٠٨:٠٩ ص UTC" },
{ time: "medium", en0: "5:40:50 PM", en1: "7:08:09 AM", ar0: "٥:٤٠:٥٠ م", ar1: "٧:٠٨:٠٩ ص" },
{ time: "short", en0: "5:40 PM", en1: "7:08 AM", ar0: "٥:٤٠ م", ar1: "٧:٠٨ ص" },
];
test("all", () => {
@ -84,22 +84,22 @@ describe("timeStyle", () => {
describe("dateStyle + timeStyle", () => {
// prettier-ignore
const data = [
{ date: "full", time: "full", en: "Tuesday, December 7, 2021 at 5:40:50\u202fPM Coordinated Universal Time", ar: "الثلاثاء، ٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م التوقيت العالمي المنسق" },
{ date: "full", time: "long", en: "Tuesday, December 7, 2021 at 5:40:50\u202fPM UTC", ar: "الثلاثاء، ٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م UTC" },
{ date: "full", time: "medium", en: "Tuesday, December 7, 2021 at 5:40:50\u202fPM", ar: "الثلاثاء، ٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م" },
{ date: "full", time: "short", en: "Tuesday, December 7, 2021 at 5:40\u202fPM", ar: "الثلاثاء، ٧ ديسمبر ٢٠٢١ في ٥:٤٠ م" },
{ date: "long", time: "full", en: "December 7, 2021 at 5:40:50\u202fPM Coordinated Universal Time", ar: "٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م التوقيت العالمي المنسق" },
{ date: "long", time: "long", en: "December 7, 2021 at 5:40:50\u202fPM UTC", ar: "٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م UTC" },
{ date: "long", time: "medium", en: "December 7, 2021 at 5:40:50\u202fPM", ar: "٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م" },
{ date: "long", time: "short", en: "December 7, 2021 at 5:40\u202fPM", ar: "٧ ديسمبر ٢٠٢١ في ٥:٤٠ م" },
{ date: "medium", time: "full", en: "Dec 7, 2021, 5:40:50\u202fPM Coordinated Universal Time", ar: "٠٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م التوقيت العالمي المنسق" },
{ date: "medium", time: "long", en: "Dec 7, 2021, 5:40:50\u202fPM UTC", ar: "٠٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م UTC" },
{ date: "medium", time: "medium", en: "Dec 7, 2021, 5:40:50\u202fPM", ar: "٠٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م" },
{ date: "medium", time: "short", en: "Dec 7, 2021, 5:40\u202fPM", ar: "٠٧/١٢‏/٢٠٢١، ٥:٤٠ م" },
{ date: "short", time: "full", en: "12/7/21, 5:40:50\u202fPM Coordinated Universal Time", ar: "٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م التوقيت العالمي المنسق" },
{ date: "short", time: "long", en: "12/7/21, 5:40:50\u202fPM UTC", ar: "٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م UTC" },
{ date: "short", time: "medium", en: "12/7/21, 5:40:50\u202fPM", ar: "٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م" },
{ date: "short", time: "short", en: "12/7/21, 5:40\u202fPM", ar: "٧/١٢‏/٢٠٢١، ٥:٤٠ م" },
{ date: "full", time: "full", en: "Tuesday, December 7, 2021 at 5:40:50 PM Coordinated Universal Time", ar: "الثلاثاء، ٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م التوقيت العالمي المنسق" },
{ date: "full", time: "long", en: "Tuesday, December 7, 2021 at 5:40:50 PM UTC", ar: "الثلاثاء، ٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م UTC" },
{ date: "full", time: "medium", en: "Tuesday, December 7, 2021 at 5:40:50 PM", ar: "الثلاثاء، ٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م" },
{ date: "full", time: "short", en: "Tuesday, December 7, 2021 at 5:40 PM", ar: "الثلاثاء، ٧ ديسمبر ٢٠٢١ في ٥:٤٠ م" },
{ date: "long", time: "full", en: "December 7, 2021 at 5:40:50 PM Coordinated Universal Time", ar: "٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م التوقيت العالمي المنسق" },
{ date: "long", time: "long", en: "December 7, 2021 at 5:40:50 PM UTC", ar: "٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م UTC" },
{ date: "long", time: "medium", en: "December 7, 2021 at 5:40:50 PM", ar: "٧ ديسمبر ٢٠٢١ في ٥:٤٠:٥٠ م" },
{ date: "long", time: "short", en: "December 7, 2021 at 5:40 PM", ar: "٧ ديسمبر ٢٠٢١ في ٥:٤٠ م" },
{ date: "medium", time: "full", en: "Dec 7, 2021, 5:40:50 PM Coordinated Universal Time", ar: "٠٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م التوقيت العالمي المنسق" },
{ date: "medium", time: "long", en: "Dec 7, 2021, 5:40:50 PM UTC", ar: "٠٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م UTC" },
{ date: "medium", time: "medium", en: "Dec 7, 2021, 5:40:50 PM", ar: "٠٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م" },
{ date: "medium", time: "short", en: "Dec 7, 2021, 5:40 PM", ar: "٠٧/١٢‏/٢٠٢١، ٥:٤٠ م" },
{ date: "short", time: "full", en: "12/7/21, 5:40:50 PM Coordinated Universal Time", ar: "٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م التوقيت العالمي المنسق" },
{ date: "short", time: "long", en: "12/7/21, 5:40:50 PM UTC", ar: "٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م UTC" },
{ date: "short", time: "medium", en: "12/7/21, 5:40:50 PM", ar: "٧/١٢‏/٢٠٢١، ٥:٤٠:٥٠ م" },
{ date: "short", time: "short", en: "12/7/21, 5:40 PM", ar: "٧/١٢‏/٢٠٢١، ٥:٤٠ م" },
];
test("all", () => {
@ -369,8 +369,8 @@ describe("dayPeriod", () => {
describe("hour", () => {
// prettier-ignore
const data = [
{ hour: "2-digit", en0: "05\u202fPM", en1: "07\u202fAM", ar0: "٠٥ م", ar1: "٠٧ ص" },
{ hour: "numeric", en0: "5\u202fPM", en1: "7\u202fAM", ar0: "٥ م", ar1: "٧ ص" },
{ hour: "2-digit", en0: "05 PM", en1: "07 AM", ar0: "٠٥ م", ar1: "٠٧ ص" },
{ hour: "numeric", en0: "5 PM", en1: "7 AM", ar0: "٥ م", ar1: "٧ ص" },
];
test("all", () => {
@ -389,8 +389,8 @@ describe("hour", () => {
describe("minute", () => {
// prettier-ignore
const data = [
{ minute: "2-digit", en0: "5:40\u202fPM", en1: "7:08\u202fAM", ar0: "٥:٤٠ م", ar1: "٧:٠٨ ص" },
{ minute: "numeric", en0: "5:40\u202fPM", en1: "7:08\u202fAM", ar0: "٥:٤٠ م", ar1: "٧:٠٨ ص" },
{ minute: "2-digit", en0: "5:40 PM", en1: "7:08 AM", ar0: "٥:٤٠ م", ar1: "٧:٠٨ ص" },
{ minute: "numeric", en0: "5:40 PM", en1: "7:08 AM", ar0: "٥:٤٠ م", ar1: "٧:٠٨ ص" },
];
test("all", () => {
@ -545,8 +545,8 @@ describe("non-Gregorian calendars", () => {
timeStyle: "long",
timeZone: "UTC",
});
expect(en.format(d0)).toBe("3 Tevet 5782 at 5:40:50\u202fPM UTC");
expect(en.format(d1)).toBe("17 Shevat 5749 at 7:08:09\u202fAM UTC");
expect(en.format(d0)).toBe("3 Tevet 5782 at 5:40:50 PM UTC");
expect(en.format(d1)).toBe("17 Shevat 5749 at 7:08:09 AM UTC");
const ar = new Intl.DateTimeFormat("ar-u-ca-hebrew", {
dateStyle: "long",
@ -563,8 +563,8 @@ describe("non-Gregorian calendars", () => {
timeStyle: "long",
timeZone: "UTC",
});
expect(en.format(d0)).toBe("Eleventh Month 4, 2021(xin-chou) at 5:40:50\u202fPM UTC");
expect(en.format(d1)).toBe("Twelfth Month 16, 1988(wu-chen) at 7:08:09\u202fAM UTC");
expect(en.format(d0)).toBe("Eleventh Month 4, 2021(xin-chou) at 5:40:50 PM UTC");
expect(en.format(d1)).toBe("Twelfth Month 16, 1988(wu-chen) at 7:08:09 AM UTC");
const zh = new Intl.DateTimeFormat("zh-u-ca-chinese", {
dateStyle: "long",

View file

@ -69,8 +69,8 @@ describe("equal dates are squashed", () => {
second: "2-digit",
timeZone: "UTC",
});
expect(en.formatRange(d0, d0)).toBe("7:08:09\u202fAM");
expect(en.formatRange(d1, d1)).toBe("5:40:50\u202fPM");
expect(en.formatRange(d0, d0)).toBe("7:08:09 AM");
expect(en.formatRange(d1, d1)).toBe("5:40:50 PM");
const ja = new Intl.DateTimeFormat("ja", {
hour: "numeric",
@ -92,8 +92,8 @@ describe("equal dates are squashed", () => {
second: "2-digit",
timeZone: "UTC",
});
expect(en.formatRange(d0, d0)).toBe("January 23, 1989 at 7:08:09\u202fAM");
expect(en.formatRange(d1, d1)).toBe("December 07, 2021 at 5:40:50\u202fPM");
expect(en.formatRange(d0, d0)).toBe("January 23, 1989 at 7:08:09 AM");
expect(en.formatRange(d1, d1)).toBe("December 07, 2021 at 5:40:50 PM");
const ja = new Intl.DateTimeFormat("ja", {
year: "numeric",
@ -114,8 +114,8 @@ describe("equal dates are squashed", () => {
timeStyle: "medium",
timeZone: "UTC",
});
expect(en.formatRange(d0, d0)).toBe("Monday, January 23, 1989 at 7:08:09\u202fAM");
expect(en.formatRange(d1, d1)).toBe("Tuesday, December 7, 2021 at 5:40:50\u202fPM");
expect(en.formatRange(d0, d0)).toBe("Monday, January 23, 1989 at 7:08:09 AM");
expect(en.formatRange(d1, d1)).toBe("Tuesday, December 7, 2021 at 5:40:50 PM");
const ja = new Intl.DateTimeFormat("ja", {
dateStyle: "full",
@ -130,10 +130,10 @@ describe("equal dates are squashed", () => {
describe("dateStyle", () => {
// prettier-ignore
const data = [
{ date: "full", en: "Monday, January 23, 1989\u2009\u2009Tuesday, December 7, 2021", ja: "1989/01/23(月曜日)2021/12/07(火曜日)" },
{ date: "long", en: "January 23, 1989\u2009\u2009December 7, 2021", ja: "1989/01/232021/12/07" },
{ date: "medium", en: "Jan 23, 1989\u2009\u2009Dec 7, 2021", ja: "1989/01/232021/12/07" },
{ date: "short", en: "1/23/89\u2009\u200912/7/21", ja: "1989/01/232021/12/07" },
{ date: "full", en: "Monday, January 23, 1989 Tuesday, December 7, 2021", ja: "1989/01/23(月曜日)2021/12/07(火曜日)" },
{ date: "long", en: "January 23, 1989 December 7, 2021", ja: "1989/01/232021/12/07" },
{ date: "medium", en: "Jan 23, 1989 Dec 7, 2021", ja: "1989/01/232021/12/07" },
{ date: "short", en: "1/23/89 12/7/21", ja: "1989/01/232021/12/07" },
];
test("all", () => {
@ -154,9 +154,7 @@ describe("dateStyle", () => {
test("dates in reverse order", () => {
const en = new Intl.DateTimeFormat("en", { dateStyle: "full", timeZone: "UTC" });
expect(en.formatRange(d1, d0)).toBe(
"Tuesday, December 7, 2021\u2009\u2009Monday, January 23, 1989"
);
expect(en.formatRange(d1, d0)).toBe("Tuesday, December 7, 2021 Monday, January 23, 1989");
const ja = new Intl.DateTimeFormat("ja", { dateStyle: "full", timeZone: "UTC" });
expect(ja.formatRange(d1, d0)).toBe("2021/12/07(火曜日)1989/01/23(月曜日)");
@ -166,10 +164,10 @@ describe("dateStyle", () => {
describe("timeStyle", () => {
// prettier-ignore
const data = [
{ time: "full", en: "1/23/1989, 7:08:09\u202fAM Coordinated Universal Time\u2009\u200912/7/2021, 5:40:50\u202fPM Coordinated Universal Time", ja: "1989/1/23 7時08分09秒 協定世界時2021/12/7 17時40分50秒 協定世界時" },
{ time: "long", en: "1/23/1989, 7:08:09\u202fAM UTC\u2009\u200912/7/2021, 5:40:50\u202fPM UTC", ja: "1989/1/23 7:08:09 UTC2021/12/7 17:40:50 UTC" },
{ time: "medium", en: "1/23/1989, 7:08:09\u202fAM\u2009\u200912/7/2021, 5:40:50\u202fPM", ja: "1989/1/23 7:08:092021/12/7 17:40:50" },
{ time: "short", en: "1/23/1989, 7:08\u202fAM\u2009\u200912/7/2021, 5:40\u202fPM", ja: "1989/1/23 7:082021/12/7 17:40" },
{ time: "full", en: "1/23/1989, 7:08:09 AM Coordinated Universal Time 12/7/2021, 5:40:50 PM Coordinated Universal Time", ja: "1989/1/23 7時08分09秒 協定世界時2021/12/7 17時40分50秒 協定世界時" },
{ time: "long", en: "1/23/1989, 7:08:09 AM UTC 12/7/2021, 5:40:50 PM UTC", ja: "1989/1/23 7:08:09 UTC2021/12/7 17:40:50 UTC" },
{ time: "medium", en: "1/23/1989, 7:08:09 AM 12/7/2021, 5:40:50 PM", ja: "1989/1/23 7:08:092021/12/7 17:40:50" },
{ time: "short", en: "1/23/1989, 7:08 AM 12/7/2021, 5:40 PM", ja: "1989/1/23 7:082021/12/7 17:40" },
];
test("all", () => {
@ -186,22 +184,22 @@ describe("timeStyle", () => {
describe("dateStyle + timeStyle", () => {
// prettier-ignore
const data = [
{ date: "full", time: "full", en: "Monday, January 23, 1989 at 7:08:09\u202fAM Coordinated Universal Time\u2009\u2009Tuesday, December 7, 2021 at 5:40:50\u202fPM Coordinated Universal Time", ja: "1989/1/23月曜日 7時08分09秒 協定世界時2021/12/7火曜日 17時40分50秒 協定世界時" },
{ date: "full", time: "long", en: "Monday, January 23, 1989 at 7:08:09\u202fAM UTC\u2009\u2009Tuesday, December 7, 2021 at 5:40:50\u202fPM UTC", ja: "1989/1/23月曜日 7:08:09 UTC2021/12/7火曜日 17:40:50 UTC" },
{ date: "full", time: "medium", en: "Monday, January 23, 1989 at 7:08:09\u202fAM\u2009\u2009Tuesday, December 7, 2021 at 5:40:50\u202fPM", ja: "1989/1/23月曜日 7:08:092021/12/7火曜日 17:40:50" },
{ date: "full", time: "short", en: "Monday, January 23, 1989 at 7:08\u202fAM\u2009\u2009Tuesday, December 7, 2021 at 5:40\u202fPM", ja: "1989/1/23月曜日 7:082021/12/7火曜日 17:40" },
{ date: "long", time: "full", en: "January 23, 1989 at 7:08:09\u202fAM Coordinated Universal Time\u2009\u2009December 7, 2021 at 5:40:50\u202fPM Coordinated Universal Time", ja: "1989/1/23 7時08分09秒 協定世界時2021/12/7 17時40分50秒 協定世界時" },
{ date: "long", time: "long", en: "January 23, 1989 at 7:08:09\u202fAM UTC\u2009\u2009December 7, 2021 at 5:40:50\u202fPM UTC", ja: "1989/1/23 7:08:09 UTC2021/12/7 17:40:50 UTC" },
{ date: "long", time: "medium", en: "January 23, 1989 at 7:08:09\u202fAM\u2009\u2009December 7, 2021 at 5:40:50\u202fPM", ja: "1989/1/23 7:08:092021/12/7 17:40:50" },
{ date: "long", time: "short", en: "January 23, 1989 at 7:08\u202fAM\u2009\u2009December 7, 2021 at 5:40\u202fPM", ja: "1989/1/23 7:082021/12/7 17:40" },
{ date: "medium", time: "full", en: "Jan 23, 1989, 7:08:09\u202fAM Coordinated Universal Time\u2009\u2009Dec 7, 2021, 5:40:50\u202fPM Coordinated Universal Time", ja: "1989/01/23 7時08分09秒 協定世界時2021/12/07 17時40分50秒 協定世界時" },
{ date: "medium", time: "long", en: "Jan 23, 1989, 7:08:09\u202fAM UTC\u2009\u2009Dec 7, 2021, 5:40:50\u202fPM UTC", ja: "1989/01/23 7:08:09 UTC2021/12/07 17:40:50 UTC" },
{ date: "medium", time: "medium", en: "Jan 23, 1989, 7:08:09\u202fAM\u2009\u2009Dec 7, 2021, 5:40:50\u202fPM", ja: "1989/01/23 7:08:092021/12/07 17:40:50" },
{ date: "medium", time: "short", en: "Jan 23, 1989, 7:08\u202fAM\u2009\u2009Dec 7, 2021, 5:40\u202fPM", ja: "1989/01/23 7:082021/12/07 17:40" },
{ date: "short", time: "full", en: "1/23/89, 7:08:09\u202fAM Coordinated Universal Time\u2009\u200912/7/21, 5:40:50\u202fPM Coordinated Universal Time", ja: "1989/01/23 7時08分09秒 協定世界時2021/12/07 17時40分50秒 協定世界時" },
{ date: "short", time: "long", en: "1/23/89, 7:08:09\u202fAM UTC\u2009\u200912/7/21, 5:40:50\u202fPM UTC", ja: "1989/01/23 7:08:09 UTC2021/12/07 17:40:50 UTC" },
{ date: "short", time: "medium", en: "1/23/89, 7:08:09\u202fAM\u2009\u200912/7/21, 5:40:50\u202fPM", ja: "1989/01/23 7:08:092021/12/07 17:40:50" },
{ date: "short", time: "short", en: "1/23/89, 7:08\u202fAM\u2009\u200912/7/21, 5:40\u202fPM", ja: "1989/01/23 7:082021/12/07 17:40" },
{ date: "full", time: "full", en: "Monday, January 23, 1989 at 7:08:09 AM Coordinated Universal Time Tuesday, December 7, 2021 at 5:40:50 PM Coordinated Universal Time", ja: "1989/1/23月曜日 7時08分09秒 協定世界時2021/12/7火曜日 17時40分50秒 協定世界時" },
{ date: "full", time: "long", en: "Monday, January 23, 1989 at 7:08:09 AM UTC Tuesday, December 7, 2021 at 5:40:50 PM UTC", ja: "1989/1/23月曜日 7:08:09 UTC2021/12/7火曜日 17:40:50 UTC" },
{ date: "full", time: "medium", en: "Monday, January 23, 1989 at 7:08:09 AM Tuesday, December 7, 2021 at 5:40:50 PM", ja: "1989/1/23月曜日 7:08:092021/12/7火曜日 17:40:50" },
{ date: "full", time: "short", en: "Monday, January 23, 1989 at 7:08 AM Tuesday, December 7, 2021 at 5:40 PM", ja: "1989/1/23月曜日 7:082021/12/7火曜日 17:40" },
{ date: "long", time: "full", en: "January 23, 1989 at 7:08:09 AM Coordinated Universal Time December 7, 2021 at 5:40:50 PM Coordinated Universal Time", ja: "1989/1/23 7時08分09秒 協定世界時2021/12/7 17時40分50秒 協定世界時" },
{ date: "long", time: "long", en: "January 23, 1989 at 7:08:09 AM UTC December 7, 2021 at 5:40:50 PM UTC", ja: "1989/1/23 7:08:09 UTC2021/12/7 17:40:50 UTC" },
{ date: "long", time: "medium", en: "January 23, 1989 at 7:08:09 AM December 7, 2021 at 5:40:50 PM", ja: "1989/1/23 7:08:092021/12/7 17:40:50" },
{ date: "long", time: "short", en: "January 23, 1989 at 7:08 AM December 7, 2021 at 5:40 PM", ja: "1989/1/23 7:082021/12/7 17:40" },
{ date: "medium", time: "full", en: "Jan 23, 1989, 7:08:09 AM Coordinated Universal Time Dec 7, 2021, 5:40:50 PM Coordinated Universal Time", ja: "1989/01/23 7時08分09秒 協定世界時2021/12/07 17時40分50秒 協定世界時" },
{ date: "medium", time: "long", en: "Jan 23, 1989, 7:08:09 AM UTC Dec 7, 2021, 5:40:50 PM UTC", ja: "1989/01/23 7:08:09 UTC2021/12/07 17:40:50 UTC" },
{ date: "medium", time: "medium", en: "Jan 23, 1989, 7:08:09 AM Dec 7, 2021, 5:40:50 PM", ja: "1989/01/23 7:08:092021/12/07 17:40:50" },
{ date: "medium", time: "short", en: "Jan 23, 1989, 7:08 AM Dec 7, 2021, 5:40 PM", ja: "1989/01/23 7:082021/12/07 17:40" },
{ date: "short", time: "full", en: "1/23/89, 7:08:09 AM Coordinated Universal Time 12/7/21, 5:40:50 PM Coordinated Universal Time", ja: "1989/01/23 7時08分09秒 協定世界時2021/12/07 17時40分50秒 協定世界時" },
{ date: "short", time: "long", en: "1/23/89, 7:08:09 AM UTC 12/7/21, 5:40:50 PM UTC", ja: "1989/01/23 7:08:09 UTC2021/12/07 17:40:50 UTC" },
{ date: "short", time: "medium", en: "1/23/89, 7:08:09 AM 12/7/21, 5:40:50 PM", ja: "1989/01/23 7:08:092021/12/07 17:40:50" },
{ date: "short", time: "short", en: "1/23/89, 7:08 AM 12/7/21, 5:40 PM", ja: "1989/01/23 7:082021/12/07 17:40" },
];
test("all", () => {

View file

@ -86,7 +86,7 @@ describe("equal dates are squashed", () => {
{ type: "minute", value: "08", source: "shared" },
{ type: "literal", value: ":", source: "shared" },
{ type: "second", value: "09", source: "shared" },
{ type: "literal", value: "\u202f", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "dayPeriod", value: "AM", source: "shared" },
]);
@ -127,7 +127,7 @@ describe("equal dates are squashed", () => {
{ type: "minute", value: "08", source: "shared" },
{ type: "literal", value: ":", source: "shared" },
{ type: "second", value: "09", source: "shared" },
{ type: "literal", value: "\u202f", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "dayPeriod", value: "AM", source: "shared" },
]);
@ -175,7 +175,7 @@ describe("equal dates are squashed", () => {
{ type: "minute", value: "08", source: "shared" },
{ type: "literal", value: ":", source: "shared" },
{ type: "second", value: "09", source: "shared" },
{ type: "literal", value: "\u202f", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "dayPeriod", value: "AM", source: "shared" },
]);
@ -213,7 +213,7 @@ describe("dateStyle", () => {
{ type: "day", value: "23", source: "startRange" },
{ type: "literal", value: ", ", source: "startRange" },
{ type: "year", value: "1989", source: "startRange" },
{ type: "literal", value: "", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "weekday", value: "Tuesday", source: "endRange" },
{ type: "literal", value: ", ", source: "endRange" },
{ type: "month", value: "December", source: "endRange" },
@ -252,7 +252,7 @@ describe("dateStyle", () => {
{ type: "day", value: "23", source: "startRange" },
{ type: "literal", value: ", ", source: "startRange" },
{ type: "year", value: "1989", source: "startRange" },
{ type: "literal", value: "\u2009\u2009", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "month", value: "December", source: "endRange" },
{ type: "literal", value: " ", source: "endRange" },
{ type: "day", value: "7", source: "endRange" },
@ -284,7 +284,7 @@ describe("dateStyle", () => {
{ type: "day", value: "23", source: "startRange" },
{ type: "literal", value: ", ", source: "startRange" },
{ type: "year", value: "1989", source: "startRange" },
{ type: "literal", value: "\u2009\u2009", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "month", value: "Dec", source: "endRange" },
{ type: "literal", value: " ", source: "endRange" },
{ type: "day", value: "7", source: "endRange" },
@ -316,7 +316,7 @@ describe("dateStyle", () => {
{ type: "day", value: "23", source: "startRange" },
{ type: "literal", value: "/", source: "startRange" },
{ type: "year", value: "89", source: "startRange" },
{ type: "literal", value: "\u2009\u2009", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "month", value: "12", source: "endRange" },
{ type: "literal", value: "/", source: "endRange" },
{ type: "day", value: "7", source: "endRange" },
@ -350,7 +350,7 @@ describe("dateStyle", () => {
{ type: "day", value: "7", source: "startRange" },
{ type: "literal", value: ", ", source: "startRange" },
{ type: "year", value: "2021", source: "startRange" },
{ type: "literal", value: "\u2009\u2009", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "weekday", value: "Monday", source: "endRange" },
{ type: "literal", value: ", ", source: "endRange" },
{ type: "month", value: "January", source: "endRange" },
@ -399,11 +399,11 @@ describe("timeStyle", () => {
{ type: "minute", value: "08", source: "startRange" },
{ type: "literal", value: ":", source: "startRange" },
{ type: "second", value: "09", source: "startRange" },
{ type: "literal", value: "\u202f", source: "startRange" },
{ type: "literal", value: " ", source: "startRange" },
{ type: "dayPeriod", value: "AM", source: "startRange" },
{ type: "literal", value: " ", source: "startRange" },
{ type: "timeZoneName", value: "Coordinated Universal Time", source: "startRange" },
{ type: "literal", value: "\u2009\u2009", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "month", value: "12", source: "endRange" },
{ type: "literal", value: "/", source: "endRange" },
{ type: "day", value: "7", source: "endRange" },
@ -415,7 +415,7 @@ describe("timeStyle", () => {
{ type: "minute", value: "40", source: "endRange" },
{ type: "literal", value: ":", source: "endRange" },
{ type: "second", value: "50", source: "endRange" },
{ type: "literal", value: "\u202f", source: "endRange" },
{ type: "literal", value: " ", source: "endRange" },
{ type: "dayPeriod", value: "PM", source: "endRange" },
{ type: "literal", value: " ", source: "endRange" },
{ type: "timeZoneName", value: "Coordinated Universal Time", source: "endRange" },
@ -467,11 +467,11 @@ describe("timeStyle", () => {
{ type: "minute", value: "08", source: "startRange" },
{ type: "literal", value: ":", source: "startRange" },
{ type: "second", value: "09", source: "startRange" },
{ type: "literal", value: "\u202f", source: "startRange" },
{ type: "literal", value: " ", source: "startRange" },
{ type: "dayPeriod", value: "AM", source: "startRange" },
{ type: "literal", value: " ", source: "startRange" },
{ type: "timeZoneName", value: "UTC", source: "startRange" },
{ type: "literal", value: "\u2009\u2009", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "month", value: "12", source: "endRange" },
{ type: "literal", value: "/", source: "endRange" },
{ type: "day", value: "7", source: "endRange" },
@ -483,7 +483,7 @@ describe("timeStyle", () => {
{ type: "minute", value: "40", source: "endRange" },
{ type: "literal", value: ":", source: "endRange" },
{ type: "second", value: "50", source: "endRange" },
{ type: "literal", value: "\u202f", source: "endRange" },
{ type: "literal", value: " ", source: "endRange" },
{ type: "dayPeriod", value: "PM", source: "endRange" },
{ type: "literal", value: " ", source: "endRange" },
{ type: "timeZoneName", value: "UTC", source: "endRange" },
@ -535,9 +535,9 @@ describe("timeStyle", () => {
{ type: "minute", value: "08", source: "startRange" },
{ type: "literal", value: ":", source: "startRange" },
{ type: "second", value: "09", source: "startRange" },
{ type: "literal", value: "\u202f", source: "startRange" },
{ type: "literal", value: " ", source: "startRange" },
{ type: "dayPeriod", value: "AM", source: "startRange" },
{ type: "literal", value: "\u2009\u2009", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "month", value: "12", source: "endRange" },
{ type: "literal", value: "/", source: "endRange" },
{ type: "day", value: "7", source: "endRange" },
@ -549,7 +549,7 @@ describe("timeStyle", () => {
{ type: "minute", value: "40", source: "endRange" },
{ type: "literal", value: ":", source: "endRange" },
{ type: "second", value: "50", source: "endRange" },
{ type: "literal", value: "\u202f", source: "endRange" },
{ type: "literal", value: " ", source: "endRange" },
{ type: "dayPeriod", value: "PM", source: "endRange" },
]);
@ -593,9 +593,9 @@ describe("timeStyle", () => {
{ type: "hour", value: "7", source: "startRange" },
{ type: "literal", value: ":", source: "startRange" },
{ type: "minute", value: "08", source: "startRange" },
{ type: "literal", value: "\u202f", source: "startRange" },
{ type: "literal", value: " ", source: "startRange" },
{ type: "dayPeriod", value: "AM", source: "startRange" },
{ type: "literal", value: "\u2009\u2009", source: "shared" },
{ type: "literal", value: " ", source: "shared" },
{ type: "month", value: "12", source: "endRange" },
{ type: "literal", value: "/", source: "endRange" },
{ type: "day", value: "7", source: "endRange" },
@ -605,7 +605,7 @@ describe("timeStyle", () => {
{ type: "hour", value: "5", source: "endRange" },
{ type: "literal", value: ":", source: "endRange" },
{ type: "minute", value: "40", source: "endRange" },
{ type: "literal", value: "\u202f", source: "endRange" },
{ type: "literal", value: " ", source: "endRange" },
{ type: "dayPeriod", value: "PM", source: "endRange" },
]);

View file

@ -127,7 +127,7 @@ describe("timeStyle", () => {
{ type: "minute", value: "08" },
{ type: "literal", value: ":" },
{ type: "second", value: "09" },
{ type: "literal", value: "\u202f" },
{ type: "literal", value: " " },
{ type: "dayPeriod", value: "AM" },
{ type: "literal", value: " " },
{ type: "timeZoneName", value: "Coordinated Universal Time" },
@ -155,7 +155,7 @@ describe("timeStyle", () => {
{ type: "minute", value: "08" },
{ type: "literal", value: ":" },
{ type: "second", value: "09" },
{ type: "literal", value: "\u202f" },
{ type: "literal", value: " " },
{ type: "dayPeriod", value: "AM" },
{ type: "literal", value: " " },
{ type: "timeZoneName", value: "UTC" },
@ -183,7 +183,7 @@ describe("timeStyle", () => {
{ type: "minute", value: "08" },
{ type: "literal", value: ":" },
{ type: "second", value: "09" },
{ type: "literal", value: "\u202f" },
{ type: "literal", value: " " },
{ type: "dayPeriod", value: "AM" },
]);
@ -205,7 +205,7 @@ describe("timeStyle", () => {
{ type: "hour", value: "7" },
{ type: "literal", value: ":" },
{ type: "minute", value: "08" },
{ type: "literal", value: "\u202f" },
{ type: "literal", value: " " },
{ type: "dayPeriod", value: "AM" },
]);

View file

@ -732,6 +732,7 @@ public:
if (icu_failure(status))
return {};
normalize_spaces(formatted_time);
return icu_string_to_string(formatted_time);
}
@ -750,6 +751,8 @@ public:
if (icu_failure(status))
return {};
normalize_spaces(formatted_time);
icu::ConstrainedFieldPosition position;
i32 previous_end_index = 0;
@ -802,6 +805,7 @@ private:
if (icu_failure(status))
return {};
normalize_spaces(formatted_time);
return formatted_time;
}
@ -842,6 +846,23 @@ private:
return formatted;
}
// ICU 72 introduced the use of NBSP to separate time fields and day periods. All major browsers have found that
// this significantly breaks web compatibilty, and they all replace these spaces with normal ASCII spaces. See:
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=1806042
// https://bugs.webkit.org/show_bug.cgi?id=252147
// https://issues.chromium.org/issues/40256057
static void normalize_spaces(icu::UnicodeString& string)
{
static char16_t NARROW_NO_BREAK_SPACE = 0x202f;
static char16_t THIN_SPACE = 0x2009;
for (i32 i = 0; i < string.length(); ++i) {
if (string[i] == NARROW_NO_BREAK_SPACE || string[i] == THIN_SPACE)
string.setCharAt(i, ' ');
}
}
icu::Locale& m_locale;
CalendarPattern m_pattern;