mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-03-20 13:55:58 +00:00
fix(calendar): Repeated events didn't use the correct timezone in some cases
This commit is contained in:
@@ -85,13 +85,12 @@
|
||||
|
||||
tzId = [self value: 0 ofAttribute: @"tzid"];
|
||||
if ([tzId length])
|
||||
{
|
||||
calendar
|
||||
= (iCalCalendar *) [self searchParentOfClass: [iCalCalendar class]];
|
||||
{
|
||||
calendar = (iCalCalendar *) [self searchParentOfClass: [iCalCalendar class]];
|
||||
timeZone = [calendar timeZoneWithId: tzId];
|
||||
//if (!timeZone)
|
||||
//[self logWithFormat: @"timezone '%@' not found in calendar", tzId];
|
||||
}
|
||||
}
|
||||
|
||||
return timeZone;
|
||||
}
|
||||
@@ -169,37 +168,37 @@
|
||||
count = [subValues count];
|
||||
dates = [NSMutableArray arrayWithCapacity: count];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
date = [subValues objectAtIndex: i];
|
||||
iTZ = [self timeZone];
|
||||
{
|
||||
date = [subValues objectAtIndex: i];
|
||||
iTZ = [self timeZone];
|
||||
|
||||
if (iTZ)
|
||||
dateTime = [iTZ dateForDateTimeString: date];
|
||||
else
|
||||
{
|
||||
initialDate = [date asCalendarDate];
|
||||
if (initialDate)
|
||||
dateTime = initialDate;
|
||||
/*
|
||||
{
|
||||
if ([date hasSuffix: @"Z"] || [date hasSuffix: @"z"])
|
||||
dateTime = initialDate;
|
||||
else
|
||||
{
|
||||
// same TODO as above
|
||||
tz = [NSTimeZone defaultTimeZone];
|
||||
dateTime = [initialDate addYear: 0 month: 0 day: 0
|
||||
hour: 0 minute: 0
|
||||
second: -[tz secondsFromGMTForDate: initialDate]];
|
||||
}
|
||||
}
|
||||
*/
|
||||
else
|
||||
dateTime = nil;
|
||||
}
|
||||
if (dateTime)
|
||||
[dates addObject: dateTime];
|
||||
}
|
||||
if (iTZ)
|
||||
dateTime = [iTZ dateForDateTimeString: date];
|
||||
else
|
||||
{
|
||||
initialDate = [date asCalendarDate];
|
||||
if (initialDate)
|
||||
dateTime = initialDate;
|
||||
/*
|
||||
{
|
||||
if ([date hasSuffix: @"Z"] || [date hasSuffix: @"z"])
|
||||
dateTime = initialDate;
|
||||
else
|
||||
{
|
||||
// same TODO as above
|
||||
tz = [NSTimeZone defaultTimeZone];
|
||||
dateTime = [initialDate addYear: 0 month: 0 day: 0
|
||||
hour: 0 minute: 0
|
||||
second: -[tz secondsFromGMTForDate: initialDate]];
|
||||
}
|
||||
}
|
||||
*/
|
||||
else
|
||||
dateTime = nil;
|
||||
}
|
||||
if (dateTime)
|
||||
[dates addObject: dateTime];
|
||||
}
|
||||
|
||||
return dates;
|
||||
}
|
||||
|
||||
@@ -122,37 +122,44 @@
|
||||
NSEnumerator *dateList;
|
||||
NSCalendarDate *rDate;
|
||||
NSString *dateString;
|
||||
NSTimeZone *rdateTimezone;
|
||||
int offset;
|
||||
unsigned i;
|
||||
|
||||
if (theTimeZone)
|
||||
{
|
||||
dates = [NSMutableArray array];
|
||||
dateList = [[self childrenWithTag: @"rdate"] objectEnumerator];
|
||||
{
|
||||
dates = [NSMutableArray array];
|
||||
dateList = [[self childrenWithTag: @"rdate"] objectEnumerator];
|
||||
|
||||
while ((dateString = [dateList nextObject]))
|
||||
{
|
||||
rDates = [(iCalDateTime*) dateString dateTimes];
|
||||
for (i = 0; i < [rDates count]; i++)
|
||||
while ((dateString = [dateList nextObject]))
|
||||
{
|
||||
|
||||
rDates = [(iCalDateTime*) dateString dateTimes];
|
||||
for (i = 0; i < [rDates count]; i++)
|
||||
{
|
||||
rDate = [rDates objectAtIndex: i];
|
||||
|
||||
// Example: timezone is -0400, date is 2012-05-24 (00:00:00 +0000),
|
||||
// and changes to 2012-05-24 04:00:00 +0000
|
||||
if ([theTimeZone isKindOfClass: [iCalTimeZone class]])
|
||||
{
|
||||
rDate = [(iCalTimeZone *) theTimeZone computedDateForDate: rDate];
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = [(NSTimeZone *) theTimeZone secondsFromGMTForDate: rDate];
|
||||
rDate = (NSCalendarDate *) [rDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
}
|
||||
|
||||
if ((rdateTimezone =[rDate timeZone]))
|
||||
{
|
||||
//The property rdate can have the timezone, https://www.kanzaki.com/docs/ical/rdate.html
|
||||
//In that case, dont force the
|
||||
}
|
||||
// Example: timezone is -0400, date is 2012-05-24 (00:00:00 +0000),
|
||||
// and changes to 2012-05-24 04:00:00 +0000
|
||||
else if ([theTimeZone isKindOfClass: [iCalTimeZone class]])
|
||||
{
|
||||
rDate = [(iCalTimeZone *) theTimeZone computedDateForDate: rDate];
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = [(NSTimeZone *) theTimeZone secondsFromGMTForDate: rDate];
|
||||
rDate = (NSCalendarDate *) [rDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-offset];
|
||||
}
|
||||
[(NSMutableArray *) dates addObject: rDate];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
dates = [self recurrenceDates];
|
||||
|
||||
@@ -351,33 +358,32 @@
|
||||
unsigned i;
|
||||
|
||||
if (theTimeZone)
|
||||
{
|
||||
dates = [NSMutableArray array];
|
||||
dateList = [[self childrenWithTag: @"exdate"] objectEnumerator];
|
||||
{
|
||||
dates = [NSMutableArray array];
|
||||
dateList = [[self childrenWithTag: @"exdate"] objectEnumerator];
|
||||
|
||||
while ((dateString = [dateList nextObject]))
|
||||
{
|
||||
exDates = [(iCalDateTime*) dateString dateTimes];
|
||||
for (i = 0; i < [exDates count]; i++)
|
||||
while ((dateString = [dateList nextObject]))
|
||||
{
|
||||
exDates = [(iCalDateTime*) dateString dateTimes];
|
||||
for (i = 0; i < [exDates count]; i++)
|
||||
{
|
||||
exDate = [exDates objectAtIndex: i];
|
||||
|
||||
// Example: timezone is -0400, date is 2012-05-24 (00:00:00 +0000),
|
||||
// and changes to 2012-05-24 04:00:00 +0000
|
||||
if ([theTimeZone isKindOfClass: [iCalTimeZone class]])
|
||||
{
|
||||
exDate = [(iCalTimeZone *) theTimeZone computedDateForDate: exDate];
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = [(NSTimeZone *) theTimeZone secondsFromGMTForDate: exDate];
|
||||
exDate = (NSCalendarDate *) [exDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
}
|
||||
// Example: timezone is -0400, date is 2012-05-24 (00:00:00 +0000),
|
||||
// and changes to 2012-05-24 04:00:00 +0000
|
||||
if ([theTimeZone isKindOfClass: [iCalTimeZone class]])
|
||||
{
|
||||
exDate = [(iCalTimeZone *) theTimeZone computedDateForDate: exDate];
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = [(NSTimeZone *) theTimeZone secondsFromGMTForDate: exDate];
|
||||
exDate = (NSCalendarDate *) [exDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-offset];
|
||||
}
|
||||
[(NSMutableArray *) dates addObject: exDate];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
dates = [self exceptionDates];
|
||||
|
||||
|
||||
@@ -1282,11 +1282,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
|
||||
cycleinfo = [content propertyList];
|
||||
if (!cycleinfo)
|
||||
{
|
||||
[self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@",
|
||||
theRecord];
|
||||
return;
|
||||
}
|
||||
{
|
||||
[self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@", theRecord];
|
||||
return;
|
||||
}
|
||||
rules = [cycleinfo objectForKey: @"rules"];
|
||||
exRules = [cycleinfo objectForKey: @"exRules"];
|
||||
rDates = [cycleinfo objectForKey: @"rDates"];
|
||||
@@ -1322,54 +1321,48 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
|
||||
eventTimeZone = [dtstart timeZone];
|
||||
if (eventTimeZone)
|
||||
{
|
||||
// Adjust the range to check with respect to the event timezone (extracted from the start date)
|
||||
checkStartDate = [eventTimeZone computedDateForDate: [theRange startDate]];
|
||||
checkEndDate = [eventTimeZone computedDateForDate: [theRange endDate]];
|
||||
recurrenceRange = [NGCalendarDateRange calendarDateRangeWithStartDate: checkStartDate
|
||||
endDate: checkEndDate];
|
||||
|
||||
}
|
||||
{
|
||||
// Adjust the range to check with respect to the event timezone (extracted from the start date)
|
||||
checkStartDate = [eventTimeZone computedDateForDate: [theRange startDate]];
|
||||
checkEndDate = [eventTimeZone computedDateForDate: [theRange endDate]];
|
||||
recurrenceRange = [NGCalendarDateRange calendarDateRangeWithStartDate: checkStartDate endDate: checkEndDate];
|
||||
}
|
||||
else
|
||||
{
|
||||
recurrenceRange = theRange;
|
||||
if ([[theRecord objectForKey: @"c_isallday"] boolValue])
|
||||
{
|
||||
recurrenceRange = theRange;
|
||||
if ([[theRecord objectForKey: @"c_isallday"] boolValue])
|
||||
{
|
||||
// The event lasts all-day and has no timezone (floating); we convert the range of the first event
|
||||
// to the user's timezone
|
||||
allDayTimeZone = timeZone;
|
||||
offset = [allDayTimeZone secondsFromGMTForDate: [firstRange startDate]];
|
||||
firstStartDate = [[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
firstEndDate = [[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0
|
||||
seconds:-offset];
|
||||
[firstStartDate setTimeZone: allDayTimeZone];
|
||||
[firstEndDate setTimeZone: allDayTimeZone];
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate
|
||||
endDate: firstEndDate];
|
||||
}
|
||||
// The event lasts all-day and has no timezone (floating); we convert the range of the first event
|
||||
// to the user's timezone
|
||||
allDayTimeZone = timeZone;
|
||||
offset = [allDayTimeZone secondsFromGMTForDate: [firstRange startDate]];
|
||||
firstStartDate = [[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-offset];
|
||||
firstEndDate = [[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-offset];
|
||||
[firstStartDate setTimeZone: allDayTimeZone];
|
||||
[firstEndDate setTimeZone: allDayTimeZone];
|
||||
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate endDate: firstEndDate];
|
||||
}
|
||||
}
|
||||
|
||||
#warning this code is ugly: we should not mix objects with different types as \
|
||||
it reduces readability
|
||||
tz = eventTimeZone ? eventTimeZone : allDayTimeZone;
|
||||
if (tz)
|
||||
{
|
||||
// Adjust the recurrence and exception dates
|
||||
exDates = [component exceptionDatesWithTimeZone: tz];
|
||||
rDates = [component recurrenceDatesWithTimeZone: tz];
|
||||
|
||||
// Adjust the recurrence rules "until" dates
|
||||
rules = [component recurrenceRulesWithTimeZone: tz];
|
||||
exRules = [component exceptionRulesWithTimeZone: tz];
|
||||
}
|
||||
{
|
||||
// Adjust the recurrence and exception dates
|
||||
exDates = [component exceptionDatesWithTimeZone: tz];
|
||||
rDates = [component recurrenceDatesWithTimeZone: tz];
|
||||
|
||||
// Adjust the recurrence rules "until" dates
|
||||
rules = [component recurrenceRulesWithTimeZone: tz];
|
||||
exRules = [component exceptionRulesWithTimeZone: tz];
|
||||
}
|
||||
|
||||
rules = [rules uniqueObjects];
|
||||
|
||||
// Calculate the occurrences for the given range
|
||||
records = [NSMutableArray array];
|
||||
ranges =
|
||||
[NSMutableArray arrayWithArray:
|
||||
ranges = [NSMutableArray arrayWithArray:
|
||||
[iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: recurrenceRange
|
||||
firstInstanceCalendarDateRange: firstRange
|
||||
recurrenceRules: rules
|
||||
@@ -1380,33 +1373,32 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
// Add the master occurrence when dealing with RDATES.
|
||||
// However, the master event must not be flagged with X-MOZ-FAKED-MASTER.
|
||||
if ([component hasRecurrenceDates] &&
|
||||
![[[component uniqueChildWithTag: @"x-moz-faked-master"]
|
||||
flattenedValuesForKey: @""] isEqualToString: @"1"] &&
|
||||
[recurrenceRange doesIntersectWithDateRange: firstRange])
|
||||
{
|
||||
[ranges insertObject: firstRange atIndex: 0];
|
||||
}
|
||||
![[[component uniqueChildWithTag: @"x-moz-faked-master"]flattenedValuesForKey: @""] isEqualToString: @"1"] &&
|
||||
[recurrenceRange doesIntersectWithDateRange: firstRange])
|
||||
{
|
||||
[ranges insertObject: firstRange atIndex: 0];
|
||||
}
|
||||
|
||||
max = [ranges count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
oneRange = [ranges objectAtIndex: count];
|
||||
fixedRow = [self fixupCycleRecord: row
|
||||
cycleRange: oneRange
|
||||
firstInstanceCalendarDateRange: firstRange
|
||||
withEventTimeZone: eventTimeZone];
|
||||
|
||||
// We now adjust the c_nextalarm based on each occurences. For each of them, we use the master event
|
||||
// alarm information since exceptions to recurrence rules might have their own, while that is not the
|
||||
// case for standard occurences.
|
||||
if ([component hasAlarms])
|
||||
{
|
||||
oneRange = [ranges objectAtIndex: count];
|
||||
fixedRow = [self fixupCycleRecord: row
|
||||
cycleRange: oneRange
|
||||
firstInstanceCalendarDateRange: firstRange
|
||||
withEventTimeZone: eventTimeZone];
|
||||
|
||||
// We now adjust the c_nextalarm based on each occurences. For each of them, we use the master event
|
||||
// alarm information since exceptions to recurrence rules might have their own, while that is not the
|
||||
// case for standard occurences.
|
||||
if ([component hasAlarms])
|
||||
{
|
||||
[self _computeAlarmForRow: fixedRow
|
||||
master: component];
|
||||
}
|
||||
|
||||
[records addObject: fixedRow];
|
||||
[self _computeAlarmForRow: fixedRow
|
||||
master: component];
|
||||
}
|
||||
|
||||
[records addObject: fixedRow];
|
||||
}
|
||||
|
||||
[self _appendCycleExceptionsFromRow: row
|
||||
firstInstanceCalendarDateRange: firstRange
|
||||
@@ -1414,7 +1406,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
withTimeZone: allDayTimeZone
|
||||
withCalendar: calendar
|
||||
toArray: records];
|
||||
|
||||
|
||||
[theRecords addObjectsFromArray: records];
|
||||
} // if ([components count]) ...
|
||||
}
|
||||
|
||||
@@ -892,7 +892,8 @@ static inline NSString* _userStateInEvent (NSArray *event)
|
||||
NSMutableArray *fields, *dayEvents, *newEvent, *allDayEvents;
|
||||
NSMutableDictionary *allEvents, *monthData, *monthEvents, *dayData;
|
||||
NSString *sort, *ascending, *day, *weekDay, *month, *userState;
|
||||
unsigned int interval, count, max;
|
||||
unsigned int count, max;
|
||||
long long interval;
|
||||
SEL sortSelector;
|
||||
|
||||
[self _setupContext];
|
||||
|
||||
Reference in New Issue
Block a user