From: helge Date: Wed, 21 Sep 2005 14:03:18 +0000 (+0000) Subject: work on recurrences X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c9c941b66b24b243f36bd2a3d537f584274cd969;p=sope work on recurrences git-svn-id: http://svn.opengroupware.org/SOPE/trunk@1126 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- diff --git a/sope-ical/NGiCal/ChangeLog b/sope-ical/NGiCal/ChangeLog index 95123ffc..f19df928 100644 --- a/sope-ical/NGiCal/ChangeLog +++ b/sope-ical/NGiCal/ChangeLog @@ -1,5 +1,13 @@ 2005-09-21 Helge Hess + * v4.5.66 + + * iCalRecurrenceRule.m: temporarily expose byDayOccurence1 until the + API is fixed + + * iCalMonthlyRecurrenceCalculator.m: prepared for byday/bymonthday + rule specs + * v4.5.65 * iCalMonthlyRecurrenceCalculator.m: fixed calculation of 'count' field diff --git a/sope-ical/NGiCal/Version b/sope-ical/NGiCal/Version index 3e9a0986..59e5bbe0 100644 --- a/sope-ical/NGiCal/Version +++ b/sope-ical/NGiCal/Version @@ -2,7 +2,7 @@ MAJOR_VERSION=4 MINOR_VERSION=5 -SUBMINOR_VERSION:=65 +SUBMINOR_VERSION:=66 # v4.5.40 requires NGExtensions v4.5.145 # v4.5.37 requires NGExtensions v4.5.140 diff --git a/sope-ical/NGiCal/iCalMonthlyRecurrenceCalculator.m b/sope-ical/NGiCal/iCalMonthlyRecurrenceCalculator.m index 7f04abb0..a9045ab3 100644 --- a/sope-ical/NGiCal/iCalMonthlyRecurrenceCalculator.m +++ b/sope-ical/NGiCal/iCalMonthlyRecurrenceCalculator.m @@ -35,16 +35,86 @@ @implementation iCalMonthlyRecurrenceCalculator +typedef BOOL NGMonthSet[12]; +typedef BOOL NGMonthDaySet[31]; + +static void NGMonthDaySet_clear(NGMonthDaySet *daySet) { + register unsigned i; + + for (i = 0; i < 31; i++) + (*daySet)[i] = NO; +} + +static void NGMonthDaySet_copyOrUnion(NGMonthDaySet *base, NGMonthDaySet *new, + BOOL doCopy) +{ + register unsigned i; + + if (doCopy) + memcpy(base, new, sizeof(NGMonthDaySet)); + else { + for (i = 0; i < 31; i++) { + if (!(*new)[i]) + (*base)[i] = NO; + } + } +} + +static void NGMonthDaySet_fillWithByMonthDay(NGMonthDaySet *daySet, + NSArray *byMonthDay, + int numDaysInMonth) +{ + /* list of days in the month */ + unsigned i, count; + + NGMonthDaySet_clear(daySet); + + for (i = 0, count = [byMonthDay count]; i < count; i++) { + int dayInMonth; /* -31..-1 and 1..31 */ + + if ((dayInMonth = [[byMonthDay objectAtIndex:i] intValue]) == 0) + continue; /* invalid value */ + if (dayInMonth > numDaysInMonth) + continue; /* this month has less days */ + if (dayInMonth < -numDaysInMonth) + continue; /* this month has less days */ + + /* adjust negative days */ + + if (dayInMonth < 0) { + /* eg: -1 == last day in month, 30 days => 30 */ + dayInMonth = 32 - dayInMonth /* because we count from 1 */; + } + + (*daySet)[dayInMonth] = YES; + } +} + +static void NGMonthDaySet_fillWithByDayX(NGMonthDaySet *daySet, + unsigned dayMask, + int occurrence1) +{ + unsigned i, count; + + NGMonthDaySet_clear(daySet); + // TODO: complete me +} + - (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r{ /* main entry */ NSMutableArray *ranges; NSTimeZone *timeZone; - NSCalendarDate *firStart, *rStart, *rEnd, *until; - unsigned i, count, interval; + NSCalendarDate *eventStartDate, *rStart, *rEnd, *until; + unsigned monthIdxInRange, numberOfMonthsInRange, interval; int diff; - - firStart = [self->firstRange startDate]; - timeZone = [firStart timeZone]; + NGMonthSet byMonthList = { // TODO: fill from rrule, this is the default + YES, YES, YES, YES, YES, YES, + YES, YES, YES, YES, YES, YES + }; + NSArray *byMonthDay = nil; // array of ints (-31..-1 and 1..31) + + eventStartDate = [self->firstRange startDate]; + timeZone = [eventStartDate timeZone]; rStart = [_r startDate]; rEnd = [_r endDate]; interval = [self->rrule repeatInterval]; @@ -65,30 +135,88 @@ rEnd = until; // TODO: why is that? end is _before_ until? } - // TODO: I think the diff is to skip recurrence which are before the + // TODO: I think the 'diff' is to skip recurrence which are before the // requested range. Not sure whether this is actually possible, eg // the repeatCount must be processed from the start. - diff = [firStart monthsBetweenDate:rStart]; - if ((diff != 0) && [rStart compare:firStart] == NSOrderedAscending) + diff = [eventStartDate monthsBetweenDate:rStart]; + if ((diff != 0) && [rStart compare:eventStartDate] == NSOrderedAscending) diff = -diff; - count = [rStart monthsBetweenDate:rEnd] + 1; - ranges = [NSMutableArray arrayWithCapacity:count]; - for (i = 0 ; i < count; i++) { + numberOfMonthsInRange = [rStart monthsBetweenDate:rEnd] + 1; + ranges = [NSMutableArray arrayWithCapacity:numberOfMonthsInRange]; + + /* + Note: we do not add 'eventStartDate', this is intentional, the event date + itself is _not_ necessarily part of the sequence, eg with monthly + byday recurrences. + */ + + for (monthIdxInRange = 0; monthIdxInRange < numberOfMonthsInRange; + monthIdxInRange++) { + NSCalendarDate *cursor; NSCalendarDate *start, *end; NGCalendarDateRange *r; - int test; + unsigned numDaysInMonth; + int monthIdxInRecurrence; + NGMonthDaySet monthDays; + BOOL didByFill; - test = diff + i; + monthIdxInRecurrence = diff + monthIdxInRange; - if (test < 0) + if (monthIdxInRecurrence < 0) continue; - if ((test % interval) != 0) + /* first check whether we are in the interval */ + + if ((monthIdxInRecurrence % interval) != 0) continue; + + /* + Then the sequence is: + - check whether the month is in the BYMONTH list + */ + + cursor = [eventStartDate dateByAddingYears:0 + months:(diff + monthIdxInRange) + days:0]; + [cursor setTimeZone:timeZone]; + numDaysInMonth = [cursor numberOfDaysInMonth]; + + + /* check whether we match the bymonth specification */ + + if (!byMonthList[[cursor monthOfYear] - 1]) + continue; + + + /* check 'day level' byXYZ rules */ - start = [firStart dateByAddingYears:0 months:(diff + i) days:0]; - [start setTimeZone:timeZone]; + didByFill = NO; + + if (byMonthDay != nil) { /* list of days in the month */ + NGMonthDaySet ruleset; + + NGMonthDaySet_fillWithByMonthDay(&ruleset, byMonthDay, numDaysInMonth); + NGMonthDaySet_copyOrUnion(&monthDays, &ruleset, !didByFill); + didByFill = YES; + } + + if ([self->rrule byDayMask] != 0) { // TODO: replace the mask with an array + NGMonthDaySet ruleset; + + NGMonthDaySet_fillWithByDayX(&ruleset, + [self->rrule byDayMask], + [self->rrule byDayOccurence1]); + NGMonthDaySet_copyOrUnion(&monthDays, &ruleset, !didByFill); + didByFill = YES; + } + + + // TODO: complete byday support + + /* set start date */ + + start = cursor; /* check whether we are still in the limits */ @@ -122,10 +250,14 @@ unsigned months, interval; interval = [self->rrule repeatInterval]; - months = ([self->rrule repeatCount] - 1) * interval; - until = [[self->firstRange startDate] dateByAddingYears:0 - months:months - days:0]; + months = [self->rrule repeatCount] - 1 /* the first counts as one! */; + + if (interval > 0) + months *= interval; + + until = [[self->firstRange startDate] dateByAddingYears:0 + months:months + days:0]; return until; } return [super lastInstanceStartDate]; diff --git a/sope-ical/NGiCal/iCalRecurrenceRule.h b/sope-ical/NGiCal/iCalRecurrenceRule.h index 45d42b75..a36d6753 100644 --- a/sope-ical/NGiCal/iCalRecurrenceRule.h +++ b/sope-ical/NGiCal/iCalRecurrenceRule.h @@ -90,6 +90,7 @@ typedef enum { - (void)setByDayMask:(unsigned)_mask; - (unsigned)byDayMask; +- (int)byDayOccurence1; /* count and untilDate are mutually exclusive */ diff --git a/sope-ical/NGiCal/iCalRecurrenceRule.m b/sope-ical/NGiCal/iCalRecurrenceRule.m index 3a32075c..f437738a 100644 --- a/sope-ical/NGiCal/iCalRecurrenceRule.m +++ b/sope-ical/NGiCal/iCalRecurrenceRule.m @@ -129,6 +129,9 @@ - (unsigned)byDayMask { return self->byDay.mask; } +- (int)byDayOccurence1 { + return self->byDayOccurence1; +} - (BOOL)isInfinite { return (self->repeatCount != 0 || self->untilDate) ? NO : YES;