Skip to content

Commit

Permalink
fix(datetime): handle am / pm variants (#5406)
Browse files Browse the repository at this point in the history
* initial commit

* update

* update

* Merge branch 'main' into datetime-fix-am/pm-variants

* clean diff

* add pm tests

* remove dead code

* update

---------

Co-authored-by: Asher Gomez <[email protected]>
  • Loading branch information
timreichen and iuioiua authored Aug 9, 2024
1 parent e1dfe0c commit 7edeba4
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 5 deletions.
35 changes: 33 additions & 2 deletions datetime/_date_time_formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,21 @@ export class DateTimeFormatter {
break;
}
case "dayPeriod": {
value = /^(A|P)M/.exec(string)?.[0] as string;
value = /^[AP](?:\.M\.|M\.?)/i.exec(string)?.[0] as string;
switch (value.toUpperCase()) {
case "AM":
case "AM.":
case "A.M.":
value = "AM";
break;
case "PM":
case "PM.":
case "P.M.":
value = "PM";
break;
default:
throw new Error(`dayPeriod '${value}' is not supported.`);
}
break;
}
case "literal": {
Expand Down Expand Up @@ -542,7 +556,24 @@ export class DateTimeFormatter {
const dayPeriod = parts.find(
(part: DateTimeFormatPart) => part.type === "dayPeriod",
);
if (dayPeriod?.value === "PM") value += 12;
if (dayPeriod) {
switch (dayPeriod.value.toUpperCase()) {
case "AM":
case "AM.":
case "A.M.":
// ignore
break;
case "PM":
case "PM.":
case "P.M.":
value += 12;
break;
default:
throw new Error(
`dayPeriod '${dayPeriod.value}' is not supported.`,
);
}
}
utc ? date.setUTCHours(value) : date.setHours(value);
break;
}
Expand Down
176 changes: 173 additions & 3 deletions datetime/_date_time_formatter_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ Deno.test("dateTimeFormatter.format()", () => {
new Date(2020, 0, 1, 23, 59, 59),
"2020-01-01 23:59:59 PM",
],
[
"yyyy-MM-dd hh:mm:ss a",
new Date(2020, 0, 1, 23, 59, 59),
"2020-01-01 11:59:59 PM",
],
["yyyy-MM-dd a", new Date(2020, 0, 1), "2020-01-01 AM"],
["yyyy-MM-dd HH:mm:ss a", new Date(2020, 0, 1), "2020-01-01 00:00:00 AM"],
["yyyy-MM-dd hh:mm:ss a", new Date(2020, 0, 1), "2020-01-01 12:00:00 AM"],
["yyyy", new Date(2020, 0, 1), "2020"],
["MM", new Date(2020, 0, 1), "01"],
] as const;
Expand Down Expand Up @@ -74,7 +81,7 @@ Deno.test("dateTimeFormatter.partsToDate()", () => {
const format = "yyyy-MM-dd HH:mm:ss.SSS a";
const formatter = new DateTimeFormatter(format);
assertEquals(
+formatter.partsToDate([
formatter.partsToDate([
{ type: "year", value: "2020" },
{ type: "month", value: "01" },
{ type: "day", value: "01" },
Expand All @@ -85,7 +92,170 @@ Deno.test("dateTimeFormatter.partsToDate()", () => {
{ type: "dayPeriod", value: "AM" },
{ type: "timeZoneName", value: "UTC" },
]),
+date,
date,
);
});
Deno.test("dateTimeFormatter.partsToDate() works with am dayPeriod", () => {
const date = new Date("2020-01-01T00:00:00.000Z");
using _time = new FakeTime(date);
const format = "HH a";
const formatter = new DateTimeFormatter(format);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "AM" },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "AM." },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "A.M." },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "am" },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "am." },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "a.m." },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
});
Deno.test("dateTimeFormatter.partsToDate() works with pm dayPeriod", () => {
const date = new Date("2020-01-01T13:00:00.000Z");
using _time = new FakeTime(date);
const format = "HH a";
const formatter = new DateTimeFormatter(format);

assertEquals(
formatter.partsToDate([
{ type: "hour", value: "01" },
{ type: "dayPeriod", value: "PM" },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "01" },
{ type: "dayPeriod", value: "PM." },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "01" },
{ type: "dayPeriod", value: "P.M." },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "01" },
{ type: "dayPeriod", value: "pm" },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "01" },
{ type: "dayPeriod", value: "pm." },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
assertEquals(
formatter.partsToDate([
{ type: "hour", value: "01" },
{ type: "dayPeriod", value: "p.m." },
{ type: "timeZoneName", value: "UTC" },
]),
date,
);
});
Deno.test("dateTimeFormatter.partsToDate() throws with invalid dayPeriods", () => {
const format = "HH a";
const formatter = new DateTimeFormatter(format);
assertThrows(
() =>
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "A.M" },
{ type: "timeZoneName", value: "UTC" },
]),
Error,
"dayPeriod 'A.M' is not supported.",
);
assertThrows(
() =>
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "a.m" },
{ type: "timeZoneName", value: "UTC" },
]),
Error,
"dayPeriod 'a.m' is not supported.",
);
assertThrows(
() =>
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "P.M" },
{ type: "timeZoneName", value: "UTC" },
]),
Error,
"dayPeriod 'P.M' is not supported.",
);
assertThrows(
() =>
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "p.m" },
{ type: "timeZoneName", value: "UTC" },
]),
Error,
"dayPeriod 'p.m' is not supported.",
);
assertThrows(
() =>
formatter.partsToDate([
{ type: "hour", value: "00" },
{ type: "dayPeriod", value: "noon" },
{ type: "timeZoneName", value: "UTC" },
]),
Error,
"dayPeriod 'noon' is not supported.",
);
});

Expand Down Expand Up @@ -118,6 +288,6 @@ Deno.test("dateTimeFormatter.partsToDate() sets utc", () => {
] as const;
for (const [format, input, output] of cases) {
const formatter = new DateTimeFormatter(format);
assertEquals(+formatter.partsToDate([...input]), +output);
assertEquals(formatter.partsToDate([...input]), output);
}
});

0 comments on commit 7edeba4

Please sign in to comment.