Skip to content

Commit 924b441

Browse files
authored
Merge pull request #178 from sKawashima/fix-recurring-schedule-filter
Fix timezone handling for recurring events
2 parents 61c3083 + cd30e5c commit 924b441

File tree

1 file changed

+22
-17
lines changed

1 file changed

+22
-17
lines changed

src/icalUtils.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,14 @@ export function extractMeetingInfo(e: any): { callUrl: string, callType: string
2727
}
2828

2929
function applyRecurrenceDateAndTimezone(originalDate: Date, currentDate: Date, tzid: string): Date {
30-
const momentOriginal = tz(originalDate, tzid);
31-
const momentCurrent = tz(currentDate, tzid);
30+
const originalMoment = tz(originalDate, tzid);
3231

33-
// Calculate the difference in hours and minutes between the original and current
34-
const hourOffset = momentOriginal.hour() - momentCurrent.hour();
35-
const minuteOffset = momentOriginal.minute() - momentCurrent.minute();
32+
// Create a moment in the target timezone using just the date components of currentDate
33+
// This avoids timezone conversion issues with the recurrence date
34+
const currentDateMoment = moment.utc(currentDate);
35+
const adjustedMoment = tz(`${currentDateMoment.format('YYYY-MM-DD')} ${originalMoment.format('HH:mm:ss')}`, tzid);
3636

37-
// Adjust the current date by the offset to keep the local time constant
38-
return momentCurrent.add(hourOffset, 'hours').add(minuteOffset, 'minutes').toDate();
37+
return adjustedMoment.toDate();
3938
}
4039

4140
function isExcluded(recurrenceDate: moment.Moment, exdateArray: moment.Moment[]): boolean {
@@ -65,29 +64,35 @@ function processRecurrenceOverrides(event: any, sortedDaysToMatch: string[], exc
6564
}
6665

6766
function processRecurringRules(event: any, sortedDaysToMatch: string[], excludedDates: moment.Moment[], matchingEvents: any[]) {
68-
const localStartOfRange = moment(sortedDaysToMatch.first()).subtract(1, 'day').startOf('day').toDate();
69-
const localEndOfRange = moment(sortedDaysToMatch.last()).add(1, 'day').endOf('day').toDate();
67+
const tzid = event.rrule.origOptions.tzid || 'UTC';
68+
69+
// Use UTC for rrule calculations to avoid timezone offset issues
70+
const startOfRange = moment.utc(sortedDaysToMatch.first()).subtract(1, 'day').startOf('day').toDate();
71+
const endOfRange = moment.utc(sortedDaysToMatch.last()).add(1, 'day').endOf('day').toDate();
7072

7173
// Get recurrence dates within the range
72-
const recurrenceDates = event.rrule.between(localStartOfRange, localEndOfRange, true);
74+
const recurrenceDates = event.rrule.between(startOfRange, endOfRange, true);
7375

7476
recurrenceDates.forEach(recurrenceDate => {
75-
const recurrenceMoment = tz(recurrenceDate, event.rrule.origOptions.tzid || 'UTC');
76-
77-
// Clone the event and use the recurrence date directly if start and end times are present
78-
const clonedEvent = { ...event };
79-
clonedEvent.start = applyRecurrenceDateAndTimezone(event.start, recurrenceDate, event.rrule.origOptions.tzid);
80-
clonedEvent.end = applyRecurrenceDateAndTimezone(event.end, recurrenceDate, event.rrule.origOptions.tzid);
77+
const recurrenceMoment = tz(recurrenceDate, tzid).startOf('day');
8178

8279
if (isExcluded(recurrenceMoment, excludedDates)) {
8380
console.debug(`Skipping excluded recurrence: ${event.summary} on ${recurrenceMoment.format('YYYY-MM-DD')}`);
8481
return;
8582
}
8683

84+
// Clone the event and apply proper timezone-aware date/time calculation
85+
const clonedEvent = { ...event };
86+
clonedEvent.start = applyRecurrenceDateAndTimezone(event.start, recurrenceDate, tzid);
87+
clonedEvent.end = applyRecurrenceDateAndTimezone(event.end, recurrenceDate, tzid);
88+
8789
delete clonedEvent.rrule;
8890
clonedEvent.recurrent = true;
8991

90-
if (moment(clonedEvent.start).isBetween(sortedDaysToMatch.first(), sortedDaysToMatch.last(), 'day', '[]')) {
92+
// Check if the recurrence falls within the requested date range using timezone-aware comparison
93+
const eventStartMoment = tz(clonedEvent.start, tzid);
94+
const eventStartDate = eventStartMoment.format('YYYY-MM-DD');
95+
if (sortedDaysToMatch.includes(eventStartDate)) {
9196
console.debug(`Adding recurring event: ${clonedEvent.summary} ${clonedEvent.start} - ${clonedEvent.end}`);
9297
console.debug("Excluded dates:", excludedDates.map(date => date.format('YYYY-MM-DD')));
9398

0 commit comments

Comments
 (0)