From 361462563ec750de2031393dc7ae8f28614185e2 Mon Sep 17 00:00:00 2001 From: znek Date: Tue, 1 Mar 2005 16:59:09 +0000 Subject: [PATCH] improved API, bugfix for recurrent dates git-svn-id: http://svn.opengroupware.org/SOPE/trunk@603 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- sope-ical/NGiCal/ChangeLog | 28 ++++++ sope-ical/NGiCal/GNUmakefile | 2 + sope-ical/NGiCal/NGiCal.h | 2 + sope-ical/NGiCal/NGiCal.xcode/project.pbxproj | 14 ++- sope-ical/NGiCal/NSCalendarDate+ICal.h | 10 ++- sope-ical/NGiCal/NSCalendarDate+ICal.m | 18 +++- sope-ical/NGiCal/Version | 2 +- sope-ical/NGiCal/iCalDateHolder.h | 13 ++- sope-ical/NGiCal/iCalRecurrenceCalculator.h | 6 ++ sope-ical/NGiCal/iCalRecurrenceCalculator.m | 86 +++++++++++++++++++ sope-ical/NGiCal/iCalRecurrenceRule.m | 12 +-- sope-ical/NGiCal/iCalRepeatableEntityObject.m | 57 ++---------- 12 files changed, 178 insertions(+), 72 deletions(-) diff --git a/sope-ical/NGiCal/ChangeLog b/sope-ical/NGiCal/ChangeLog index 8eda6a0f..7409c5ec 100644 --- a/sope-ical/NGiCal/ChangeLog +++ b/sope-ical/NGiCal/ChangeLog @@ -1,3 +1,31 @@ +2005-02-28 Marcus Mueller + + * v4.5.47 + + * iCalRepeatableEntityObject.m: shifted code to + iCalRecurrenceCalculator + + * iCalRecurrenceCalculator.[hm]: new class method to calculate + complex recurrences. In SOGo this information is stored in quick + fields to reduce the complexity of lookups, hence we need to offer + a proper API to calculate date ranges from this information. + Fixed all calculations to explicitly set timeZone for all newly + created startDates - it seems the timeZone information is not + properly retained by the hour:minute:second: method from NGExtensions + which lead to improper DST related shifts. + + * iCalDateHolder.h: exposed the API + + * NSCalendarDate+ICal.[hm]: new convenience constructor for calendar + dates from iCal representations (uses iCalDateHolder internally) + + * iCalRecurrenceRule.m: changed setUntil: to utilize new public + NSCalendarDate+ICal category + + * NGiCal.h: added NSCalendarDate+ICal.h to the public headers + + * GNUmakefile: NSCalendarDate+ICal.h is public now + 2005-02-20 Helge Hess * NGiCal.xmap: fixed a missing semicolon (did not load on MacOSX) diff --git a/sope-ical/NGiCal/GNUmakefile b/sope-ical/NGiCal/GNUmakefile index 68633a8f..403b9863 100644 --- a/sope-ical/NGiCal/GNUmakefile +++ b/sope-ical/NGiCal/GNUmakefile @@ -31,6 +31,8 @@ libNGiCal_HEADER_FILES = \ iCalRenderer.h \ iCalRecurrenceRule.h \ iCalRecurrenceCalculator.h \ + \ + NSCalendarDate+ICal.h \ # IcalResponse.h \ diff --git a/sope-ical/NGiCal/NGiCal.h b/sope-ical/NGiCal/NGiCal.h index 55728e65..a7ab997c 100644 --- a/sope-ical/NGiCal/NGiCal.h +++ b/sope-ical/NGiCal/NGiCal.h @@ -41,4 +41,6 @@ #include #include +#include + #endif /* __NGiCal_H__ */ diff --git a/sope-ical/NGiCal/NGiCal.xcode/project.pbxproj b/sope-ical/NGiCal/NGiCal.xcode/project.pbxproj index 260e7a10..7bedbf0d 100644 --- a/sope-ical/NGiCal/NGiCal.xcode/project.pbxproj +++ b/sope-ical/NGiCal/NGiCal.xcode/project.pbxproj @@ -27,6 +27,9 @@ fileRef = AD770E6707AE627500F5C7A1; isa = PBXBuildFile; settings = { + ATTRIBUTES = ( + Public, + ); }; }; AD770E6A07AE627500F5C7A1 = { @@ -55,6 +58,9 @@ fileRef = AD77103C07AE8F8500F5C7A1; isa = PBXBuildFile; settings = { + ATTRIBUTES = ( + Public, + ); }; }; AD77103F07AE8F8500F5C7A1 = { @@ -97,6 +103,9 @@ fileRef = ADAACE6607B3973900FC48D6; isa = PBXBuildFile; settings = { + ATTRIBUTES = ( + Public, + ); }; }; ADAACE6907B3973900FC48D6 = { @@ -164,7 +173,7 @@ ); buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 4.5.44; + DYLIB_CURRENT_VERSION = 4.5.47; FRAMEWORK_SEARCH_PATHS = "\"$(USER_LIBRARY_DIR)/EmbeddedFrameworks\""; FRAMEWORK_VERSION = A; GCC_PRECOMPILE_PREFIX_HEADER = NO; @@ -578,7 +587,7 @@ ); buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 4.5.44; + DYLIB_CURRENT_VERSION = 4.5.47; FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks"; FRAMEWORK_VERSION = A; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -1321,6 +1330,7 @@ isa = PBXBuildFile; settings = { ATTRIBUTES = ( + Public, ); }; }; diff --git a/sope-ical/NGiCal/NSCalendarDate+ICal.h b/sope-ical/NGiCal/NSCalendarDate+ICal.h index 2af61b3f..1f10eac2 100644 --- a/sope-ical/NGiCal/NSCalendarDate+ICal.h +++ b/sope-ical/NGiCal/NSCalendarDate+ICal.h @@ -19,14 +19,16 @@ 02111-1307, USA. */ -#ifndef __ICal2_NSCalendarDate_ICal_H__ -#define __ICal2_NSCalendarDate_ICal_H__ +#ifndef __NGiCal_NSCalendarDate_ICal_H__ +#define __NGiCal_NSCalendarDate_ICal_H__ #import @class NSTimeZone; -@interface NSCalendarDate(ICalValue) +@interface NSCalendarDate(iCalRepresentation) + ++ (id)calendarDateWithICalRepresentation:(NSString *)_iCalRep; /* represention */ @@ -41,4 +43,4 @@ - (unsigned)daysBetweenDate:(NSCalendarDate *)_date; @end -#endif /* __ICal2_NSCalendarDate_ICal_H__ */ +#endif /* __NGiCal_NSCalendarDate_ICal_H__ */ diff --git a/sope-ical/NGiCal/NSCalendarDate+ICal.m b/sope-ical/NGiCal/NSCalendarDate+ICal.m index 7a51986c..dd5b70a9 100644 --- a/sope-ical/NGiCal/NSCalendarDate+ICal.m +++ b/sope-ical/NGiCal/NSCalendarDate+ICal.m @@ -20,6 +20,7 @@ */ #include "NSCalendarDate+ICal.h" +#include "iCalDateHolder.h" #include "common.h" static NSTimeZone *gmt = nil; @@ -28,12 +29,27 @@ static inline void _setupGMT(void) { gmt = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain]; } -@implementation NSCalendarDate(ICalValue) +@interface iCalDateHolder (PrivateAPI) +- (id)awakeAfterUsingSaxDecoder:(id)_decoder; +@end + +@implementation NSCalendarDate(iCalRepresentation) /* represention */ static NSString *gmtcalfmt = @"%Y%m%dT%H%M00Z"; ++ (id)calendarDateWithICalRepresentation:(NSString *)_iCalRep { + iCalDateHolder *dh; + NSCalendarDate *date; + + dh = [[iCalDateHolder alloc] init]; + [dh setString:_iCalRep]; + date = [dh awakeAfterUsingSaxDecoder:nil]; + [dh release]; + return date; +} + - (NSString *)icalStringInGMT { NSTimeZone *oldtz; NSString *s; diff --git a/sope-ical/NGiCal/Version b/sope-ical/NGiCal/Version index bca2a88b..e3250f18 100644 --- a/sope-ical/NGiCal/Version +++ b/sope-ical/NGiCal/Version @@ -2,7 +2,7 @@ MAJOR_VERSION=4 MINOR_VERSION=5 -SUBMINOR_VERSION:=46 +SUBMINOR_VERSION:=47 # v4.5.40 requires NGExtensions v4.5.145 # v4.5.37 requires NGExtensions v4.5.140 diff --git a/sope-ical/NGiCal/iCalDateHolder.h b/sope-ical/NGiCal/iCalDateHolder.h index 12927894..fa620273 100644 --- a/sope-ical/NGiCal/iCalDateHolder.h +++ b/sope-ical/NGiCal/iCalDateHolder.h @@ -24,7 +24,7 @@ #import -@class NSString; +@class NSString, NSTimeZone; @interface iCalDateHolder : NSObject { @@ -33,6 +33,17 @@ NSString *tag; } +- (void)setString:(NSString *)_value; +- (NSString *)string; + +- (void)setTag:(NSString *)_value; +- (NSString *)tag; + +- (void)setTzid:(NSString *)_value; +- (NSString *)tzid; + +- (NSTimeZone *)timeZone; + @end #endif /* __NGiCal_iCalDateHolder_H__ */ diff --git a/sope-ical/NGiCal/iCalRecurrenceCalculator.h b/sope-ical/NGiCal/iCalRecurrenceCalculator.h index 661e91b6..007a2ed8 100644 --- a/sope-ical/NGiCal/iCalRecurrenceCalculator.h +++ b/sope-ical/NGiCal/iCalRecurrenceCalculator.h @@ -40,6 +40,12 @@ iCalRecurrenceRule *rrule; } ++ (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r + firstInstanceCalendarDateRange:(NGCalendarDateRange *)_fir + recurrenceRules:(NSArray *)_rRules + exceptionRules:(NSArray *)_exRules + exceptionDates:(NSArray *)_exDates; + + (id)recurrenceCalculatorForRecurrenceRule:(iCalRecurrenceRule *)_rrule withFirstInstanceCalendarDateRange:(NGCalendarDateRange *)_range; diff --git a/sope-ical/NGiCal/iCalRecurrenceCalculator.m b/sope-ical/NGiCal/iCalRecurrenceCalculator.m index 2931f5a6..d51e845a 100644 --- a/sope-ical/NGiCal/iCalRecurrenceCalculator.m +++ b/sope-ical/NGiCal/iCalRecurrenceCalculator.m @@ -61,6 +61,8 @@ @implementation iCalRecurrenceCalculator +static Class NSCalendarDateClass = Nil; +static Class iCalRecurrenceRuleClass = Nil; static Class dailyCalcClass = Nil; static Class weeklyCalcClass = Nil; static Class monthlyCalcClass = Nil; @@ -72,12 +74,17 @@ static Class yearlyCalcClass = Nil; if (didInit) return; didInit = YES; + NSCalendarDateClass = [NSCalendarDate class]; + iCalRecurrenceRuleClass = [iCalRecurrenceRule class]; + dailyCalcClass = [iCalDailyRecurrenceCalculator class]; weeklyCalcClass = [iCalWeeklyRecurrenceCalculator class]; monthlyCalcClass = [iCalMonthlyRecurrenceCalculator class]; yearlyCalcClass = [iCalYearlyRecurrenceCalculator class]; } +/* factory */ + + (id)recurrenceCalculatorForRecurrenceRule:(iCalRecurrenceRule *)_rrule withFirstInstanceCalendarDateRange:(NGCalendarDateRange *)_range { @@ -85,6 +92,80 @@ static Class yearlyCalcClass = Nil; firstInstanceCalendarDateRange:_range] autorelease]; } +/* complex calculation convenience */ + ++ (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r + firstInstanceCalendarDateRange:(NGCalendarDateRange *)_fir + recurrenceRules:(NSArray *)_rRules + exceptionRules:(NSArray *)_exRules + exceptionDates:(NSArray *)_exDates +{ + id rule; + iCalRecurrenceCalculator *calc; + NSMutableArray *ranges; + unsigned i, count, rCount; + + ranges = [NSMutableArray array]; + count = [_rRules count]; + for (i = 0; i < count; i++) { + NSArray *rs; + + rule = [_rRules objectAtIndex:i]; + if (![rule isKindOfClass:iCalRecurrenceRuleClass]) + rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule]; + + calc = [self recurrenceCalculatorForRecurrenceRule:rule + withFirstInstanceCalendarDateRange:_fir]; + rs = [calc recurrenceRangesWithinCalendarDateRange:_r]; + [ranges addObjectsFromArray:rs]; + } + + if (![ranges count]) + return nil; + + /* test if any exceptions do match */ + count = [_exRules count]; + for (i = 0; i < count; i++) { + NSArray *rs; + + rule = [_exRules objectAtIndex:i]; + if (![rule isKindOfClass:iCalRecurrenceRuleClass]) + rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule]; + + calc = [self recurrenceCalculatorForRecurrenceRule:rule + withFirstInstanceCalendarDateRange:_fir]; + rs = [calc recurrenceRangesWithinCalendarDateRange:_r]; + [ranges removeObjectsInArray:rs]; + } + + if (![ranges count]) + return nil; + + /* exception dates are also possible */ + rCount = [ranges count]; + count = [_exDates count]; + for (i = 0; i < count; i++) { + id exDate; + NGCalendarDateRange *r; + unsigned k; + + exDate = [_exDates objectAtIndex:i]; + if (![exDate isKindOfClass:NSCalendarDateClass]) { + exDate = [NSCalendarDate calendarDateWithICalRepresentation:exDate]; + } + for (k = 0; k < rCount; k++) { + r = [ranges objectAtIndex:(rCount - k) - 1]; + if ([r containsDate:exDate]) { + [ranges removeObjectAtIndex:k]; + } + } + } + return ranges; +} + + +/* init */ + - (id)initWithRecurrenceRule:(iCalRecurrenceRule *)_rrule firstInstanceCalendarDateRange:(NGCalendarDateRange *)_range { @@ -252,6 +333,7 @@ static Class yearlyCalcClass = Nil; NGCalendarDateRange *r; start = [NSCalendarDate dateForJulianNumber:jnCurrent]; + [start setTimeZone:[firStart timeZone]]; start = [start hour: [firStart hourOfDay] minute:[firStart minuteOfHour] second:[firStart secondOfMinute]]; @@ -348,6 +430,7 @@ static Class yearlyCalcClass = Nil; NGCalendarDateRange *r; start = [NSCalendarDate dateForJulianNumber:jnCurrent]; + [start setTimeZone:[firStart timeZone]]; start = [start hour: [firStart hourOfDay] minute:[firStart minuteOfHour] second:[firStart secondOfMinute]]; @@ -395,6 +478,7 @@ static Class yearlyCalcClass = Nil; NGCalendarDateRange *r; start = [NSCalendarDate dateForJulianNumber:jnCurrent]; + [start setTimeZone:[firStart timeZone]]; start = [start hour: [firStart hourOfDay] minute:[firStart minuteOfHour] second:[firStart secondOfMinute]]; @@ -465,6 +549,7 @@ static Class yearlyCalcClass = Nil; start = [firStart dateByAddingYears:0 months:diff + i days:0]; + [start setTimeZone:[firStart timeZone]]; end = [start addTimeInterval:[self->firstRange duration]]; r = [NGCalendarDateRange calendarDateRangeWithStartDate:start endDate:end]; @@ -525,6 +610,7 @@ static Class yearlyCalcClass = Nil; start = [firStart dateByAddingYears:diff + i months:0 days:0]; + [start setTimeZone:[firStart timeZone]]; end = [start addTimeInterval:[self->firstRange duration]]; r = [NGCalendarDateRange calendarDateRangeWithStartDate:start endDate:end]; diff --git a/sope-ical/NGiCal/iCalRecurrenceRule.m b/sope-ical/NGiCal/iCalRecurrenceRule.m index 7c09525f..a502ed9d 100644 --- a/sope-ical/NGiCal/iCalRecurrenceRule.m +++ b/sope-ical/NGiCal/iCalRecurrenceRule.m @@ -20,7 +20,6 @@ */ #include "iCalRecurrenceRule.h" -#include "iCalDateHolder.h" #include "NSCalendarDate+ICal.h" #include "common.h" @@ -41,11 +40,6 @@ wkst = rrWeekStart; */ -@interface iCalDateHolder (PrivateAPI) -- (void)setString:(NSString *)_value; -- (id)awakeAfterUsingSaxDecoder:(id)_decoder; -@end - @interface iCalRecurrenceRule (PrivateAPI) - (iCalWeekDay)weekDayFromICalRepresentation:(NSString *)_day; - (NSString *)iCalRepresentationForWeekDay:(iCalWeekDay)_weekDay; @@ -305,14 +299,10 @@ self->repeatCount = [_count unsignedIntValue]; } - (void)setUntil:(NSString *)_until { - iCalDateHolder *dh; NSCalendarDate *date; - dh = [[iCalDateHolder alloc] init]; - [dh setString:_until]; - date = [dh awakeAfterUsingSaxDecoder:nil]; + date = [NSCalendarDate calendarDateWithICalRepresentation:_until]; ASSIGN(self->untilDate, date); - [dh release]; } - (void)setWkst:(NSString *)_weekStart { diff --git a/sope-ical/NGiCal/iCalRepeatableEntityObject.m b/sope-ical/NGiCal/iCalRepeatableEntityObject.m index 48ab00cc..6d9a0ea8 100644 --- a/sope-ical/NGiCal/iCalRepeatableEntityObject.m +++ b/sope-ical/NGiCal/iCalRepeatableEntityObject.m @@ -123,58 +123,11 @@ - (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r firstInstanceCalendarDateRange:(NGCalendarDateRange *)_fir { - iCalRecurrenceRule *rule; - iCalRecurrenceCalculator *calc; - NSMutableArray *ranges; - unsigned i, count, rCount; - - ranges = [NSMutableArray array]; - count = [self->rRules count]; - for (i = 0; i < count; i++) { - NSArray *rs; - - rule = [self->rRules objectAtIndex:i]; - calc = [iCalRecurrenceCalculator recurrenceCalculatorForRecurrenceRule:rule - withFirstInstanceCalendarDateRange:_fir]; - rs = [calc recurrenceRangesWithinCalendarDateRange:_r]; - [ranges addObjectsFromArray:rs]; - } - - if (![ranges count]) - return nil; - - /* test if any exceptions do match */ - count = [self->exRules count]; - for (i = 0; i < count; i++) { - NSArray *rs; - - rule = [self->exRules objectAtIndex:i]; - calc = [iCalRecurrenceCalculator recurrenceCalculatorForRecurrenceRule:rule - withFirstInstanceCalendarDateRange:_fir]; - rs = [calc recurrenceRangesWithinCalendarDateRange:_r]; - [ranges removeObjectsInArray:rs]; - } - - if (![ranges count]) - return nil; - - /* exception dates are also possible */ - rCount = [ranges count]; - count = [self->exDates count]; - for (i = 0; i < count; i++) { - NSCalendarDate *exDate; - NGCalendarDateRange *r; - unsigned k; - - exDate = [self->exDates objectAtIndex:i]; - for (k = 0; k < rCount; k++) { - r = [ranges objectAtIndex:(rCount - k) - 1]; - if ([r containsDate:exDate]) { - [ranges removeObjectAtIndex:k]; - } - } - } - return ranges; + return [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange:_r + firstInstanceCalendarDateRange:_fir + recurrenceRules:self->rRules + exceptionRules:self->exRules + exceptionDates:self->exDates]; } -- 2.39.5