Skip to content

Commit

Permalink
fix: timezone fixes (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-clegg authored Nov 24, 2024
1 parent bf2c673 commit a7ad66a
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 50 deletions.
36 changes: 29 additions & 7 deletions directus/extensions/directus-extension-events/src/events/get.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {AdminAccountability, isUserLeader} from "./utils";
import {RRule} from "rrule";
import {setHours, setMinutes, setSeconds, startOfDay} from "date-fns";
import { startOfDay } from "date-fns";
import { utcToZonedTime } from 'date-fns-tz';

const timeZone = "Europe/London";

export async function getConsentInfo(req: any, res: any, services: any, database: any) {
const {
Expand Down Expand Up @@ -146,7 +149,6 @@ export async function getConsentInfo(req: any, res: any, services: any, database
}
}


export async function get(req: any, res: any, services: any, database: any) {
const {
ItemsService
Expand Down Expand Up @@ -242,14 +244,34 @@ export async function get(req: any, res: any, services: any, database: any) {
continue;
}

const rule = RRule.fromString(event.rrule);
const occurrences = rule.between(start, end, true);
const eventStartDay = startOfDay(new Date(event.start_date));
// Parse event start and end dates in 'Europe/London' time zone
const eventStartDate = utcToZonedTime(event.start_date, timeZone);
const eventEndDate = utcToZonedTime(event.end_date, timeZone);

// Parse RRULE with time zone information
const ruleOptions = RRule.parseString(event.rrule);
ruleOptions.dtstart = eventStartDate;
ruleOptions.tzid = timeZone;

const rule = new RRule(ruleOptions);

// Ensure 'start' and 'end' are in 'Europe/London' time zone
const startLondon = utcToZonedTime(start, timeZone);
const endLondon = utcToZonedTime(end, timeZone);

// Get occurrences in the specified time range
const occurrences = rule.between(startLondon, endLondon, true);

const eventStartDay = startOfDay(eventStartDate);

for (const occurrence of occurrences) {
// occurrence is in 'Europe/London' time zone
// Adjust end date based on duration
const duration = eventEndDate.getTime() - eventStartDate.getTime();
const combinedEndDate = new Date(occurrence.getTime() + duration);

// Calculate instance number
const instances = rule.between(eventStartDay, occurrence, true);
const endDate = new Date(event.end_date);
const combinedEndDate = setHours(setMinutes(setSeconds(occurrence, endDate.getSeconds()), endDate.getMinutes()), endDate.getHours());

events.push({
...event,
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/components/base/DatePicker.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<template>
<vue-date-picker
v-model="internalValue"
:format="formatDate"
:is24="false"
:placeholder="placeholder"
auto-apply
:close-on-auto-apply="false"
:enable-time-picker="enableTimePicker ?? false"
:min-date="minDate"
:max-date="maxDate"
utc
locale="en-GB">
<template #calendar-icon>
<span class="dp__clock-icon">
Expand Down
14 changes: 9 additions & 5 deletions frontend/src/components/events/UpcomingList.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
import {
addYears, format
} from "date-fns";
import { addYears } from "date-fns";
import {utcToZonedTime, format} from "date-fns-tz";
import { ExclamationTriangleIcon } from "@heroicons/vue/16/solid";
import type { EventItem } from "~/types";
Expand Down Expand Up @@ -72,8 +71,13 @@ function formatDate (event: EventItem) {
if (props.dateFormatter) {
return props.dateFormatter(event);
} else {
let result = format(new Date(event.start_date), "do MMMM yyyy, ");
result += `${formatShortTime(new Date(event.start_date))} - ${formatShortTime(new Date(event.end_date))}`;
const timeZone = "Europe/London";
const dateStart = new Date(event.start_date);
const dateEnd = new Date(event.end_date);
const localStartTime = utcToZonedTime(dateStart, timeZone);
const localEndTime = utcToZonedTime(dateEnd, timeZone);
let result = format(localStartTime, "do MMMM yyyy, ", { timeZone });
result += `${formatShortTime(localStartTime)} - ${formatShortTime(localEndTime)}`;
return result;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ watch(() => event.value.paddleType, (val) => {
class="space-y-2">
<input-radio-group
v-model="selectedPaddleType"
label="Paddle type"
:disabled="!eventType || eventType.id === 'meetings'"
:required="eventType && eventType.id !== 'meetings'"
by="id"
Expand Down
32 changes: 10 additions & 22 deletions frontend/src/components/events/wizard/RecurringRuleStep.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,6 @@ function useExample (example: string) {
const previewDates = ref<string[]>([]);
function toDatetime (date: Date | string) {
const newDate = new Date(date);
return datetime(newDate.getUTCFullYear(),
newDate.getUTCMonth() + 1,
newDate.getUTCDate(),
newDate.getUTCHours(),
newDate.getUTCMinutes(),
newDate.getUTCSeconds());
}
watch(() => event.value.rrule, (val) => {
console.log("CHANGE", val);
if (!val) {
Expand Down Expand Up @@ -118,24 +108,22 @@ const exampleEvents = computed(() => [{

<div>
<ul class="flex flex-wrap gap-3">
<ul class="flex flex-wrap gap-3">
<li
v-for="(example, index) in examples"
:key="index">
<button
class="rounded bg-indigo-50 px-2 py-1 text-sm font-semibold text-indigo-600 shadow-sm hover:bg-indigo-100 border border-indigo-300"
@click="() => useExample(example)">
{{ example }}
</button>
</li>
</ul>
<li
v-for="(example, index) in examples"
:key="index">
<button
class="rounded bg-indigo-50 px-2 py-1 text-sm font-semibold text-indigo-600 shadow-sm hover:bg-indigo-100 border border-indigo-300"
@click="() => useExample(example)">
{{ example }}
</button>
</li>
</ul>
</div>

<hr>

<div class="space-y-5">
<span>Preview the dates here; days with a circle indicate when this event will occur.</span>
<span>Preview the dates here; circled dates indicate when this event will occur.</span>
<small-calendar
:events="exampleEvents" />
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/events/wizard/SingleDateStep.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ watch(() => event.value.startDate, (val) => {
}
if (!event.value.endDate || event.value.endDate < val) {
event.value.endDate = formatISO(addHours(new Date(val), 1));
event.value.endDate = addHours(new Date(val), 1);
}
});
Expand Down
24 changes: 14 additions & 10 deletions frontend/src/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@
<script setup lang="ts">
import { HandRaisedIcon } from "@heroicons/vue/24/outline";
import { ChevronRightIcon } from "@heroicons/vue/24/solid";
import { format, addDays, startOfDay, endOfDay, setHours, setMinutes, setSeconds } from "date-fns";
import { addDays, endOfDay } from "date-fns";
import type { EventItem, Home, NewsItem } from "~/types";
import {utcToZonedTime, format} from "date-fns-tz";
definePageMeta({
layout: "no-container"
Expand All @@ -129,18 +130,18 @@ const { data: home } = await useAsyncData("home", async () => {
});
});
const eventsStart = new Date();
const eventsEnd = endOfDay(addDays(eventsStart, 7));
function eventDateFormatter (event: EventItem) {
const startDate = new Date(event.start_date);
const endDate = new Date(event.end_date);
const timeZone = "Europe/London";
const localStartDate = utcToZonedTime(new Date(event.start_date), timeZone);
const localEndDate = utcToZonedTime(new Date(event.end_date), timeZone);
console.log("local", event.start_date, localStartDate)
const startDay = format(startDate, "iiii do");
const startTime = formatShortTime(startDate);
const startDay = format(localStartDate, "iiii do", {timeZone});
const startTime = formatShortTime(localStartDate);
const endDay = format(endDate, "iiii do");
const endTime = formatShortTime(endDate);
const endDay = format(localEndDate, "iiii do", {timeZone});
const endTime = formatShortTime(localEndDate);
if (startDay !== endDay) {
return `${startDay} @ ${startTime} to ${endDay} @ ${endTime}`;
Expand All @@ -149,6 +150,9 @@ function eventDateFormatter (event: EventItem) {
return `${startDay} @ ${startTime} - ${endTime}`;
}
const eventsStart = new Date();
const eventsEnd = endOfDay(addDays(eventsStart, 7));
const dateRangeLabel = computed(() => {
const startMonth = format(eventsStart, "MMM");
const endMonth = format(eventsEnd, "MMM");
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/utils/strings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { format } from "date-fns";
import { format } from "date-fns-tz";

export function capitalize (input: string) {
return input[0].toUpperCase() + input.slice(1);
Expand All @@ -11,11 +11,12 @@ export function clickOrTap (shouldCapitalize?: boolean) {
}

export function formatShortTime (input: Date | string) {
const timeZone = "Europe/London";
const date = new Date(input);
const minutes = format(date, "mm");
if (minutes === "00") {
return format(date, "haa").toLowerCase();
return format(date, "haa", {timeZone}).toLowerCase();
} else {
return format(date, "h:mmaa").toLowerCase();
return format(date, "h:mmaa", {timeZone}).toLowerCase();
}
}

0 comments on commit a7ad66a

Please sign in to comment.