mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-29 06:07:25 +02:00
🐛 Fix fallback behaviour for unrecognized timezones (#1241)
This commit is contained in:
parent
7bc978b87a
commit
2798ba65ce
3 changed files with 63 additions and 25 deletions
|
@ -19,24 +19,59 @@ export function parseIanaTimezone(timezone: string): {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBrowserTimeZone() {
|
export function getBrowserTimeZone() {
|
||||||
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
const timeZone = dayjs.tz.guess();
|
||||||
return resolveGeographicTimeZone(timeZone);
|
return normalizeTimeZone(timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveGeographicTimeZone(timezone: string) {
|
function getTimeZoneOffset(timeZone: string) {
|
||||||
const tz = supportedTimeZones.find((tz) => tz === timezone);
|
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) {
|
if (!tz) {
|
||||||
// find nearest timezone with the same offset
|
tz = supportedTimeZones.find((tz) => {
|
||||||
let offset = 0;
|
return getTimeZoneOffset(tz) === timeZoneOffset;
|
||||||
try {
|
})!; // We assume there has to be a timezone with the same offset
|
||||||
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;
|
|
||||||
})!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tz;
|
return tz;
|
||||||
|
|
|
@ -5,34 +5,40 @@ import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import { supportedTimeZones } from "@/utils/supported-time-zones";
|
import { supportedTimeZones } from "@/utils/supported-time-zones";
|
||||||
|
|
||||||
import { resolveGeographicTimeZone } from "./date-time-utils";
|
import { normalizeTimeZone } from "./date-time-utils";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
|
|
||||||
describe("resolveGeographicTimezone", () => {
|
describe("Normalize Time Zone", () => {
|
||||||
it("should return same timezone when given a geographic timezone", () => {
|
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
|
// Assert that the browser time zone is one of the supported time zones
|
||||||
expect(browserTimeZone).toBe("Europe/London");
|
expect(browserTimeZone).toBe("Europe/London");
|
||||||
});
|
});
|
||||||
it("should return a supported timezone when given a fixed offset timezone", () => {
|
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
|
// Assert that the browser time zone is one of the supported time zones
|
||||||
expect(supportedTimeZones.includes(browserTimeZone)).toBe(true);
|
expect(supportedTimeZones.includes(browserTimeZone)).toBe(true);
|
||||||
});
|
});
|
||||||
it("should return a supported timezone when given GMT", () => {
|
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
|
// Assert that the browser time zone is one of the supported time zones
|
||||||
expect(supportedTimeZones.includes(browserTimeZone)).toBe(true);
|
expect(supportedTimeZones.includes(browserTimeZone)).toBe(true);
|
||||||
});
|
});
|
||||||
it("should return a supported timezone when given UTC", () => {
|
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
|
// Assert that the browser time zone is one of the supported time zones
|
||||||
expect(supportedTimeZones.includes(browserTimeZone)).toBe(true);
|
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");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,10 +17,7 @@ import * as React from "react";
|
||||||
import { useAsync } from "react-use";
|
import { useAsync } from "react-use";
|
||||||
|
|
||||||
import { usePreferences } from "@/contexts/preferences";
|
import { usePreferences } from "@/contexts/preferences";
|
||||||
import {
|
import { getBrowserTimeZone, normalizeTimeZone } from "@/utils/date-time-utils";
|
||||||
getBrowserTimeZone,
|
|
||||||
resolveGeographicTimeZone,
|
|
||||||
} from "@/utils/date-time-utils";
|
|
||||||
|
|
||||||
import { useRequiredContext } from "../components/use-required-context";
|
import { useRequiredContext } from "../components/use-required-context";
|
||||||
|
|
||||||
|
@ -213,7 +210,7 @@ export const DayjsProvider: React.FunctionComponent<{
|
||||||
const preferredTimeZone = React.useMemo(
|
const preferredTimeZone = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
config?.timeZone
|
config?.timeZone
|
||||||
? resolveGeographicTimeZone(config?.timeZone)
|
? normalizeTimeZone(config?.timeZone)
|
||||||
: getBrowserTimeZone(),
|
: getBrowserTimeZone(),
|
||||||
[config?.timeZone],
|
[config?.timeZone],
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue