Skip to content

Commit 9b8228f

Browse files
authored
fix: Incorrect Times/Timezones when fetching events (#1060)
* 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
1 parent aa7650d commit 9b8228f

File tree

3 files changed

+120
-211
lines changed

3 files changed

+120
-211
lines changed

lib/calendarUtils.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import moment from 'moment-timezone';
2+
3+
export async function fetchRemoteICalFile(url: string): Promise<string | null> {
4+
try {
5+
const response = await fetch(url, { method: 'GET', mode: 'no-cors' });
6+
if (!response.ok) {
7+
throw new Error(`HTTP error! status: ${response.status}`);
8+
}
9+
return await response.text();
10+
} catch (error) {
11+
console.error('Error fetching iCal file:', error);
12+
return null;
13+
}
14+
}
15+
16+
export function printEventsForNextWeeks(icalData: { [x: string]: any }) {
17+
const arrayDates = [];
18+
if (!icalData) {
19+
console.error('iCal data is empty or invalid.');
20+
return;
21+
}
22+
23+
// Calculate the range of dates for the next 12 weeks from today
24+
const today = moment().startOf('day');
25+
const nextTwelveWeeksEnd = moment().add(12, 'weeks').endOf('day');
26+
27+
// Loop through the events in the iCal data
28+
for (const k in icalData) {
29+
const event = icalData[k];
30+
31+
if (event.type === 'VEVENT') {
32+
const title = event.summary;
33+
34+
const timezoneL = moment.tz.guess(); // Default to UTC if timezone information is not provided
35+
36+
const startDate = moment.tz(event.start, timezoneL);
37+
38+
// Complicated case - if an RRULE exists, handle multiple recurrences of the event.
39+
if (event.rrule !== undefined) {
40+
const dates = event.rrule.between(
41+
today.toDate(),
42+
nextTwelveWeeksEnd.toDate(),
43+
true,
44+
);
45+
46+
// Loop through the set of date entries to see which recurrences should be printed.
47+
for (const date of dates) {
48+
const startDate = moment.tz(date, timezoneL);
49+
const eventtimezone = event.start.tz;
50+
const owntimezone = moment.tz.guess();
51+
const eventOffset = moment.tz(eventtimezone).utcOffset();
52+
const localOffset = moment.tz(owntimezone).utcOffset();
53+
const offsetDifference = localOffset - eventOffset;
54+
55+
// Check if the event falls within the next 4 weeks from today
56+
if (startDate.isBetween(today, nextTwelveWeeksEnd, undefined, '[]')) {
57+
const dateTimezone = moment.tz.zone(event.start.tz);
58+
let offset;
59+
if (dateTimezone && offsetDifference)
60+
offset = offsetDifference - dateTimezone.utcOffset(date);
61+
62+
const newDate = moment(date).subtract(offset, 'minutes').toDate();
63+
64+
const start = moment(newDate);
65+
const utcDate = start.utc();
66+
67+
const time = utcDate.format('MMMM Do YYYY, HH:mm');
68+
const day = utcDate.format('D');
69+
const parsedStartDate = utcDate.format('YYYY-MM-DD HH:mm:ss');
70+
arrayDates.push({
71+
title,
72+
time,
73+
day,
74+
timezone: 'UTC',
75+
parsedStartDate,
76+
});
77+
}
78+
}
79+
} else {
80+
// Simple case - no recurrences, just print out the calendar event.
81+
if (startDate.isBetween(today, nextTwelveWeeksEnd, undefined, '[]')) {
82+
const utcDate = startDate.utc();
83+
84+
const time = utcDate.format('MMMM Do YYYY, HH:mm');
85+
const day = utcDate.format('D');
86+
const parsedStartDate = utcDate.format('YYYY-MM-DD HH:mm:ss');
87+
arrayDates.push({
88+
title,
89+
time,
90+
day,
91+
timezone: 'UTC',
92+
parsedStartDate,
93+
});
94+
}
95+
}
96+
}
97+
}
98+
99+
arrayDates.sort(
100+
(x, y) =>
101+
new Date(x.parsedStartDate).getTime() -
102+
new Date(y.parsedStartDate).getTime(),
103+
);
104+
105+
return arrayDates;
106+
}

pages/community/index.page.tsx

Lines changed: 4 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import { GetStaticProps } from 'next';
1111
import Card from '~/components/Card';
1212
import Image from 'next/image';
1313
import ical from 'node-ical';
14-
import moment from 'moment-timezone';
14+
import {
15+
fetchRemoteICalFile,
16+
printEventsForNextWeeks,
17+
} from '../../lib/calendarUtils';
1518

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

38-
async function fetchRemoteICalFile(url: string) {
39-
try {
40-
const response = await fetch(url, { method: 'GET', mode: 'no-cors' });
41-
if (!response.ok) {
42-
throw new Error(`HTTP error! status: ${response.status}`);
43-
}
44-
const data = await response.text();
45-
return data;
46-
} catch (error) {
47-
console.error('Error fetching iCal file:', error);
48-
return null;
49-
}
50-
}
5141
const remoteICalUrl =
5242
'https://calendar.google.com/calendar/ical/json.schema.community%40gmail.com/public/basic.ics';
5343
const datesInfo = await fetchRemoteICalFile(remoteICalUrl)
@@ -61,88 +51,6 @@ export const getStaticProps: GetStaticProps = async () => {
6151
},
6252
};
6353
};
64-
function printEventsForNextWeeks(icalData: { [x: string]: any }) {
65-
const arrayDates = [];
66-
if (!icalData) {
67-
console.error('iCal data is empty or invalid.');
68-
return;
69-
}
70-
71-
const today = moment().startOf('day');
72-
const nextFourWeeksEnd = moment().add(12, 'weeks').endOf('day');
73-
74-
for (const event of Object.values(icalData)) {
75-
if (event.type === 'VEVENT') {
76-
const title = event.summary;
77-
78-
const timezoneL = moment.tz.guess();
79-
const startDate = moment.tz(event.start, timezoneL);
80-
81-
if (event.rrule !== undefined) {
82-
const dates = event.rrule.between(
83-
today.toDate(),
84-
nextFourWeeksEnd.toDate(),
85-
true,
86-
);
87-
88-
for (const date of dates) {
89-
const startDate = moment.tz(date, timezoneL);
90-
91-
if (startDate.isBetween(today, nextFourWeeksEnd, undefined, '[]')) {
92-
const dateTimezone = moment.tz.zone(event.start.tz);
93-
const localTimezone = moment.tz.guess();
94-
const tz =
95-
event.rrule.origOptions.tzid === localTimezone
96-
? event.rrule.origOptions.tzid
97-
: localTimezone;
98-
const timezone = moment.tz.zone(tz);
99-
let offset;
100-
if (timezone && dateTimezone)
101-
offset = timezone.utcOffset(date) - dateTimezone.utcOffset(date);
102-
const newDate = moment(date).add(offset, 'minutes').toDate();
103-
104-
const start = moment(newDate);
105-
const utcDate = start.utc();
106-
107-
const time = utcDate.format('MMMM Do YYYY, h:mm a');
108-
const day = utcDate.format('D');
109-
const parsedStartDate = utcDate.format('YYYY-MM-DD HH:mm:ss');
110-
arrayDates.push({
111-
title,
112-
time,
113-
day,
114-
timezone: 'UTC',
115-
parsedStartDate,
116-
});
117-
}
118-
}
119-
} else {
120-
if (startDate.isBetween(today, nextFourWeeksEnd, undefined, '[]')) {
121-
const utcDate = startDate.utc();
122-
123-
const time = utcDate.format('MMMM Do YYYY, h:mm a');
124-
const day = utcDate.format('D');
125-
const parsedStartDate = startDate.format('YYYY-MM-DD HH:mm:ss');
126-
arrayDates.push({
127-
title,
128-
time,
129-
day,
130-
timezone: 'UTC',
131-
parsedStartDate,
132-
});
133-
}
134-
}
135-
}
136-
}
137-
138-
arrayDates.sort(
139-
(x, y) =>
140-
new Date(x.parsedStartDate).getTime() -
141-
new Date(y.parsedStartDate).getTime(),
142-
);
143-
144-
return arrayDates;
145-
}
14654

