diff --git a/ChangeLog b/ChangeLog index 0041f4b31..80e169a81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2011-01-21 Francis Lachapelle + + * SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m + (-doesOccurOnDate:): we set the timezone of floating all-day + events to the one of the date passed as argument. + + * UI/Scheduler/UIxAppointmentEditor.m (-defaultAction): when + dealing with "floating" start dates (no timezone), we convert it to + the user's timezone. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + (:_flattenCycleRecord:forRange:intoArray:): we must set the + timezone of floating all-day events to the user's timezone. + (_flattenCycleRecords:fetchRange:): Adjust the range so it ends at + midnight. This is necessary when calculating recurrences of all-day events. + 2011-01-17 Francis Lachapelle * SoObjects/Appointments/SOGoEMailAlarmsManager.m diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 86bd4fff5..4dae9225b 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -827,6 +827,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir * @param theRecord the event definition. * @param theRange the period to look in. * @param theRecords the array into which are copied the resulting occurrences. + * @see [iCalRepeatableEntityObject+SOGo doesOccurOnDate:] */ - (void) _flattenCycleRecord: (NSDictionary *) theRecord forRange: (NGCalendarDateRange *) theRange @@ -840,10 +841,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir NSArray *elements, *components; NSString *content; iCalRepeatableEntityObject *component; - iCalDateTime *firstStartDate, *firstEndDate; + id firstStartDate, firstEndDate; NSCalendarDate *checkStartDate, *checkEndDate; iCalTimeZone *eventTimeZone; - unsigned i, count; + unsigned i, count, offset; records = [NSMutableArray array]; ranges = nil; @@ -882,9 +883,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir { // Retrieve the range of the first event component = [components objectAtIndex: 0]; - firstStartDate = (iCalDateTime*)[component uniqueChildWithTag: @"dtstart"]; - firstEndDate = (iCalDateTime*)[component uniqueChildWithTag: @"dtend"]; - eventTimeZone = [firstStartDate timeZone]; + firstStartDate = [component uniqueChildWithTag: @"dtstart"]; + firstEndDate = [component uniqueChildWithTag: @"dtend"]; + eventTimeZone = [(iCalDateTime*)firstStartDate timeZone]; firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [[[firstStartDate values] lastObject] asCalendarDate] endDate: [[[firstEndDate values] lastObject] asCalendarDate]]; @@ -897,6 +898,20 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir endDate: checkEndDate]; exDates = [eventTimeZone computedDatesForStrings: exDates]; } + else if ([theRecord objectForKey: @"c_isallday"]) + { + // The event lasts all-day and has no timezone (floating); we convert the range of the first event + // to the user's timezone + offset = [timeZone secondsFromGMTForDate: [firstRange startDate]]; + firstStartDate = (NSCalendarDate*)[[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + seconds:-offset]; + firstEndDate = (NSCalendarDate*)[[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + seconds:-offset]; + [firstStartDate setTimeZone: timeZone]; + [firstEndDate setTimeZone: timeZone]; + firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate + endDate: firstEndDate]; + } // Calculate the occurrences for the given range ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: theRange @@ -934,11 +949,18 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir // TODO: is the result supposed to be sorted by date? NSMutableArray *ma; NSDictionary *row; + NSCalendarDate *rangeEndDate; unsigned int count, max; max = [_records count]; ma = [NSMutableArray arrayWithCapacity: max]; + // Adjust the range so it ends at midnight. This is necessary when calculating + // recurrences of all-day events. + rangeEndDate = [[_r endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:1]; + _r = [NGCalendarDateRange calendarDateRangeWithStartDate: [_r startDate] + endDate: rangeEndDate]; + for (count = 0; count < max; count++) { row = [_records objectAtIndex: count]; diff --git a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m index c03ee9956..4e510999a 100644 --- a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m @@ -110,6 +110,7 @@ /** * Checks if a date is part of the recurring entity. * @param theOccurrenceDate the date to verify. + * @see [SOGoAppointmentFolder _flattenCycleRecord:forRange:intoArray:] * @return true if the occurence date is part of the recurring entity. */ - (BOOL) doesOccurOnDate: (NSCalendarDate *) theOccurenceDate @@ -117,9 +118,11 @@ NSArray *ranges; NGCalendarDateRange *checkRange, *firstRange; NSCalendarDate *startDate, *endDate; - iCalDateTime *firstStartDate; + NSTimeZone *timeZone; + id *firstStartDate, firstEndDate; iCalTimeZone *eventTimeZone; BOOL doesOccur; + int offset; doesOccur = [self isRecurrent]; if (doesOccur) @@ -128,12 +131,29 @@ firstRange = [self firstOccurenceRange]; // Set the range to check with respect to the event timezone (extracted from the start date) - firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"]; - eventTimeZone = [firstStartDate timeZone]; + firstStartDate = [self uniqueChildWithTag: @"dtstart"]; + eventTimeZone = [(iCalDateTime *)firstStartDate timeZone]; if (eventTimeZone) startDate = [eventTimeZone computedDateForDate: theOccurenceDate]; else - startDate = theOccurenceDate; + { + startDate = theOccurenceDate; + if ([self isAllDay]) + { + // The event lasts all-day and has no timezone (floating); we convert the range of the first event + // to the occurence's timezone. + timeZone = [theOccurenceDate timeZone]; + offset = [timeZone secondsFromGMTForDate: [firstRange startDate]]; + firstStartDate = (NSCalendarDate*)[[firstRange startDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + seconds:-offset]; + firstEndDate = (NSCalendarDate*)[[firstRange endDate] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + seconds:-offset]; + [firstStartDate setTimeZone: timeZone]; + [firstEndDate setTimeZone: timeZone]; + firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate + endDate: firstEndDate]; + } + } endDate = [startDate addTimeInterval: [self occurenceInterval]]; checkRange = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate endDate: endDate]; diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index 1478e529a..4be043942 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -245,8 +245,10 @@ NSString *duration; NSTimeZone *timeZone; unsigned int total, hours, minutes; + signed int offset; SOGoObject *co; SOGoUserDefaults *ud; + iCalTimeZone *eventTimeZone; [self event]; co = [self clientObject]; @@ -280,7 +282,21 @@ isAllDay = [event isAllDay]; endDate = [event endDate]; if (isAllDay) - endDate = [endDate dateByAddingYears: 0 months: 0 days: -1]; + { + endDate = [endDate dateByAddingYears: 0 months: 0 days: -1]; + + // Verify if the start date is "floating" (no timezone). In this case, convert it + // to the user's timezone. + eventTimeZone = [(iCalDateTime*)[event uniqueChildWithTag: @"dtstart"] timeZone]; + if (eventTimeZone == nil) + { + offset = [timeZone secondsFromGMTForDate: startDate]; + startDate = [startDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + seconds:-offset]; + endDate = [endDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 + seconds:-offset]; + } + } isTransparent = ![event isOpaque]; }