🐛 Fix fallback behaviour for unrecognized timezones (#1241)

This commit is contained in:
Luke Vella 2024-08-04 13:27:41 +01:00 committed by GitHub
parent 7bc978b87a
commit 2798ba65ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 63 additions and 25 deletions

View file

@ -19,24 +19,59 @@ export function parseIanaTimezone(timezone: string): {
}
export function getBrowserTimeZone() {
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
return resolveGeographicTimeZone(timeZone);
const timeZone = dayjs.tz.guess();
return normalizeTimeZone(timeZone);
}
export function resolveGeographicTimeZone(timezone: string) {
const tz = supportedTimeZones.find((tz) => tz === timezone);
function getTimeZoneOffset(timeZone: string) {
try {
return dayjs().tz(timeZone).utcOffset();
} catch (e) {
console.error(`Failed to resolve timezone ${timeZone}`);
return 0;
}
}
function isFixedOffsetTimeZone(timeZone: string) {
return (
timeZone.toLowerCase().startsWith("etc") ||
timeZone.toLowerCase().startsWith("gmt") ||
timeZone.toLowerCase().startsWith("utc")
);
}
/**
* Given a timezone, this function returns a normalized timezone
* that is supported by the application. If the timezone is not
* recognized, it will return a timezone in the same continent
* with the same offset.
* @param timeZone
* @returns
*/
export function normalizeTimeZone(timeZone: string) {
let tz = supportedTimeZones.find((tz) => tz === timeZone);
if (tz) {
return tz;
}
const timeZoneOffset = getTimeZoneOffset(timeZone);
if (!isFixedOffsetTimeZone(timeZone)) {
// Find a timezone in the same continent with the same offset
const [continent] = timeZone.split("/");
const sameContinentTimeZones = supportedTimeZones.filter((tz) =>
tz.startsWith(continent),
);
tz = sameContinentTimeZones.find((tz) => {
return dayjs().tz(tz, true).utcOffset() === timeZoneOffset;
});
}
if (!tz) {
// find nearest timezone with the same offset
let offset = 0;
try {
offset = dayjs().tz(timezone).utcOffset();
} catch (e) {
console.error(`Failed to resolve timezone ${timezone}`);
}
return supportedTimeZones.find((tz) => {
return dayjs().tz(tz, true).utcOffset() === offset;
})!;
tz = supportedTimeZones.find((tz) => {
return getTimeZoneOffset(tz) === timeZoneOffset;
})!; // We assume there has to be a timezone with the same offset
}
return tz;

View file

@ -5,34 +5,40 @@ import { describe, expect, it } from "vitest";
import { supportedTimeZones } from "@/utils/supported-time-zones";
import { resolveGeographicTimeZone } from "./date-time-utils";
import { normalizeTimeZone } from "./date-time-utils";
dayjs.extend(utc);
dayjs.extend(timezone);
describe("resolveGeographicTimezone", () => {
describe("Normalize Time Zone", () => {
it("should return same timezone when given a geographic timezone", () => {
const browserTimeZone = resolveGeographicTimeZone("Europe/London");
const browserTimeZone = normalizeTimeZone("Europe/London");
// Assert that the browser time zone is one of the supported time zones
expect(browserTimeZone).toBe("Europe/London");
});
it("should return a supported timezone when given a fixed offset timezone", () => {
const browserTimeZone = resolveGeographicTimeZone("Etc/GMT-1");
const browserTimeZone = normalizeTimeZone("Etc/GMT-1");
// Assert that the browser time zone is one of the supported time zones
expect(supportedTimeZones.includes(browserTimeZone)).toBe(true);
});
it("should return a supported timezone when given GMT", () => {
const browserTimeZone = resolveGeographicTimeZone("GMT");
const browserTimeZone = normalizeTimeZone("GMT");
// Assert that the browser time zone is one of the supported time zones
expect(supportedTimeZones.includes(browserTimeZone)).toBe(true);
});
it("should return a supported timezone when given UTC", () => {
const browserTimeZone = resolveGeographicTimeZone("UTC");
const browserTimeZone = normalizeTimeZone("UTC");
// Assert that the browser time zone is one of the supported time zones
expect(supportedTimeZones.includes(browserTimeZone)).toBe(true);
});
it("should return a valid timezone in the same continent when not recognized", () => {
const browserTimeZone = normalizeTimeZone("Asia/Kolkata");
expect(browserTimeZone).toBe("Asia/Calcutta");
});
});

View file

@ -17,10 +17,7 @@ import * as React from "react";
import { useAsync } from "react-use";
import { usePreferences } from "@/contexts/preferences";
import {
getBrowserTimeZone,
resolveGeographicTimeZone,
} from "@/utils/date-time-utils";
import { getBrowserTimeZone, normalizeTimeZone } from "@/utils/date-time-utils";
import { useRequiredContext } from "../components/use-required-context";
@ -213,7 +210,7 @@ export const DayjsProvider: React.FunctionComponent<{
const preferredTimeZone = React.useMemo(
() =>
config?.timeZone
? resolveGeographicTimeZone(config?.timeZone)
? normalizeTimeZone(config?.timeZone)
: getBrowserTimeZone(),
[config?.timeZone],
);