diff --git a/ChangeLog b/ChangeLog index 935d2b160..2d7d42a74 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2011-01-13 Francis Lachapelle + + * SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m + (-doesOccurOnDate:): the date to verify must be adjusted to the + event's timezone before performing the calculation of the + recurrence rule. + + * SoObjects/Appointments/iCalEvent+SOGo.m (-firstOccurenceRange): + extract the original start and end dates from the event, based on + the event's timezone. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + (-fixupCycleRecord:cycleRange:firstInstanceCalendarDateRange:withEventTimeZone:): + the event dates must be first be adjusted to the event's timezone + before being adjusted to the user's timezone. + (-_flattenCycleRecord:forRange:intoArray:): the calculation of the + occurrences must be performed with respect to the event's + timezone, not the user's timezone. + + * SoObjects/Appointments/SOGoAppointmentObject.m + (-newOccurenceWithID:): new occurrences are now independant of the + user's timezone. + 2011-01-11 Ludovic Marcotte * OpenChange/MAPIStoreContactsMessageTable.m diff --git a/SOPE/NGCards/ChangeLog b/SOPE/NGCards/ChangeLog index 7c53d724a..4f2ba4898 100644 --- a/SOPE/NGCards/ChangeLog +++ b/SOPE/NGCards/ChangeLog @@ -1,3 +1,13 @@ +2011-01-13 Francis Lachapelle + + * iCalTimeZone.m (-computedDateForDate:): Was + _computedDateTimeForDate. Adjusts a date with respect to this + vTimeZone. + + * iCalDateTime.m (-dateTimes): when there's no timezone defined + (which is the case when parsing a vTimeZone!), don't fallback to + the system timezone. + 2010-11-18 Wolfgang Sourdeau * NGVCard.m (-setVName, vName): removed useless and non-standard diff --git a/SOPE/NGCards/iCalDailyRecurrenceCalculator.m b/SOPE/NGCards/iCalDailyRecurrenceCalculator.m index 1fa749048..ee8c2b6f6 100644 --- a/SOPE/NGCards/iCalDailyRecurrenceCalculator.m +++ b/SOPE/NGCards/iCalDailyRecurrenceCalculator.m @@ -161,6 +161,7 @@ currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]]; r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate endDate: currentEndDate]; + if ([_r containsDateRange: r]) [ranges addObject: r]; } diff --git a/SOPE/NGCards/iCalDateTime.m b/SOPE/NGCards/iCalDateTime.m index d11fd139c..f572f9881 100644 --- a/SOPE/NGCards/iCalDateTime.m +++ b/SOPE/NGCards/iCalDateTime.m @@ -1,6 +1,6 @@ /* iCalDateTime.m - this file is part of SOPE * - * Copyright (C) 2006 Inverse inc. + * Copyright (C) 2006-2011 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -161,7 +161,7 @@ NSString *date; NSCalendarDate *initialDate, *dateTime; NSMutableArray *dates; - NSTimeZone *tz; + //NSTimeZone *tz; unsigned count, i; count = [[self values] count]; @@ -170,24 +170,28 @@ { date = [self value: 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 */ + // 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; } diff --git a/SOPE/NGCards/iCalTimeZone.h b/SOPE/NGCards/iCalTimeZone.h index a0963844d..339df3711 100644 --- a/SOPE/NGCards/iCalTimeZone.h +++ b/SOPE/NGCards/iCalTimeZone.h @@ -1,6 +1,6 @@ /* iCalTimeZone.h - this file is part of SOPE * - * Copyright (C) 2006-2009 Inverse inc. + * Copyright (C) 2006-2011 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -28,10 +28,14 @@ #import "CardGroup.h" +@class iCalTimeZonePeriod; + @interface iCalTimeZone : CardGroup + (iCalTimeZone *) timeZoneForName: (NSString *) theName; - (NSString *) tzId; +- (iCalTimeZonePeriod *) periodForDate: (NSCalendarDate *) date; +- (NSCalendarDate *) computedDateForDate: (NSCalendarDate *) theDate; - (NSString *) dateTimeStringForDate: (NSCalendarDate *) date; - (NSString *) dateStringForDate: (NSCalendarDate *) date; - (NSCalendarDate *) dateForDateTimeString: (NSString *) string; diff --git a/SOPE/NGCards/iCalTimeZone.m b/SOPE/NGCards/iCalTimeZone.m index 1f61abc49..9ca51dc88 100644 --- a/SOPE/NGCards/iCalTimeZone.m +++ b/SOPE/NGCards/iCalTimeZone.m @@ -1,9 +1,10 @@ /* iCalTimeZone.m - this file is part of SOPE * - * Copyright (C) 2006-2009 Inverse inc. + * Copyright (C) 2006-2011 Inverse inc. * * Author: Wolfgang Sourdeau * Ludovic Marcotte + * Francis Lachapelle * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -185,35 +186,38 @@ static NSMutableDictionary *cache; period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"daylight"]; } - // NSLog (@"chosen period: '%@'", [period tag]); - return period; } -- (NSCalendarDate *) _computedDateTimeForDate: (NSCalendarDate *) date +/** + * Adjust a date with respect to this vTimeZone. + * @param theDate the date to adjust to the timezone. + * @return a new GMT date adjusted with the offset of the timezone. + */ +- (NSCalendarDate *) computedDateForDate: (NSCalendarDate *) theDate { NSCalendarDate *tmpDate; NSTimeZone *utc; utc = [NSTimeZone timeZoneWithName: @"GMT"]; - tmpDate = [date copy]; + tmpDate = [theDate copy]; [tmpDate autorelease]; [tmpDate setTimeZone: utc]; return [tmpDate addYear: 0 month: 0 day: 0 hour: 0 minute: 0 - second: [[self periodForDate: date] secondsOffsetFromGMT]]; + second: [[self periodForDate: theDate] secondsOffsetFromGMT]]; } - (NSString *) dateTimeStringForDate: (NSCalendarDate *) date { - return [[self _computedDateTimeForDate: date] + return [[self computedDateForDate: date] iCalFormattedDateTimeString]; } - (NSString *) dateStringForDate: (NSCalendarDate *) date { - return [[self _computedDateTimeForDate: date] + return [[self computedDateForDate: date] iCalFormattedDateString]; } diff --git a/SOPE/NGCards/iCalTimeZonePeriod.m b/SOPE/NGCards/iCalTimeZonePeriod.m index 512c1a551..7a774ca99 100644 --- a/SOPE/NGCards/iCalTimeZonePeriod.m +++ b/SOPE/NGCards/iCalTimeZonePeriod.m @@ -137,6 +137,7 @@ } tzStart = [self startDate]; + [tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]]; tmpDate = [NSCalendarDate dateWithYear: [refDate yearOfCommonEra] month: [[rrule namedValue: @"bymonth"] intValue] diff --git a/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m b/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m index 052d93065..41ed0dc0f 100644 --- a/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m +++ b/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m @@ -186,6 +186,7 @@ currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]]; r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate endDate: currentEndDate]; + if ([_r containsDateRange: r]) [ranges addObject: r]; } diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 84d3fdc08..ff0c7147d 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2009 Inverse inc. + Copyright (C) 2007-2011 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of OpenGroupware.org. @@ -46,6 +46,7 @@ #import #import #import +#import #import #import #import @@ -576,33 +577,38 @@ static NSNumber *sharedYes = nil; forKey: [currentRecord objectForKey: @"c_name"]]; } -- (NSMutableDictionary *) fixupRecord: (NSDictionary *) _record +/** + * Set the timezone of the event start and end dates to the user's timezone. + * @param theRecord an dictionnary with the attributes of the event. + * @return a copy of theRecord with adjusted dates. + */ +- (NSMutableDictionary *) fixupRecord: (NSDictionary *) theRecord { - NSMutableDictionary *md; + NSMutableDictionary *record; static NSString *fields[] = { @"c_startdate", @"startDate", @"c_enddate", @"endDate" }; unsigned int count; NSCalendarDate *date; NSNumber *dateValue; - md = [[_record mutableCopy] autorelease]; + record = [[theRecord mutableCopy] autorelease]; for (count = 0; count < 2; count++) { - dateValue = [_record objectForKey: fields[count * 2]]; + dateValue = [theRecord objectForKey: fields[count * 2]]; if (dateValue) { date = [NSCalendarDate dateWithTimeIntervalSince1970: [dateValue unsignedIntValue]]; if (date) { [date setTimeZone: timeZone]; - [md setObject: date forKey: fields[count * 2 + 1]]; + [record setObject: date forKey: fields[count * 2 + 1]]; } } else [self logWithFormat: @"missing '%@' in record?", fields[count * 2]]; } - return md; + return record; } - (NSArray *) fixupRecords: (NSArray *) theRecords @@ -629,42 +635,52 @@ static NSNumber *sharedYes = nil; return ma; } -- (NSMutableDictionary *) fixupCycleRecord: (NSDictionary *) _record - cycleRange: (NGCalendarDateRange *) _r - firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir - forViewRange: (NGCalendarDateRange *) _viewRange +/** + * Adjust the timezone of the start and end dates to the user's timezone. + * The event is recurrent and the dates must first be adjusted with respect to + * the event's timezone. + * @param theRecord + * @param theCycle + * @param theFirstCycle + * @param theEventTimeZone + * @see fixupRecord: + * @return a copy of theRecord with adjusted dates. + */ +- (NSMutableDictionary *) fixupCycleRecord: (NSDictionary *) theRecord + cycleRange: (NGCalendarDateRange *) theCycle + firstInstanceCalendarDateRange: (NGCalendarDateRange *) theFirstCycle + withEventTimeZone: (iCalTimeZone *) theEventTimeZone { - NSMutableDictionary *md; + NSMutableDictionary *record; NSNumber *dateSecs; - id tmp; - - md = [[_record mutableCopy] autorelease]; + id date; + int secondsOffsetFromGMT; - /* cycle is in _r. We also have to override the c_startdate/c_enddate with the date values of - the reccurence since we use those when displaying events in SOGo Web */ + record = [[theRecord mutableCopy] autorelease]; - tmp = [_r startDate]; - [tmp setTimeZone: timeZone]; - [md setObject: tmp forKey: @"startDate"]; - dateSecs = [NSNumber numberWithInt: [tmp timeIntervalSince1970]]; - [md setObject: dateSecs forKey: @"c_startdate"]; + date = [theCycle startDate]; + secondsOffsetFromGMT = (int) [[theEventTimeZone periodForDate: date] secondsOffsetFromGMT]; + date = [date dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -secondsOffsetFromGMT]; + [date setTimeZone: timeZone]; + [record setObject: date forKey: @"startDate"]; + dateSecs = [NSNumber numberWithInt: [date timeIntervalSince1970]]; + [record setObject: dateSecs forKey: @"c_startdate"]; + [record setObject: dateSecs forKey: @"c_recurrence_id"]; - tmp = [_r endDate]; - [tmp setTimeZone: timeZone]; - [md setObject: tmp forKey: @"endDate"]; - dateSecs = [NSNumber numberWithInt: [tmp timeIntervalSince1970]]; - [md setObject: dateSecs forKey: @"c_enddate"]; - - tmp = [_r startDate]; - dateSecs = [NSNumber numberWithInt: [tmp timeIntervalSince1970]]; - [md setObject: dateSecs forKey: @"c_recurrence_id"]; + date = [theCycle endDate]; + secondsOffsetFromGMT = (int) [[theEventTimeZone periodForDate: date] secondsOffsetFromGMT]; + date = [date dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: -secondsOffsetFromGMT]; + [date setTimeZone: timeZone]; + [record setObject: date forKey: @"endDate"]; + dateSecs = [NSNumber numberWithInt: [date timeIntervalSince1970]]; + [record setObject: dateSecs forKey: @"c_enddate"]; // The first instance date is added to the dictionary so it can // be used by UIxCalListingActions to compute the DST offset. - tmp = [_fir startDate]; - [md setObject: tmp forKey: @"cycleStartDate"]; + date = [theFirstCycle startDate]; + [record setObject: date forKey: @"cycleStartDate"]; - return md; + return record; } - (int) _indexOfRecordMatchingDate: (NSCalendarDate *) matchDate @@ -805,26 +821,39 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir } } -- (void) _flattenCycleRecord: (NSDictionary *) _row - forRange: (NGCalendarDateRange *) _r - intoArray: (NSMutableArray *) _ma +/** + * Calculate and return the occurrences of the recurrent event for the given + * period. + * @param theRecord the event definition. + * @param theRange the period to look in. + * @param theRecords the array into which are copied the resulting occurrences. + */ +- (void) _flattenCycleRecord: (NSDictionary *) theRecord + forRange: (NGCalendarDateRange *) theRange + intoArray: (NSMutableArray *) theRecords { NSMutableDictionary *row, *fixedRow; - NSMutableArray *recordArray; - NSDictionary *cycleinfo; - NSCalendarDate *startDate, *endDate; - NGCalendarDateRange *fir, *rRange; - NSArray *rules, *exRules, *exDates, *ranges; - unsigned i, count; + NSMutableArray *records; + NSDictionary *cycleinfo; + NSCalendarDate *startDate, *endDate; + NGCalendarDateRange *firstRange, *oneRange; + NSArray *rules, *exRules, *exDates, *ranges; + NSArray *elements, *components; NSString *content; + iCalRepeatableEntityObject *component; + iCalDateTime *firstStartDate, *firstEndDate; + NSCalendarDate *checkStartDate, *checkEndDate; + iCalTimeZone *eventTimeZone; + unsigned i, count; - recordArray = [NSMutableArray array]; + records = [NSMutableArray array]; + ranges = nil; - content = [_row objectForKey: @"c_cycleinfo"]; + content = [theRecord objectForKey: @"c_cycleinfo"]; if (![content isNotNull]) { [self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@", - _row]; + theRecord]; return; } @@ -832,45 +861,72 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir if (!cycleinfo) { [self errorWithFormat:@"cyclic record doesn't have cycleinfo -> %@", - _row]; + theRecord]; return; } - row = [self fixupRecord: _row]; + row = [self fixupRecord: theRecord]; [row removeObjectForKey: @"c_cycleinfo"]; [row setObject: sharedYes forKey: @"isRecurrentEvent"]; startDate = [row objectForKey: @"startDate"]; endDate = [row objectForKey: @"endDate"]; - fir = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate - endDate: endDate]; - rules = [cycleinfo objectForKey: @"rules"]; - exRules = [cycleinfo objectForKey: @"exRules"]; - exDates = [cycleinfo objectForKey: @"exDates"]; - - ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: _r - firstInstanceCalendarDateRange: fir - recurrenceRules: rules - exceptionRules: exRules - exceptionDates: exDates]; + + content = [theRecord objectForKey: @"c_content"]; + if ([content length]) + { + elements = [iCalCalendar parseFromSource: content]; + if ([elements count]) + { + components = [[elements objectAtIndex: 0] events]; + if ([components count]) + { + // Retrieve the range of the first event + component = [components objectAtIndex: 0]; + firstStartDate = (iCalDateTime*)[component uniqueChildWithTag: @"dtstart"]; + firstEndDate = (iCalDateTime*)[component uniqueChildWithTag: @"dtend"]; + eventTimeZone = [firstStartDate timeZone]; + startDate = [eventTimeZone computedDateForDate: startDate]; + firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [[[firstStartDate values] lastObject] asCalendarDate] + endDate: [[[firstEndDate values] lastObject] asCalendarDate]]; + + // 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]]; + theRange = [NGCalendarDateRange calendarDateRangeWithStartDate: checkStartDate + endDate: checkEndDate]; + + // Calculate the occurrences for the given range + rules = [cycleinfo objectForKey: @"rules"]; + exRules = [cycleinfo objectForKey: @"exRules"]; + exDates = [cycleinfo objectForKey: @"exDates"]; + ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: theRange + firstInstanceCalendarDateRange: firstRange + recurrenceRules: rules + exceptionRules: exRules + exceptionDates: exDates]; + } + } + } + count = [ranges count]; for (i = 0; i < count; i++) { - rRange = [ranges objectAtIndex: i]; - fixedRow = [self fixupCycleRecord: row - cycleRange: rRange - firstInstanceCalendarDateRange: fir - forViewRange: _r]; + oneRange = [ranges objectAtIndex: i]; + fixedRow = [self fixupCycleRecord: row + cycleRange: oneRange + firstInstanceCalendarDateRange: firstRange + withEventTimeZone: eventTimeZone]; if (fixedRow) - [recordArray addObject: fixedRow]; + [records addObject: fixedRow]; } [self _appendCycleExceptionsFromRow: row - firstInstanceCalendarDateRange: fir - forRange: _r - toArray: recordArray]; + firstInstanceCalendarDateRange: firstRange + forRange: theRange + toArray: records]; - [_ma addObjectsFromArray: recordArray]; + [theRecords addObjectsFromArray: records]; } - (NSArray *) _flattenCycleRecords: (NSArray *) _records diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 64d398c5b..3583d0fd7 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -1,6 +1,6 @@ /* + Copyright (C) 2007-2011 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2007-2009 Inverse inc. This file is part of OpenGroupware.org. @@ -80,33 +80,28 @@ inContainer: self]; } -- (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) recID +/** + * Return a new exception in a the recurrent event. + * @param theRecurrenceID the ID of the occurence. + * @return a new occurence. + */ +- (iCalRepeatableEntityObject *) newOccurenceWithID: (NSString *) theRecurrenceID { iCalEvent *newOccurence, *master; NSCalendarDate *date, *firstDate; unsigned int interval, nbrDays; SOGoUserDefaults *ud; NSTimeZone *timeZone; - int daylightOffset; ud = [[SOGoUser userWithLogin: owner] userDefaults]; timeZone = [ud timeZone]; - newOccurence = (iCalEvent *) [super newOccurenceWithID: recID]; + newOccurence = (iCalEvent *) [super newOccurenceWithID: theRecurrenceID]; date = [newOccurence recurrenceId]; master = [self component: NO secure: NO]; firstDate = [master startDate]; - // We are creating a new exception in a recurrent event -- compute the daylight - // saving time with respect to the first occurrence of the recurrent event. - daylightOffset = (int) ([timeZone secondsFromGMTForDate: firstDate] - - [timeZone secondsFromGMTForDate: date]); - if (daylightOffset) - date = [date dateByAddingYears: 0 months: 0 days: 0 - hours:0 minutes: 0 seconds: daylightOffset]; - [date setTimeZone: timeZone]; - interval = [[master endDate] timeIntervalSinceDate: firstDate]; if ([newOccurence isAllDay]) diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index df1e1059b..2fc7a2021 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -1,8 +1,9 @@ /* iCalEvent+SOGo.m - this file is part of SOGo * - * Copyright (C) 2007 Inverse inc. + * Copyright (C) 2007-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Francis Lachapelle * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,6 +31,7 @@ #import #import +#import #import #import #import @@ -240,10 +242,22 @@ return row; } +/** + * Extract the start and end dates from the event, from which all recurrence + * calculations will be based on. + * @return the range of the first occurrence. + */ - (NGCalendarDateRange *) firstOccurenceRange { - return [NGCalendarDateRange calendarDateRangeWithStartDate: [self startDate] - endDate: [self endDate]]; + iCalDateTime *firstStartDate, *firstEndDate; + NGCalendarDateRange *firstRange; + + firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"]; + firstEndDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtend"]; + firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [[[firstStartDate values] lastObject] asCalendarDate] + endDate: [[[firstEndDate values] lastObject] asCalendarDate]]; + + return firstRange; } - (unsigned int) occurenceInterval diff --git a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m index cbdd8d9ab..70984bf36 100644 --- a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m @@ -1,6 +1,6 @@ -/* iCalRepeatableEntityObject+SOGo.m - this file is part of SOGo +/* + Copyright (C) 2008-2011 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2008 Inverse inc. This file is part of OpenGroupware.org. @@ -26,8 +26,11 @@ #import #import +#import #import #import +#import +#import #import #import "iCalRepeatableEntityObject+SOGo.h" @@ -104,25 +107,39 @@ return 0; } -- (BOOL) doesOccurOnDate: (NSCalendarDate *) occurenceDate +/** + * Checks if a date is part of the recurring entity. + * @param theOccurrenceDate the date to verify. + * @return true if the occurence date is part of the recurring entity. + */ +- (BOOL) doesOccurOnDate: (NSCalendarDate *) theOccurenceDate { NSArray *ranges; - NGCalendarDateRange *checkRange; - NSCalendarDate *endDate; + NGCalendarDateRange *checkRange, *firstRange; + NSCalendarDate *startDate, *endDate; + iCalDateTime *firstStartDate; BOOL doesOccur; doesOccur = [self isRecurrent]; if (doesOccur) { - endDate = [occurenceDate addTimeInterval: [self occurenceInterval]]; - checkRange = [NGCalendarDateRange calendarDateRangeWithStartDate: occurenceDate - endDate: endDate]; + // Retrieve the range of the first event + firstRange = [self firstOccurenceRange]; + + // Set the range to check with respect to the event timezone (extracted from the start date) + firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"]; + startDate = [[firstStartDate timeZone] computedDateForDate: theOccurenceDate]; + endDate = [startDate addTimeInterval: [self occurenceInterval]]; + checkRange = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate + endDate: endDate]; + + // Calculate the occurrences for the given date ranges = [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: checkRange - firstInstanceCalendarDateRange: [self firstOccurenceRange] - recurrenceRules: [self recurrenceRules] - exceptionRules: [self exceptionRules] - exceptionDates: [self exceptionDates]]; - doesOccur = [ranges dateRangeArrayContainsDate: occurenceDate]; + firstInstanceCalendarDateRange: firstRange + recurrenceRules: [self recurrenceRules] + exceptionRules: [self exceptionRules] + exceptionDates: [self exceptionDates]]; + doesOccur = [ranges dateRangeArrayContainsDate: startDate]; } return doesOccur; diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index 0a5ff8271..14387800c 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -1,8 +1,9 @@ /* UIxCalListingActions.m - this file is part of SOGo * - * Copyright (C) 2006-2010 Inverse inc. + * Copyright (C) 2006-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Francis Lachapelle * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,6 +35,8 @@ #import #import +#import +#import #import #import @@ -246,8 +249,12 @@ static NSArray *tasksFields = nil; forKey: @"c_title"]; } -/* TODO: shouldn't this be handled when creating the quick records ? */ -- (void) _fixDates: (NSMutableDictionary *) aRecord +/* + * Adjust the event start and end dates when there's a time change + * in the period covering the view for the user's timezone. + * @param theRecord the attributes of the event. + */ +- (void) _fixDates: (NSMutableDictionary *) theRecord { NSCalendarDate *aDate, *aStartDate; NSNumber *aDateValue; @@ -272,41 +279,25 @@ static NSArray *tasksFields = nil; http://www.sogo.nu/bugs/view.php?id=678 ... */ - if (dayBasedView || [[aRecord objectForKey: @"c_isallday"] boolValue]) + + if (dayBasedView || [[theRecord objectForKey: @"c_isallday"] boolValue]) { for (count = 0; count < 2; count++) { aDateField = fields[count * 2]; - aDate = [aRecord objectForKey: aDateField]; - daylightOffset = (int) ([userTimeZone secondsFromGMTForDate: aDate] - - [userTimeZone secondsFromGMTForDate: startDate]); + aDate = [theRecord objectForKey: aDateField]; + daylightOffset = (int) ([userTimeZone secondsFromGMTForDate: aDate] + - [userTimeZone secondsFromGMTForDate: startDate]); if (daylightOffset) { aDate = [aDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: daylightOffset]; - [aRecord setObject: aDate forKey: aDateField]; + [theRecord setObject: aDate forKey: aDateField]; aDateValue = [NSNumber numberWithInt: [aDate timeIntervalSince1970]]; - [aRecord setObject: aDateValue forKey: fields[count * 2 + 1]]; + [theRecord setObject: aDateValue forKey: fields[count * 2 + 1]]; } } } - - aDateValue = [aRecord objectForKey: @"c_recurrence_id"]; - aDate = [aRecord objectForKey: @"cycleStartDate"]; - if (aDateValue && aDate) - { - aStartDate = [aRecord objectForKey: @"startDate"]; - if ([userTimeZone isDaylightSavingTimeForDate: aStartDate] != - [userTimeZone isDaylightSavingTimeForDate: aDate]) - { - // For the event's recurrence id, compute the daylight saving time - // offset with respect to the first occurrence of the recurring event. - daylightOffset = (signed int)[userTimeZone secondsFromGMTForDate: aStartDate] - - (signed int)[userTimeZone secondsFromGMTForDate: aDate]; - aDateValue = [NSNumber numberWithInt: [aDateValue intValue] + daylightOffset]; - [aRecord setObject: aDateValue forKey: @"c_recurrence_id"]; - } - } } - (NSArray *) _fetchFields: (NSArray *) fields @@ -341,7 +332,6 @@ static NSArray *tasksFields = nil; component: component] objectEnumerator]; owner = [currentFolder ownerInContext: context]; ownerUser = [SOGoUser userWithLogin: owner]; - /* TODO: this should be handled per-folder rather than per-event. */ isErasable = ([owner isEqualToString: userLogin] || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]); while ((newInfo = [currentInfos nextObject])) @@ -349,10 +339,12 @@ static NSArray *tasksFields = nil; if ([fields containsObject: @"editable"]) { if (folderIsRemote) + // .ics subscriptions are not editable [newInfo setObject: [NSNumber numberWithInt: 0] forKey: @"editable"]; else { + // Identifies whether the active user can edit the event. role = [currentFolder roleForComponentsWithAccessClass: [[newInfo objectForKey: @"c_classification"] intValue]