#include "iCalRecurrenceCalculator.h"
#include <NGExtensions/NGCalendarDateRange.h>
#include "iCalRecurrenceRule.h"
+#include "NSCalendarDate+ICal.h"
#include "common.h"
-
/* class cluster */
@interface iCalDailyRecurrenceCalculator : iCalRecurrenceCalculator
}
@end
-/* superclass */
+/* Private */
+
+@interface iCalRecurrenceCalculator (PrivateAPI)
+- (NSCalendarDate *)lastInstanceStartDate;
+@end
@implementation iCalRecurrenceCalculator
return nil; /* subclass responsibility */
}
- (BOOL)doesRecurrWithinCalendarDateRange:(NGCalendarDateRange *)_range {
- return NO; /* subclass responsibility */
+ NSArray *ranges;
+
+ ranges = [self recurrenceRangesWithinCalendarDateRange:_range];
+ return (ranges == nil || [ranges count] == 0) ? NO : YES;
}
-@end /* iCalRecurrenceCalculator */
+- (NGCalendarDateRange *)firstInstanceCalendarDateRange {
+ return self->firstRange;
+}
+- (NGCalendarDateRange *)lastInstanceCalendarDateRange {
+ NSCalendarDate *start, *end;
-/* ZNeK: NOTE
-taeglich:
-alle events
-fuer all diese jeweils
-differenz der julian numbers durch interval teilen, wenn ok, ...
-*/
-@implementation iCalDailyRecurrenceCalculator
+ start = [self lastInstanceStartDate];
+ if (!start)
+ return nil;
+ end = [start addTimeInterval:[self->firstRange duration]];
+ return [NGCalendarDateRange calendarDateRangeWithStartDate:start
+ endDate:end];
+}
-- (unsigned)factor {
- return 1;
+- (NSCalendarDate *)lastInstanceStartDate {
+ NSCalendarDate *until;
+
+ /* NOTE: this is horribly inaccurate and doesn't even consider the use
+ of repeatCount. It MUST be implemented by subclasses properly! However,
+ it does the trick for SOGO 1.0 - that's why it's left here.
+ */
+ if ((until = [self->rrule untilDate]) != nil)
+ return until;
+ return nil;
}
-- (BOOL)doesRecurrWithinCalendarDateRange:(NGCalendarDateRange *)_range {
- long i, jnFirst, jnStart, jnEnd, startEndCount;
- unsigned interval;
+@end /* iCalRecurrenceCalculator */
- jnFirst = [[self->firstRange startDate] julianNumber];
- jnEnd = [[_range endDate] julianNumber];
- if (jnFirst > jnEnd)
- return NO;
+@implementation iCalDailyRecurrenceCalculator
- jnStart = [[_range startDate] julianNumber];
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r {
+ NSMutableArray *ranges;
+ NSCalendarDate *firStart;
+ long i, jnFirst, jnStart, jnEnd, startEndCount;
+ unsigned interval;
+
+ firStart = [self->firstRange startDate];
+ jnFirst = [firStart julianNumber];
+ jnEnd = [[_r endDate] julianNumber];
+
+ if (jnFirst > jnEnd)
+ return nil;
+
+ jnStart = [[_r startDate] julianNumber];
interval = [self->rrule repeatInterval];
-
+
/* if rule is bound, check the bounds */
if (![self->rrule isInfinite]) {
NSCalendarDate *until;
long jnRuleLast;
-
+
until = [self->rrule untilDate];
if (until) {
- if ([until compare:[_range startDate]] == NSOrderedAscending)
- return NO;
+ if ([until compare:[_r startDate]] == NSOrderedAscending)
+ return nil;
jnRuleLast = [until julianNumber];
}
else {
jnRuleLast = (interval * [self->rrule repeatCount] * [self factor])
- + jnFirst;
+ + jnFirst;
if (jnRuleLast < jnStart)
- return NO;
+ return nil;
}
/* jnStart < jnRuleLast < jnEnd ? */
if (jnEnd > jnRuleLast)
jnEnd = jnRuleLast;
}
+ ranges = [NSMutableArray arrayWithCapacity:5];
startEndCount = (jnEnd - jnStart) + 1;
for (i = 0 ; i < startEndCount; i++) {
long jnTest;
jnTest = (jnStart + i) - jnFirst;
- if ((jnTest % interval) == 0)
- return YES;
+ if ((jnTest % interval) == 0) {
+ NSCalendarDate *start, *end;
+ NGCalendarDateRange *r;
+
+ start = [NSCalendarDate dateForJulianNumber:jnStart + i];
+ start = [start hour: [firStart hourOfDay]
+ minute:[firStart minuteOfHour]
+ second:[firStart secondOfMinute]];
+ end = [start addTimeInterval:[self->firstRange duration]];
+ r = [NGCalendarDateRange calendarDateRangeWithStartDate:start
+ endDate:end];
+ [ranges addObject:r];
+ }
+ }
+
+ return ranges;
+}
+
+- (NSCalendarDate *)lastInstanceStartDate {
+ if ([self->rrule repeatCount] > 0) {
+ long jnFirst, jnRuleLast;
+ NSCalendarDate *firStart, *until;
+
+ firStart = [self->firstRange startDate];
+ jnFirst = [firStart julianNumber];
+ jnRuleLast = ([self->rrule repeatInterval] *
+ [self->rrule repeatCount] *
+ [self factor]) +
+ jnFirst;
+ until = [NSCalendarDate dateForJulianNumber:jnRuleLast];
+ until = [until hour: [firStart hourOfDay]
+ minute:[firStart minuteOfHour]
+ second:[firStart secondOfMinute]];
+ return until;
}
- return NO;
+ return [super lastInstanceStartDate];
+}
+
+- (unsigned)factor {
+ return 1;
}
@end /* iCalDailyRecurrenceCalculator */
+
/*
- ZNeK: NOTE
- woechentlich:
- wie taeglich, aber teilen durch interval*7
+ TODO: If BYDAY is specified, lastInstanceStartDate and recurrences will
+ differ significantly!
*/
@implementation iCalWeeklyRecurrenceCalculator
@end /* iCalWeeklyRecurrenceCalculator */
-/*
- ZNeK: NOTE
- monatlich:
- monatlich: differenz der monate / interval
- alle events mit korrektem tag.
- dann differenz der monate durch interval teilen, wenn ok, ...
- (diffrenz der monate = 12 * 'volle' jahre plus monate im start jahr und monate im ziel jahr
- */
@implementation iCalMonthlyRecurrenceCalculator
-- (BOOL)doesRecurrWithinCalendarDateRange:(NGCalendarDateRange *)_range {
- return YES;
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r {
+ NSMutableArray *ranges;
+ NSCalendarDate *firStart, *rStart, *rEnd, *until;
+ unsigned i, count, interval, diff;
+
+ firStart = [self->firstRange startDate];
+ rStart = [_r startDate];
+ rEnd = [_r endDate];
+ interval = [self->rrule repeatInterval];
+ until = [self lastInstanceStartDate];
+
+ if (until) {
+ if ([until compare:rStart] == NSOrderedAscending)
+ return nil;
+ if ([until compare:rEnd] == NSOrderedDescending)
+ rEnd = until;
+ }
+
+ diff = [firStart monthsBetweenDate:rStart];
+ count = [rStart monthsBetweenDate:rEnd] + 1;
+ ranges = [NSMutableArray arrayWithCapacity:count];
+ for (i = 0 ; i < count; i++) {
+ unsigned test;
+
+ test = diff + i;
+ if ((test % interval) == 0) {
+ NSCalendarDate *start, *end;
+ NGCalendarDateRange *r;
+
+ start = [firStart dateByAddingYears:0
+ months:diff + i
+ days:0];
+ end = [start addTimeInterval:[self->firstRange duration]];
+ r = [NGCalendarDateRange calendarDateRangeWithStartDate:start
+ endDate:end];
+ [ranges addObject:r];
+ }
+ }
+ return ranges;
+}
+
+- (NSCalendarDate *)lastInstanceStartDate {
+ if ([self->rrule repeatCount] > 0) {
+ NSCalendarDate *until;
+ unsigned months, interval;
+
+ interval = [self->rrule repeatInterval];
+ months = [self->rrule repeatCount] * interval;
+ until = [[self->firstRange startDate] dateByAddingYears:0
+ months:months
+ days:0];
+ return until;
+ }
+ return [super lastInstanceStartDate];
}
@end /* iCalMonthlyRecurrenceCalculator */
-/*
- ZNeK: NOTE
- jaehrlich:
- alle events mit dem korrekten tag und monat.
- fuer all diese jeweils
- differenz in jahren durch interval teilen, wenn ok, dann event fuer den tag.
-*/
@implementation iCalYearlyRecurrenceCalculator
-- (BOOL)doesRecurrWithinCalendarDateRange:(NGCalendarDateRange *)_range {
- return YES;
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r {
+ NSMutableArray *ranges;
+ NSCalendarDate *firStart, *rStart, *rEnd, *until;
+ unsigned i, count, interval, diff;
+
+ firStart = [self->firstRange startDate];
+ rStart = [_r startDate];
+ rEnd = [_r endDate];
+ interval = [self->rrule repeatInterval];
+ until = [self lastInstanceStartDate];
+
+ if (until) {
+ if ([until compare:rStart] == NSOrderedAscending)
+ return nil;
+ if ([until compare:rEnd] == NSOrderedDescending)
+ rEnd = until;
+ }
+
+ diff = [firStart yearsBetweenDate:rStart];
+ count = [rStart yearsBetweenDate:rEnd] + 1;
+ ranges = [NSMutableArray arrayWithCapacity:count];
+ for (i = 0 ; i < count; i++) {
+ unsigned test;
+
+ test = diff + i;
+ if ((test % interval) == 0) {
+ NSCalendarDate *start, *end;
+ NGCalendarDateRange *r;
+
+ start = [firStart dateByAddingYears:diff + i
+ months:0
+ days:0];
+ end = [start addTimeInterval:[self->firstRange duration]];
+ r = [NGCalendarDateRange calendarDateRangeWithStartDate:start
+ endDate:end];
+ [ranges addObject:r];
+ }
+ }
+ return ranges;
+}
+
+- (NSCalendarDate *)lastInstanceStartDate {
+ if ([self->rrule repeatCount] > 0) {
+ NSCalendarDate *until;
+ unsigned years, interval;
+
+ interval = [self->rrule repeatInterval];
+ years = [self->rrule repeatCount] * interval;
+ until = [[self->firstRange startDate] dateByAddingYears:years
+ months:0
+ days:0];
+ return until;
+ }
+ return [super lastInstanceStartDate];
}
@end /* iCalYearlyRecurrenceCalculator */