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
This commit is contained in:
parent
eb25ef266a
commit
d3fa6cec80
6 changed files with 69 additions and 28 deletions
|
@ -18,8 +18,7 @@ export const DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps
|
|||
<DialogPrimitive.Content
|
||||
{...props}
|
||||
className="min-w-[360px] fixed left-1/2 top-1/2 p-6 text-left bg-white rounded shadow-xl overflow-hidden -translate-x-1/2 -translate-y-1/2 sm:align-middle sm:w-full sm:max-w-lg"
|
||||
ref={forwardedRef}
|
||||
>
|
||||
ref={forwardedRef}>
|
||||
{children}
|
||||
</DialogPrimitive.Content>
|
||||
)
|
||||
|
|
|
@ -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 => {
|
|||
)}
|
||||
<p className="text-green-500 mb-4">
|
||||
<CalendarIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{dayjs(date)
|
||||
.tz(timeZone())
|
||||
.format(timeFormat + ", dddd DD MMMM YYYY")}
|
||||
{parseZone(date).format(timeFormat + ", dddd DD MMMM YYYY")}
|
||||
</p>
|
||||
<p className="dark:text-white text-gray-600 mb-8">{props.eventType.description}</p>
|
||||
</div>
|
||||
|
|
45
lib/parseZone.ts
Normal file
45
lib/parseZone.ts
Normal file
|
@ -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
|
||||
);
|
||||
}
|
|
@ -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 <Loader />;
|
||||
}
|
||||
|
||||
function toggleShowAPIKey() {
|
||||
setShowAPIKey(!showAPIKey);
|
||||
}
|
||||
|
||||
async function deleteIntegrationHandler(event) {
|
||||
event.preventDefault();
|
||||
|
||||
|
|
|
@ -170,8 +170,7 @@ export default function Home({ integrations }: Props) {
|
|||
<div className="w-2/12 text-right pt-2">
|
||||
<button
|
||||
onClick={() => integrationHandler(integration.type)}
|
||||
className="font-medium text-neutral-900 hover:text-neutral-500"
|
||||
>
|
||||
className="font-medium text-neutral-900 hover:text-neutral-500">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
|
@ -276,8 +275,7 @@ export default function Home({ integrations }: Props) {
|
|||
return (
|
||||
<Dialog
|
||||
open={isAddCalDavIntegrationDialogOpen}
|
||||
onOpenChange={(isOpen) => setIsAddCalDavIntegrationDialogOpen(isOpen)}
|
||||
>
|
||||
onOpenChange={(isOpen) => setIsAddCalDavIntegrationDialogOpen(isOpen)}>
|
||||
<DialogContent>
|
||||
<DialogHeader
|
||||
title="Connect to CalDav Server"
|
||||
|
@ -299,16 +297,14 @@ export default function Home({ integrations }: Props) {
|
|||
<Button
|
||||
type="submit"
|
||||
form={ADD_CALDAV_INTEGRATION_FORM_TITLE}
|
||||
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"
|
||||
>
|
||||
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
|
||||
</Button>
|
||||
<DialogClose
|
||||
onClick={() => {
|
||||
setIsAddCalDavIntegrationDialogOpen(false);
|
||||
}}
|
||||
asChild
|
||||
>
|
||||
asChild>
|
||||
<Button color="secondary">Cancel</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
|
@ -321,8 +317,7 @@ export default function Home({ integrations }: Props) {
|
|||
return (
|
||||
<Dialog
|
||||
open={isAddAppleIntegrationDialogOpen}
|
||||
onOpenChange={(isOpen) => setIsAddAppleIntegrationDialogOpen(isOpen)}
|
||||
>
|
||||
onOpenChange={(isOpen) => setIsAddAppleIntegrationDialogOpen(isOpen)}>
|
||||
<DialogContent>
|
||||
<DialogHeader
|
||||
title="Connect to Apple Server"
|
||||
|
@ -333,8 +328,7 @@ export default function Home({ integrations }: Props) {
|
|||
className="text-indigo-400"
|
||||
href="https://appleid.apple.com/account/manage"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
https://appleid.apple.com/account/manage
|
||||
</a>
|
||||
. Your credentials will be stored and encrypted.
|
||||
|
@ -357,16 +351,14 @@ export default function Home({ integrations }: Props) {
|
|||
<button
|
||||
type="submit"
|
||||
form={ADD_APPLE_INTEGRATION_FORM_TITLE}
|
||||
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"
|
||||
>
|
||||
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
|
||||
</button>
|
||||
<DialogClose
|
||||
onClick={() => {
|
||||
setIsAddAppleIntegrationDialogOpen(false);
|
||||
}}
|
||||
asChild
|
||||
>
|
||||
asChild>
|
||||
<Button color="secondary">Cancel</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
|
|
13
test/lib/parseZone.test.ts
Normal file
13
test/lib/parseZone.test.ts
Normal file
|
@ -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);
|
||||
});
|
Loading…
Reference in a new issue