14755
export default function communityPages(props: any) {
14856
const blogPosts = props.blogPosts;

pages/index.page.tsx

Lines changed: 10 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ const PATH = 'pages/blog/posts';
77
import readingTime from 'reading-time';
88
import Link from 'next/link';
99
import TextTruncate from 'react-text-truncate';
10-
10+
import {
11+
fetchRemoteICalFile,
12+
printEventsForNextWeeks,
13+
} from '../lib/calendarUtils';
1114
import { Headline4 } from '~/components/Headlines';
1215
import { GetStaticProps } from 'next';
1316

1417
/* eslint-disable */
1518
import ical from 'node-ical';
16-
import moment from 'moment-timezone';
1719
import { useTheme } from 'next-themes';
1820

1921
// apiKey and appId are set in the .env.local file
@@ -33,32 +35,17 @@ export const getStaticProps: GetStaticProps = async () => {
3335
);
3436
const { data: frontmatter, content } = matter(fullFileName);
3537
return {
36-
slug: slug,
38+
slug,
3739
frontmatter,
3840
content,
3941
};
4042
})
41-
.sort((a, b) => {
42-
const dateA = new Date(a.frontmatter.date).getTime();
43-
const dateB = new Date(b.frontmatter.date).getTime();
44-
return dateA < dateB ? 1 : -1;
45-
})
43+
.sort(
44+
(a, b) =>
45+
new Date(b.frontmatter.date).getTime() -
46+
new Date(a.frontmatter.date).getTime(),
47+
)
4648
.slice(0, 5);
47-
48-
// Function to fetch the remote iCal file
49-
async function fetchRemoteICalFile(url: string) {
50-
try {
51-
const response = await fetch(url, { method: 'GET', mode: 'no-cors' });
52-
if (!response.ok) {
53-
throw new Error(`HTTP error! status: ${response.status}`);
54-
}
55-
const data = await response.text();
56-
return data;
57-
} catch (error) {
58-
console.error('Error fetching iCal file:', error);
59-
return null;
60-
}
61-
}
6249
// Example usage:
6350
const remoteICalUrl =
6451
'https://calendar.google.com/calendar/ical/json.schema.community%40gmail.com/public/basic.ics'; // Replace with the actual URL
@@ -74,99 +61,7 @@ export const getStaticProps: GetStaticProps = async () => {
7461
},
7562
};
7663
};
77-
// Function to filter and print events for the next weeks from today
78-
function printEventsForNextWeeks(icalData: { [x: string]: any }) {
79-
const arrayDates = [];
80-
if (!icalData) {
81-
console.error('iCal data is empty or invalid.');
82-
return;
83-
}
84-
85-
// Calculate the range of dates for the next 12 weeks from today
86-
const today = moment().startOf('day');
87-
const nextFourWeeksEnd = moment().add(12, 'weeks').endOf('day');
88-
89-
// Loop through the events in the iCal data
90-
for (const k in icalData) {
91-
const event = icalData[k];
92-
93-
if (event.type === 'VEVENT') {
94-
const title = event.summary;
95-
96-
const timezoneL = moment.tz.guess(); // Default to UTC if timezone information is not provided
97-
const startDate = moment.tz(event.start, timezoneL);
98-
99-
// Complicated case - if an RRULE exists, handle multiple recurrences of the event.
100-
if (event.rrule !== undefined) {
101-
// For recurring events, get the set of event start dates that fall within the range
102-
// of dates we're looking for.
103-
const dates = event.rrule.between(
104-
today.toDate(),
105-
nextFourWeeksEnd.toDate(),
106-
true,
107-
);
10864

109-
// Loop through the set of date entries to see which recurrences should be printed.
110-
for (const date of dates) {
111-
const startDate = moment.tz(date, timezoneL);
112-
113-
// Check if the event falls within the next 4 weeks from today
114-
if (startDate.isBetween(today, nextFourWeeksEnd, undefined, '[]')) {
115-
const dateTimezone = moment.tz.zone(event.start.tz);
116-
const localTimezone = moment.tz.guess();
117-
const tz =
118-
event.rrule.origOptions.tzid === localTimezone
119-
? event.rrule.origOptions.tzid
120-
: localTimezone;
121-
const timezone = moment.tz.zone(tz);
122-
let offset;
123-
if (timezone && dateTimezone)
124-
offset = timezone.utcOffset(date) - dateTimezone.utcOffset(date);
125-
const newDate = moment(date).add(offset, 'minutes').toDate();
126-
127-
const start = moment(newDate);
128-
const utcDate = start.utc();
129-
130-
const time = utcDate.format('MMMM Do YYYY, h:mm a');
131-
const day = utcDate.format('D');
132-
const parsedStartDate = utcDate.format('YYYY-MM-DD HH:mm:ss');
133-
arrayDates.push({
134-
title,
135-
time,
136-
day,
137-
timezone: 'UTC',
138-
parsedStartDate,
139-
});
140-
}
141-
}
142-
} else {
143-
// Simple case - no recurrences, just print out the calendar event.
144-
if (startDate.isBetween(today, nextFourWeeksEnd, undefined, '[]')) {
145-
const utcDate = startDate.utc();
146-
147-
const time = utcDate.format('MMMM Do YYYY, h:mm a');
148-
const day = utcDate.format('D');
149-
const parsedStartDate = startDate.format('YYYY-MM-DD HH:mm:ss');
150-
arrayDates.push({
151-
title,
152-
time,
153-
day,
154-
timezone: 'UTC',
155-
parsedStartDate,
156-
});
157-
}
158-
}
159-
}
160-
}
161-
162-
arrayDates.sort(
163-
(x, y) =>
164-
new Date(x.parsedStartDate).getTime() -
165-
new Date(y.parsedStartDate).getTime(),
166-
);
167-
168-
return arrayDates;
169-
}
17065
export function AlgoliaSearch() {
17166
useEffect(() => {
17267
const customButton = document.querySelector('.herobtn');

0 commit comments

Comments
 (0)