mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-10-15 19:45:42 -04:00
fix(i18n): format Upcoming month headers in UTC so native shows the right month
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
3 files changed
+34
-4
No files matched your search
@@ -4,8 +4,11 @@
|
||||
* Load order matters — each polyfill depends on the ones before it.
|
||||
* See: https://formatjs.github.io/docs/guides/react-native-hermes
|
||||
*
|
||||
* We use `polyfill` (not `polyfill-force`) so Hermes's native Intl
|
||||
* implementations are preserved when available, and only gaps are filled.
|
||||
* We use `polyfill` (not `polyfill-force`). Note: Hermes has no
|
||||
* Intl.DateTimeFormat#formatRange, so the DateTimeFormat polyfill always
|
||||
* installs on device. No time-zone data is loaded, so its default time zone
|
||||
* is UTC — always pass `timeZone` explicitly when formatting dates (format
|
||||
* date-only values as UTC instants with timeZone: "UTC").
|
||||
*/
|
||||
|
||||
// 1. getCanonicalLocales (no dependencies)
|
||||
|
||||
@@ -27,8 +27,10 @@ function getEndOfWeek(today: string): string {
|
||||
}
|
||||
|
||||
function getMonthLabel(dateStr: string): string {
|
||||
const d = new Date(`${dateStr}T00:00:00`);
|
||||
return new Intl.DateTimeFormat(i18n.locale, { month: "long" }).format(d);
|
||||
// Format a UTC instant in UTC: correct regardless of the formatter's default time zone
|
||||
// (the native Intl polyfill defaults to UTC; browsers use the device zone).
|
||||
const d = new Date(`${dateStr}T00:00:00Z`);
|
||||
return new Intl.DateTimeFormat(i18n.locale, { month: "long", timeZone: "UTC" }).format(d);
|
||||
}
|
||||
|
||||
type BucketKey = "today" | "tomorrow" | "this_week" | "next_week" | string;
|
||||
|
||||
@@ -75,6 +75,31 @@ describe("groupByDateBucket", () => {
|
||||
expect(result[0].key).toBe("next_week");
|
||||
});
|
||||
|
||||
test("month bucket label names the bucket's month", () => {
|
||||
expect(groupByDateBucket([{ date: "2024-08-15" }])[0].label).toBe("August");
|
||||
expect(groupByDateBucket([{ date: "2024-12-03" }])[0].label).toBe("December");
|
||||
});
|
||||
|
||||
test("month label doesn't depend on the process time zone", () => {
|
||||
const originalTz = process.env.TZ;
|
||||
try {
|
||||
const labels: Record<string, string> = {};
|
||||
for (const zone of ["Europe/Berlin", "Pacific/Kiritimati", "America/Los_Angeles", "UTC"]) {
|
||||
process.env.TZ = zone;
|
||||
labels[zone] = groupByDateBucket([{ date: "2024-12-03" }])[0].label;
|
||||
}
|
||||
expect(labels).toEqual({
|
||||
"Europe/Berlin": "December",
|
||||
"Pacific/Kiritimati": "December",
|
||||
"America/Los_Angeles": "December",
|
||||
UTC: "December",
|
||||
});
|
||||
} finally {
|
||||
if (originalTz === undefined) delete process.env.TZ;
|
||||
else process.env.TZ = originalTz;
|
||||
}
|
||||
});
|
||||
|
||||
test("item 30+ days out goes to month bucket", () => {
|
||||
const result = groupByDateBucket([{ date: "2024-08-15" }]);
|
||||
expect(result).toHaveLength(1);
|
||||
|
||||
Reference in new issue
Block a user