Skip to content

Commit

Permalink
fix: Incorrect Times/Timezones when fetching events (#1060)
Browse files Browse the repository at this point in the history
* fix: Incorrect Times/Timezones when fetching events from the Community calendar

* fix: Incorrect Times/Timezones when fetching events from the Community calendar

* fix :the build and lint workflows error

* fix :the build and lint workflows error

* Display the correct time and convert it to a 24-hour format.

* fix :the build and lint workflows error

* fix :the build and lint workflows error

* Refactor community page logic to eliminate code duplication and fix time display (24-hour format)

* Refactor calendar parsing logic to eliminate duplication and improve reusability

* fix :the build and lint workflows error

* fix :the build and lint workflows error

* fix :the build and lint workflows error

* fix :the build and lint workflows error

* fix :the build and lint workflows error

* Refactor: Separate calendar parsing logic into utility module

* fix :the build and lint workflows error

* fix :the build and lint workflows error

* fix :the build and lint workflows error
  • Loading branch information
techmannih authored Dec 13, 2024
1 parent aa7650d commit 9b8228f
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 211 deletions.
106 changes: 106 additions & 0 deletions lib/calendarUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import moment from 'moment-timezone';

export async function fetchRemoteICalFile(url: string): Promise<string | null> {
try {
const response = await fetch(url, { method: 'GET', mode: 'no-cors' });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
} catch (error) {
console.error('Error fetching iCal file:', error);
return null;
}
}

export function printEventsForNextWeeks(icalData: { [x: string]: any }) {
const arrayDates = [];
if (!icalData) {
console.error('iCal data is empty or invalid.');
return;
}

// Calculate the range of dates for the next 12 weeks from today
const today = moment().startOf('day');
const nextTwelveWeeksEnd = moment().add(12, 'weeks').endOf('day');

// Loop through the events in the iCal data
for (const k in icalData) {
const event = icalData[k];

if (event.type === 'VEVENT') {
const title = event.summary;

const timezoneL = moment.tz.guess(); // Default to UTC if timezone information is not provided

const startDate = moment.tz(event.start, timezoneL);

// Complicated case - if an RRULE exists, handle multiple recurrences of the event.
if (event.rrule !== undefined) {
const dates = event.rrule.between(
today.toDate(),
nextTwelveWeeksEnd.toDate(),
true,
);

// Loop through the set of date entries to see which recurrences should be printed.
for (const date of dates) {
const startDate = moment.tz(date, timezoneL);
const eventtimezone = event.start.tz;
const owntimezone = moment.tz.guess();
const eventOffset = moment.tz(eventtimezone).utcOffset();
const localOffset = moment.tz(owntimezone).utcOffset();
const offsetDifference = localOffset - eventOffset;

// Check if the event falls within the next 4 weeks from today
if (startDate.isBetween(today, nextTwelveWeeksEnd, undefined, '[]')) {
const dateTimezone = moment.tz.zone(event.start.tz);
let offset;
if (dateTimezone && offsetDifference)
offset = offsetDifference - dateTimezone.utcOffset(date);

const newDate = moment(date).subtract(offset, 'minutes').toDate();

const start = moment(newDate);
const utcDate = start.utc();

const time = utcDate.format('MMMM Do YYYY, HH:mm');
const day = utcDate.format('D');
const parsedStartDate = utcDate.format('YYYY-MM-DD HH:mm:ss');
arrayDates.push({
title,
time,
day,
timezone: 'UTC',
parsedStartDate,
});
}
}
} else {
// Simple case - no recurrences, just print out the calendar event.
if (startDate.isBetween(today, nextTwelveWeeksEnd, undefined, '[]')) {
const utcDate = startDate.utc();

const time = utcDate.format('MMMM Do YYYY, HH:mm');
const day = utcDate.format('D');
const parsedStartDate = utcDate.format('YYYY-MM-DD HH:mm:ss');
arrayDates.push({
title,
time,
day,
timezone: 'UTC',
parsedStartDate,
});
}
}
}
}

arrayDates.sort(
(x, y) =>
new Date(x.parsedStartDate).getTime() -
new Date(y.parsedStartDate).getTime(),
);

return arrayDates;
}
100 changes: 4 additions & 96 deletions pages/community/index.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import { GetStaticProps } from 'next';
import Card from '~/components/Card';
import Image from 'next/image';
import ical from 'node-ical';
import moment from 'moment-timezone';
import {
fetchRemoteICalFile,
printEventsForNextWeeks,
} from '../../lib/calendarUtils';

export const getStaticProps: GetStaticProps = async () => {
const PATH = 'pages/blog/posts';
Expand All @@ -35,19 +38,6 @@ export const getStaticProps: GetStaticProps = async () => {
})
.slice(0, 5);

async function fetchRemoteICalFile(url: string) {
try {
const response = await fetch(url, { method: 'GET', mode: 'no-cors' });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.text();
return data;
} catch (error) {
console.error('Error fetching iCal file:', error);
return null;
}
}
const remoteICalUrl =
'https://calendar.google.com/calendar/ical/json.schema.community%40gmail.com/public/basic.ics';
const datesInfo = await fetchRemoteICalFile(remoteICalUrl)
Expand All @@ -61,88 +51,6 @@ export const getStaticProps: GetStaticProps = async () => {
},
};
};
function printEventsForNextWeeks(icalData: { [x: string]: any }) {
const arrayDates = [];
if (!icalData) {
console.error('iCal data is empty or invalid.');
return;
}

const today = moment().startOf('day');
const nextFourWeeksEnd = moment().add(12, 'weeks').endOf('day');

for (const event of Object.values(icalData)) {
if (event.type === 'VEVENT') {
const title = event.summary;

const timezoneL = moment.tz.guess();
const startDate = moment.tz(event.start, timezoneL);

if (event.rrule !== undefined) {
const dates = event.rrule.between(
today.toDate(),
nextFourWeeksEnd.toDate(),
true,
);

for (const date of dates) {
const startDate = moment.tz(date, timezoneL);

if (startDate.isBetween(today, nextFourWeeksEnd, undefined, '[]')) {
const dateTimezone = moment.tz.zone(event.start.tz);
const localTimezone = moment.tz.guess();
const tz =
event.rrule.origOptions.tzid === localTimezone
? event.rrule.origOptions.tzid
: localTimezone;
const timezone = moment.tz.zone(tz);
let offset;
if (timezone && dateTimezone)
offset = timezone.utcOffset(date) - dateTimezone.utcOffset(date);
const newDate = moment(date).add(offset, 'minutes').toDate();

const start = moment(newDate);
const utcDate = start.utc();

const time = utcDate.format('MMMM Do YYYY, h:mm a');
const day = utcDate.format('D');
const parsedStartDate = utcDate.format('YYYY-MM-DD HH:mm:ss');
arrayDates.push({
title,
time,
day,
timezone: 'UTC',
parsedStartDate,
});
}
}
} else {
if (startDate.isBetween(today, nextFourWeeksEnd, undefined, '[]')) {
const utcDate = startDate.utc();

const time = utcDate.format('MMMM Do YYYY, h:mm a');
const day = utcDate.format('D');
const parsedStartDate = startDate.format('YYYY-MM-DD HH:mm:ss');
arrayDates.push({
title,
time,
day,
timezone: 'UTC',
parsedStartDate,
});
}
}
}
}

arrayDates.sort(
(x, y) =>
new Date(x.parsedStartDate).getTime() -
new Date(y.parsedStartDate).getTime(),
);

return arrayDates;
}

