From d3fa6cec8071c517a6c66110706d5f0a6b16d900 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Sep 2021 03:58:29 +0100 Subject: [PATCH] Fixes timeZone() by no longer needing timeZone() (#646) * Fixes timeZone() by no longer needing timeZone() * Added simple testcase to demonstrate the behaviour of parseZone() vs dayjs() * Fixed eslint errors --- components/Dialog.tsx | 3 +- components/booking/pages/BookingPage.tsx | 5 ++- lib/parseZone.ts | 45 ++++++++++++++++++++++++ pages/integrations/[integration].tsx | 7 ---- pages/integrations/index.tsx | 24 +++++-------- test/lib/parseZone.test.ts | 13 +++++++ 6 files changed, 69 insertions(+), 28 deletions(-) create mode 100644 lib/parseZone.ts create mode 100644 test/lib/parseZone.test.ts diff --git a/components/Dialog.tsx b/components/Dialog.tsx index 3798733139..9776f87669 100644 --- a/components/Dialog.tsx +++ b/components/Dialog.tsx @@ -18,8 +18,7 @@ export const DialogContent = React.forwardRef + ref={forwardedRef}> {children} ) diff --git a/components/booking/pages/BookingPage.tsx b/components/booking/pages/BookingPage.tsx index b704b3b91a..dceb54c206 100644 --- a/components/booking/pages/BookingPage.tsx +++ b/components/booking/pages/BookingPage.tsx @@ -14,6 +14,7 @@ import { asStringOrNull } from "@lib/asStringOrNull"; import { timeZone } from "@lib/clock"; import useTheme from "@lib/hooks/useTheme"; import AvatarGroup from "@components/ui/AvatarGroup"; +import { parseZone } from "@lib/parseZone"; const BookingPage = (props: any): JSX.Element => { const router = useRouter(); @@ -183,9 +184,7 @@ const BookingPage = (props: any): JSX.Element => { )}

- {dayjs(date) - .tz(timeZone()) - .format(timeFormat + ", dddd DD MMMM YYYY")} + {parseZone(date).format(timeFormat + ", dddd DD MMMM YYYY")}

{props.eventType.description}

diff --git a/lib/parseZone.ts b/lib/parseZone.ts new file mode 100644 index 0000000000..999c82f21b --- /dev/null +++ b/lib/parseZone.ts @@ -0,0 +1,45 @@ +import dayjs from "dayjs"; + +const ISO8601_OFFSET_FORMAT = /^(.*)([+-])(\d{2}):(\d{2})|(Z)$/; + +// @see https://github.com/iamkun/dayjs/issues/651#issuecomment-763033265 +// decorates dayjs in order to keep the utcOffset of the given date string +// ; natively dayjs auto-converts to local time & losing utcOffset info. +export function parseZone( + date?: dayjs.ConfigType, + format?: dayjs.OptionType, + locale?: string, + strict?: boolean +) { + if (typeof date !== "string") { + return dayjs(date, format, locale, strict); + } + const match = date.match(ISO8601_OFFSET_FORMAT); + if (match === null) { + return; + } + if (match[0] === "Z") { + return dayjs( + date, + { + utc: true, + ...format, + }, + locale, + strict + ); + } + const [, dateTime, sign, tzHour, tzMinute] = match; + const uOffset: number = tzHour * 60 + parseInt(tzMinute, 10); + const offset = sign === "+" ? uOffset : -uOffset; + + return dayjs( + dateTime, + { + $offset: offset, + ...format, + } as dayjs.OptionType, + locale, + strict + ); +} diff --git a/pages/integrations/[integration].tsx b/pages/integrations/[integration].tsx index 4635785478..f61e075842 100644 --- a/pages/integrations/[integration].tsx +++ b/pages/integrations/[integration].tsx @@ -1,7 +1,6 @@ import prisma from "@lib/prisma"; import { getIntegrationName, getIntegrationType } from "@lib/integrations"; import Shell from "@components/Shell"; -import { useState } from "react"; import { useRouter } from "next/router"; import { useSession } from "next-auth/client"; import Loader from "@components/Loader"; @@ -12,16 +11,10 @@ export default function Integration(props) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [session, loading] = useSession(); - const [showAPIKey, setShowAPIKey] = useState(false); - if (loading) { return ; } - function toggleShowAPIKey() { - setShowAPIKey(!showAPIKey); - } - async function deleteIntegrationHandler(event) { event.preventDefault(); diff --git a/pages/integrations/index.tsx b/pages/integrations/index.tsx index 75657eede5..250ab4f197 100644 --- a/pages/integrations/index.tsx +++ b/pages/integrations/index.tsx @@ -170,8 +170,7 @@ export default function Home({ integrations }: Props) {
@@ -276,8 +275,7 @@ export default function Home({ integrations }: Props) { return ( setIsAddCalDavIntegrationDialogOpen(isOpen)} - > + onOpenChange={(isOpen) => setIsAddCalDavIntegrationDialogOpen(isOpen)}> + className="flex justify-center py-2 px-4 border border-transparent rounded-sm shadow-sm text-sm font-medium text-white bg-neutral-900 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900"> Save { setIsAddCalDavIntegrationDialogOpen(false); }} - asChild - > + asChild> @@ -321,8 +317,7 @@ export default function Home({ integrations }: Props) { return ( setIsAddAppleIntegrationDialogOpen(isOpen)} - > + onOpenChange={(isOpen) => setIsAddAppleIntegrationDialogOpen(isOpen)}> + rel="noopener noreferrer"> https://appleid.apple.com/account/manage . Your credentials will be stored and encrypted. @@ -357,16 +351,14 @@ export default function Home({ integrations }: Props) { { setIsAddAppleIntegrationDialogOpen(false); }} - asChild - > + asChild> diff --git a/test/lib/parseZone.test.ts b/test/lib/parseZone.test.ts new file mode 100644 index 0000000000..af77ce9f63 --- /dev/null +++ b/test/lib/parseZone.test.ts @@ -0,0 +1,13 @@ +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { parseZone } from "@lib/parseZone"; + +dayjs.extend(utc); + +const EXPECTED_DATE_STRING = "2021-06-20T11:59:59+02:00"; + +it("has the right utcOffset regardless of the local timeZone", async () => { + expect(parseZone(EXPECTED_DATE_STRING).utcOffset()).toEqual(120); + expect(parseZone(EXPECTED_DATE_STRING).format()).toEqual(EXPECTED_DATE_STRING); + expect(dayjs(EXPECTED_DATE_STRING).format()).not.toEqual(EXPECTED_DATE_STRING); +});