export default function communityPages(props: any) {
const blogPosts = props.blogPosts;
Expand Down
125 changes: 10 additions & 115 deletions pages/index.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ const PATH = 'pages/blog/posts';
import readingTime from 'reading-time';
import Link from 'next/link';
import TextTruncate from 'react-text-truncate';

import {
fetchRemoteICalFile,
printEventsForNextWeeks,
} from '../lib/calendarUtils';
import { Headline4 } from '~/components/Headlines';
import { GetStaticProps } from 'next';

/* eslint-disable */
import ical from 'node-ical';
import moment from 'moment-timezone';
import { useTheme } from 'next-themes';

// apiKey and appId are set in the .env.local file
Expand All @@ -33,32 +35,17 @@ export const getStaticProps: GetStaticProps = async () => {
);
const { data: frontmatter, content } = matter(fullFileName);
return {
slug: slug,
slug,
frontmatter,
content,
};
})
.sort((a, b) => {
const dateA = new Date(a.frontmatter.date).getTime();
const dateB = new Date(b.frontmatter.date).getTime();
return dateA < dateB ? 1 : -1;
})
.sort(
(a, b) =>
new Date(b.frontmatter.date).getTime() -
new Date(a.frontmatter.date).getTime(),
)
.slice(0, 5);

// Function to fetch the remote iCal file
async function fetchRemoteICalFile(url: string) {
try {
const response = await fetch(url, { method: 'GET', mode: 'no-cors' });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.text();
return data;
} catch (error) {
console.error('Error fetching iCal file:', error);
return null;
}
}
// Example usage:
const remoteICalUrl =
'https://calendar.google.com/calendar/ical/json.schema.community%40gmail.com/public/basic.ics'; // Replace with the actual URL
Expand All @@ -74,99 +61,7 @@ export const getStaticProps: GetStaticProps = async () => {
},
};
};
// Function to filter and print events for the next weeks from today
function printEventsForNextWeeks(icalData: { [x: string]: any }) {
const arrayDates = [];
if (!icalData) {
console.error('iCal data is empty or invalid.');
return;
}

// Calculate the range of dates for the next 12 weeks from today
const today = moment().startOf('day');
const nextFourWeeksEnd = moment().add(12, 'weeks').endOf('day');

// Loop through the events in the iCal data
for (const k in icalData) {
const event = icalData[k];

if (event.type === 'VEVENT') {
const title = event.summary;

const timezoneL = moment.tz.guess(); // Default to UTC if timezone information is not provided
const startDate = moment.tz(event.start, timezoneL);

// Complicated case - if an RRULE exists, handle multiple recurrences of the event.
if (event.rrule !== undefined) {
// For recurring events, get the set of event start dates that fall within the range
// of dates we're looking for.
const dates = event.rrule.between(
today.toDate(),
nextFourWeeksEnd.toDate(),
true,
);

// Loop through the set of date entries to see which recurrences should be printed.
for (const date of dates) {
const startDate = moment.tz(date, timezoneL);

// Check if the event falls within the next 4 weeks from today
if (startDate.isBetween(today, nextFourWeeksEnd, undefined, '[]')) {
const dateTimezone = moment.tz.zone(event.start.tz);
const localTimezone = moment.tz.guess();
const tz =
event.rrule.origOptions.tzid === localTimezone
? event.rrule.origOptions.tzid
: localTimezone;
const timezone = moment.tz.zone(tz);
let offset;
if (timezone && dateTimezone)
offset = timezone.utcOffset(date) - dateTimezone.utcOffset(date);
const newDate = moment(date).add(offset, 'minutes').toDate();

const start = moment(newDate);
const utcDate = start.utc();

const time = utcDate.format('MMMM Do YYYY, h:mm a');
const day = utcDate.format('D');
const parsedStartDate = utcDate.format('YYYY-MM-DD HH:mm:ss');
arrayDates.push({
title,
time,
day,
timezone: 'UTC',
parsedStartDate,
});
}
}
} else {
// Simple case - no recurrences, just print out the calendar event.
if (startDate.isBetween(today, nextFourWeeksEnd, undefined, '[]')) {
const utcDate = startDate.utc();

const time = utcDate.format('MMMM Do YYYY, h:mm a');
const day = utcDate.format('D');
const parsedStartDate = startDate.format('YYYY-MM-DD HH:mm:ss');
arrayDates.push({
title,
time,
day,
timezone: 'UTC',
parsedStartDate,
});
}
}
}
}

arrayDates.sort(
(x, y) =>
new Date(x.parsedStartDate).getTime() -
new Date(y.parsedStartDate).getTime(),
);

return arrayDates;
}
export function AlgoliaSearch() {
useEffect(() => {
const customButton = document.querySelector('.herobtn');
Expand Down

0 comments on commit 9b8228f

Please sign in to comment.