2007-01-31 18:11:29 +01:00
|
|
|
/*
|
2007-08-07 23:16:48 +02:00
|
|
|
Copyright (C) 2004-2007 SKYRIX Software AG
|
|
|
|
Copyright (C) 2007 Helge Hess
|
2010-04-19 23:05:35 +02:00
|
|
|
Copyright (C) 2010 Inverse inc.
|
2007-01-31 18:11:29 +01:00
|
|
|
|
|
|
|
This file is part of SOPE.
|
|
|
|
|
|
|
|
SOPE is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU Lesser General Public License as published by the
|
|
|
|
Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
later version.
|
|
|
|
|
|
|
|
SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with SOPE; see the file COPYING. If not, write to the
|
|
|
|
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2012-04-23 23:17:54 +02:00
|
|
|
#import <Foundation/NSString.h>
|
2007-06-07 18:17:51 +02:00
|
|
|
#import <NGExtensions/NSCalendarDate+misc.h>
|
|
|
|
|
|
|
|
#import "iCalRecurrenceCalculator.h"
|
2007-01-31 18:11:29 +01:00
|
|
|
|
|
|
|
@interface iCalMonthlyRecurrenceCalculator : iCalRecurrenceCalculator
|
|
|
|
@end
|
|
|
|
|
2007-06-07 18:17:51 +02:00
|
|
|
#import <NGExtensions/NGCalendarDateRange.h>
|
|
|
|
#import "iCalRecurrenceRule.h"
|
2010-04-19 23:05:35 +02:00
|
|
|
#import "iCalByDayMask.h"
|
2007-06-07 18:17:51 +02:00
|
|
|
#import "NSCalendarDate+ICal.h"
|
2010-04-19 23:05:35 +02:00
|
|
|
|
2007-06-07 18:17:51 +02:00
|
|
|
#import <string.h>
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
@interface iCalRecurrenceCalculator (PrivateAPI)
|
|
|
|
|
|
|
|
- (NSCalendarDate *) lastInstanceStartDate;
|
|
|
|
|
2007-01-31 18:11:29 +01:00
|
|
|
@end
|
|
|
|
|
|
|
|
// #define HEAVY_DEBUG 1
|
|
|
|
|
|
|
|
@implementation iCalMonthlyRecurrenceCalculator
|
|
|
|
|
|
|
|
typedef BOOL NGMonthSet[12];
|
|
|
|
typedef BOOL NGMonthDaySet[32]; // 0 is unused
|
|
|
|
|
2012-04-23 23:17:54 +02:00
|
|
|
static inline void
|
2008-12-10 19:57:55 +01:00
|
|
|
NGMonthDaySet_clear (NGMonthDaySet *daySet)
|
|
|
|
|
2008-03-26 21:30:53 +01:00
|
|
|
{
|
2012-04-23 23:17:54 +02:00
|
|
|
memset (daySet, 0, sizeof (NGMonthDaySet));
|
2007-01-31 18:11:29 +01:00
|
|
|
}
|
|
|
|
|
2008-03-26 21:30:53 +01:00
|
|
|
static void
|
2008-12-10 19:57:55 +01:00
|
|
|
NGMonthDaySet_copyOrUnion (NGMonthDaySet *base, NGMonthDaySet *new,
|
|
|
|
BOOL doCopy)
|
|
|
|
|
2007-01-31 18:11:29 +01:00
|
|
|
{
|
|
|
|
register unsigned i;
|
2008-03-26 21:30:53 +01:00
|
|
|
|
2007-01-31 18:11:29 +01:00
|
|
|
if (doCopy)
|
2008-12-10 19:57:55 +01:00
|
|
|
memcpy (base, new, sizeof (NGMonthDaySet));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = 1; i <= 31; i++)
|
|
|
|
{
|
|
|
|
if (! (*new)[i])
|
|
|
|
(*base)[i] = NO;
|
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
/**
|
|
|
|
* This method fills split the positions of the BYMONTHDAY constraints
|
|
|
|
* into two separate arrays: one for the positive positions and one for the
|
|
|
|
* negatives positions (converted to their absolute values).
|
|
|
|
*/
|
|
|
|
static BOOL NGMonthDaySet_fillWithByMonthDay (NGMonthDaySet *positiveDaySet,
|
|
|
|
NGMonthDaySet *negativeDaySet,
|
2008-12-10 19:57:55 +01:00
|
|
|
NSArray *byMonthDay)
|
2007-01-31 18:11:29 +01:00
|
|
|
{
|
|
|
|
unsigned i, count;
|
|
|
|
BOOL ok;
|
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
NGMonthDaySet_clear (positiveDaySet);
|
|
|
|
NGMonthDaySet_clear (negativeDaySet);
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
for (i = 0, count = [byMonthDay count], ok = YES; i < count; i++)
|
|
|
|
{
|
|
|
|
int dayInMonth; /* -31..-1 and 1..31 */
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
if ((dayInMonth = [[byMonthDay objectAtIndex: i] intValue]) == 0)
|
|
|
|
{
|
|
|
|
ok = NO;
|
|
|
|
continue; /* invalid value */
|
|
|
|
}
|
|
|
|
if (dayInMonth > 31)
|
|
|
|
{
|
|
|
|
ok = NO;
|
|
|
|
continue; /* error, value to large */
|
|
|
|
}
|
2010-04-19 23:05:35 +02:00
|
|
|
if (dayInMonth < -31)
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
|
|
|
ok = NO;
|
|
|
|
continue; /* error, value to large */
|
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
/* adjust negative days */
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
if (dayInMonth < 0)
|
2010-04-19 23:05:35 +02:00
|
|
|
(*negativeDaySet)[abs(dayInMonth)] = YES;
|
|
|
|
else
|
|
|
|
(*positiveDaySet)[dayInMonth] = YES;
|
2008-12-10 19:57:55 +01:00
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
static inline unsigned iCalDoWForNSDoW (int dow)
|
|
|
|
{
|
2010-04-19 23:05:35 +02:00
|
|
|
switch (dow)
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
|
|
|
case 0: return iCalWeekDaySunday;
|
|
|
|
case 1: return iCalWeekDayMonday;
|
|
|
|
case 2: return iCalWeekDayTuesday;
|
|
|
|
case 3: return iCalWeekDayWednesday;
|
|
|
|
case 4: return iCalWeekDayThursday;
|
|
|
|
case 5: return iCalWeekDayFriday;
|
|
|
|
case 6: return iCalWeekDaySaturday;
|
|
|
|
case 7: return iCalWeekDaySunday;
|
|
|
|
default: return 0;
|
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
}
|
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
- (BOOL) _addInstanceWithStartDate: (NSCalendarDate *)_startDate
|
|
|
|
limitDate: (NSCalendarDate *)_until
|
|
|
|
limitRange: (NGCalendarDateRange *)_r
|
|
|
|
toArray: (NSMutableArray *)_ranges
|
|
|
|
|
2007-01-31 18:11:29 +01:00
|
|
|
{
|
|
|
|
NGCalendarDateRange *r;
|
|
|
|
NSCalendarDate *end;
|
|
|
|
|
|
|
|
/* check whether we are still in the limits */
|
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
if (_until != nil)
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
|
|
|
/* Note: the 'until' in the rrule is inclusive as per spec */
|
|
|
|
if ([_until compare: _startDate] == NSOrderedAscending)
|
|
|
|
/* start after until */
|
|
|
|
return NO; /* Note: we assume that the algorithm is sequential */
|
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
|
|
|
|
/* create end date */
|
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
end = [_startDate addTimeInterval: [firstRange duration]];
|
|
|
|
[end setTimeZone: [_startDate timeZone]];
|
2007-01-31 18:11:29 +01:00
|
|
|
|
|
|
|
/* create range and check whether its in the requested range */
|
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
r = [[NGCalendarDateRange alloc] initWithStartDate: _startDate endDate: end];
|
|
|
|
if ([_r containsDateRange: r])
|
|
|
|
[_ranges addObject: r];
|
2010-04-19 23:05:35 +02:00
|
|
|
[r release];
|
|
|
|
r = nil;
|
2007-01-31 18:11:29 +01:00
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
/**
|
|
|
|
* TODO : Unsupported conditions for MONTHLY recurrences :
|
|
|
|
*
|
|
|
|
* BYYEAR
|
|
|
|
* BYYEARDAY
|
|
|
|
* BYWEEKNO
|
|
|
|
* BYHOUR
|
|
|
|
* BYMINUTE
|
|
|
|
*
|
|
|
|
* There's no GUI to defined such conditions, so there's no
|
|
|
|
* problem for now.
|
|
|
|
*/
|
2008-12-10 19:57:55 +01:00
|
|
|
- (NSArray *)
|
|
|
|
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
|
|
|
{
|
2007-01-31 18:11:29 +01:00
|
|
|
// TODO: check whether this is OK for multiday-events!
|
|
|
|
NSMutableArray *ranges;
|
|
|
|
NSTimeZone *timeZone;
|
2010-04-19 23:05:35 +02:00
|
|
|
NSCalendarDate *eventStartDate, *rStart, *rEnd, *until, *referenceDate;
|
2007-01-31 18:11:29 +01:00
|
|
|
int eventDayOfMonth;
|
2010-04-19 23:05:35 +02:00
|
|
|
unsigned monthIdxInRange, numberOfMonthsInRange, interval, repeatCount;
|
|
|
|
int diff, count;
|
|
|
|
NGMonthSet byMonthList = {
|
|
|
|
// Enable all months of the year
|
2007-01-31 18:11:29 +01:00
|
|
|
YES, YES, YES, YES, YES, YES,
|
|
|
|
YES, YES, YES, YES, YES, YES
|
|
|
|
};
|
2012-04-23 23:17:54 +02:00
|
|
|
NSArray *byMonth, *byMonthDay, *bySetPos; // array of ints (-31..-1 and 1..31)
|
2010-04-19 23:05:35 +02:00
|
|
|
NGMonthDaySet byPositiveMonthDaySet, byNegativeMonthDaySet;
|
|
|
|
iCalByDayMask *byDayMask;
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
eventStartDate = [firstRange startDate];
|
2007-01-31 18:11:29 +01:00
|
|
|
eventDayOfMonth = [eventStartDate dayOfMonth];
|
2007-08-07 23:16:48 +02:00
|
|
|
timeZone = [eventStartDate timeZone];
|
|
|
|
rStart = [_r startDate];
|
|
|
|
rEnd = [_r endDate];
|
2008-12-10 19:57:55 +01:00
|
|
|
interval = [rrule repeatInterval];
|
2010-04-19 23:05:35 +02:00
|
|
|
until = nil;
|
|
|
|
repeatCount = [rrule repeatCount];
|
|
|
|
byMonth = [rrule byMonth];
|
2008-12-10 19:57:55 +01:00
|
|
|
byMonthDay = [rrule byMonthDay];
|
2010-04-19 23:05:35 +02:00
|
|
|
byDayMask = [rrule byDayMask];
|
2012-04-23 23:17:54 +02:00
|
|
|
bySetPos = [rrule bySetPos];
|
2010-04-19 23:05:35 +02:00
|
|
|
diff = 0;
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
if (![rrule isInfinite])
|
|
|
|
{
|
|
|
|
if (repeatCount > 0 && ![rrule hasByMask])
|
|
|
|
{
|
|
|
|
// When there's no BYxxx mask, we can find the date of the last
|
|
|
|
// occurrence.
|
|
|
|
until = [eventStartDate dateByAddingYears: 0
|
|
|
|
months: (interval * (repeatCount - 1))
|
|
|
|
days: 0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
until = [rrule untilDate];
|
|
|
|
}
|
|
|
|
}
|
2007-10-09 19:19:20 +02:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
if (until != nil)
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
2010-04-19 23:05:35 +02:00
|
|
|
if ([until compare: rStart] == NSOrderedAscending)
|
|
|
|
// Range starts after last occurrence
|
2008-12-10 19:57:55 +01:00
|
|
|
return nil;
|
2012-04-23 23:17:54 +02:00
|
|
|
if ([until compare: rEnd] == NSOrderedAscending)
|
2010-04-19 23:05:35 +02:00
|
|
|
// Range ends after last occurence; adjust end date
|
|
|
|
rEnd = until;
|
2008-12-10 19:57:55 +01:00
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
if (byMonth && [byMonth count] > 0)
|
|
|
|
{
|
|
|
|
int i;
|
2012-04-17 00:37:26 +02:00
|
|
|
for (i = 0; i < 12; i++)
|
2010-04-19 23:05:35 +02:00
|
|
|
byMonthList[i] = [byMonth containsObject: [NSString stringWithFormat: @"%i", i + 1]];
|
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
/* precalculate month days */
|
|
|
|
|
|
|
|
if (byMonthDay)
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
2010-04-19 23:05:35 +02:00
|
|
|
NGMonthDaySet_fillWithByMonthDay (&byPositiveMonthDaySet, &byNegativeMonthDaySet, byMonthDay);
|
2008-12-10 19:57:55 +01:00
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
if (repeatCount > 0)
|
|
|
|
{
|
|
|
|
numberOfMonthsInRange = [eventStartDate monthsBetweenDate: rEnd] + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
diff = [eventStartDate monthsBetweenDate: rStart];
|
|
|
|
if ((diff != 0) && [rStart compare: eventStartDate] == NSOrderedAscending)
|
|
|
|
diff = -diff;
|
|
|
|
|
|
|
|
numberOfMonthsInRange = [rStart monthsBetweenDate: rEnd] + 1;
|
|
|
|
}
|
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
ranges = [NSMutableArray arrayWithCapacity: numberOfMonthsInRange];
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
// There's a bug in GNUstep in [NSCalendarDate dateByAddingYears:months:days:]
|
|
|
|
// that causes errors when adding subsequently a month. For this reason,
|
|
|
|
// we set the day of the reference date to 1.
|
|
|
|
referenceDate = [NSCalendarDate dateWithYear: [eventStartDate yearOfCommonEra]
|
|
|
|
month: [eventStartDate monthOfYear]
|
|
|
|
day: 1
|
|
|
|
hour: [eventStartDate hourOfDay]
|
|
|
|
minute: [eventStartDate minuteOfHour]
|
|
|
|
second: 0
|
|
|
|
timeZone: [eventStartDate timeZone]];
|
|
|
|
|
|
|
|
for (monthIdxInRange = 0, count = 0;
|
|
|
|
monthIdxInRange < numberOfMonthsInRange;
|
2008-12-10 19:57:55 +01:00
|
|
|
monthIdxInRange++)
|
|
|
|
{
|
|
|
|
NSCalendarDate *cursor;
|
|
|
|
unsigned numDaysInMonth;
|
|
|
|
int monthIdxInRecurrence, dom;
|
|
|
|
NGMonthDaySet monthDays;
|
|
|
|
BOOL didByFill, doCont;
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
monthIdxInRecurrence = diff + monthIdxInRange;
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
if (monthIdxInRecurrence < 0)
|
|
|
|
continue;
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
/* first check whether we are in the interval */
|
2007-10-09 19:19:20 +02:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
if ((monthIdxInRecurrence % interval) != 0)
|
|
|
|
continue;
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
cursor = [referenceDate dateByAddingYears: 0
|
|
|
|
months: monthIdxInRecurrence
|
|
|
|
days: 0];
|
2008-12-10 19:57:55 +01:00
|
|
|
[cursor setTimeZone: timeZone];
|
|
|
|
numDaysInMonth = [cursor numberOfDaysInMonth];
|
2010-04-19 23:05:35 +02:00
|
|
|
|
|
|
|
/* check whether we match the BYMONTH constraint */
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
if (!byMonthList[[cursor monthOfYear] - 1])
|
|
|
|
continue;
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
/* check whether we match the BYMONTHDAY and BYDAY constraints */
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
didByFill = NO;
|
2010-04-19 23:05:35 +02:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
if (byMonthDay)
|
2010-04-19 23:05:35 +02:00
|
|
|
{
|
|
|
|
// Initialize the monthDays array with the positive days positions
|
|
|
|
NGMonthDaySet_copyOrUnion (&monthDays, &byPositiveMonthDaySet, !didByFill);
|
|
|
|
|
|
|
|
// Add to the array the days matching the negative days positions
|
|
|
|
int i;
|
|
|
|
for (i = 1; i <= 31; i++)
|
|
|
|
if (byNegativeMonthDaySet[i])
|
|
|
|
monthDays[numDaysInMonth - i + 1] = YES;
|
2008-12-10 19:57:55 +01:00
|
|
|
didByFill = YES;
|
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
if (byDayMask)
|
|
|
|
{
|
2012-04-23 23:17:54 +02:00
|
|
|
if (!didByFill)
|
|
|
|
NGMonthDaySet_clear (&monthDays);
|
|
|
|
|
|
|
|
if (bySetPos)
|
|
|
|
{
|
|
|
|
NSUInteger monthDay;
|
|
|
|
NSInteger currentPos;
|
|
|
|
iCalWeekDay currentWeekDay;
|
|
|
|
|
|
|
|
currentWeekDay = [[cursor firstDayOfMonth] dayOfWeek];
|
|
|
|
currentPos = 1;
|
|
|
|
for (monthDay = 0; monthDay <= numDaysInMonth; monthDay++)
|
|
|
|
{
|
|
|
|
if ([byDayMask occursOnDay: currentWeekDay])
|
|
|
|
{
|
|
|
|
if ([bySetPos containsObject:
|
|
|
|
[NSString stringWithFormat: @"%d", currentPos]])
|
|
|
|
monthDays[monthDay+1] = YES;
|
|
|
|
currentPos++;
|
|
|
|
}
|
|
|
|
currentWeekDay = (currentWeekDay + 1) % 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentWeekDay = [[cursor lastDayOfMonth] dayOfWeek];
|
|
|
|
currentPos = -1;
|
|
|
|
for (monthDay = numDaysInMonth; monthDay > 0; monthDay--)
|
|
|
|
{
|
|
|
|
if ([byDayMask occursOnDay: currentWeekDay])
|
|
|
|
{
|
|
|
|
if ([bySetPos containsObject:
|
|
|
|
[NSString stringWithFormat: @"%d", currentPos]])
|
|
|
|
monthDays[monthDay] = YES;
|
|
|
|
currentPos--;
|
|
|
|
}
|
|
|
|
if (currentWeekDay > 0)
|
|
|
|
currentWeekDay--;
|
|
|
|
else
|
|
|
|
currentWeekDay = 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned int firstDoWInMonth, currentWeekDay;
|
|
|
|
unsigned int weekDaysCount[7], currentWeekDaysCount[7];
|
|
|
|
int i, positiveOrder, negativeOrder;
|
|
|
|
|
|
|
|
firstDoWInMonth = [[cursor firstDayOfMonth] dayOfWeek];
|
|
|
|
|
|
|
|
// Fill weekDaysCount to handle negative positions
|
|
|
|
currentWeekDay = firstDoWInMonth;
|
|
|
|
memset(weekDaysCount, 0, 7 * sizeof(unsigned int));
|
|
|
|
for (i = 1; i <= numDaysInMonth; i++)
|
|
|
|
{
|
|
|
|
weekDaysCount[currentWeekDay]++;
|
|
|
|
currentWeekDay = (currentWeekDay + 1) % 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentWeekDay = firstDoWInMonth;
|
|
|
|
memset(currentWeekDaysCount, 0, 7 * sizeof(unsigned int));
|
|
|
|
for (i = 1; i <= numDaysInMonth; i++)
|
|
|
|
{
|
|
|
|
if (!didByFill || monthDays[i])
|
|
|
|
{
|
|
|
|
positiveOrder = currentWeekDaysCount[currentWeekDay] + 1;
|
|
|
|
negativeOrder = currentWeekDaysCount[currentWeekDay] - weekDaysCount[currentWeekDay];
|
|
|
|
monthDays[i] = (([byDayMask occursOnDay: (iCalWeekDay)currentWeekDay
|
|
|
|
withWeekNumber: positiveOrder]) ||
|
|
|
|
([byDayMask occursOnDay: (iCalWeekDay)currentWeekDay
|
|
|
|
withWeekNumber: negativeOrder]));
|
|
|
|
}
|
|
|
|
currentWeekDaysCount[currentWeekDay]++;
|
|
|
|
currentWeekDay = (currentWeekDay + 1) % 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
didByFill = YES;
|
2008-12-10 19:57:55 +01:00
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
if (didByFill)
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
2010-04-19 23:05:35 +02:00
|
|
|
if (diff + monthIdxInRange == 0)
|
|
|
|
{
|
|
|
|
// When dealing with the month of the first occurence, remove days
|
|
|
|
// that occur before the first occurrence.
|
2011-03-22 21:56:57 +01:00
|
|
|
memset (monthDays, NO, sizeof (BOOL) * eventDayOfMonth);
|
2010-04-19 23:05:35 +02:00
|
|
|
// The first occurrence must always be included.
|
2011-03-22 21:56:57 +01:00
|
|
|
monthDays[eventDayOfMonth] = YES;
|
2010-04-19 23:05:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// No rules applied, take the dayOfMonth of the startDate
|
2008-12-10 19:57:55 +01:00
|
|
|
NGMonthDaySet_clear (&monthDays);
|
|
|
|
monthDays[eventDayOfMonth] = YES;
|
|
|
|
}
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
/*
|
|
|
|
Next step is to create NSCalendarDate instances from our 'monthDays'
|
|
|
|
set. We walk over each day of the 'monthDays' set. If its flag isn't
|
|
|
|
set, we continue.
|
|
|
|
If its set, we add the date to the instance.
|
2007-08-07 23:16:48 +02:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
The 'cursor' is the *startdate* of the event (not necessarily a
|
|
|
|
component of the sequence!) plus the currently processed month.
|
|
|
|
Eg:
|
2007-08-07 23:16:48 +02:00
|
|
|
startdate: 2007-01-30
|
|
|
|
cursor[1]: 2007-01-30
|
|
|
|
cursor[2]: 2007-02-28 <== Note: we have February!
|
2008-12-10 19:57:55 +01:00
|
|
|
*/
|
2007-08-07 23:16:48 +02:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
for (dom = 1, doCont = YES; dom <= numDaysInMonth && doCont; dom++)
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
|
|
|
NSCalendarDate *start;
|
2007-01-31 18:11:29 +01:00
|
|
|
|
2008-12-10 19:57:55 +01:00
|
|
|
if (!monthDays[dom])
|
|
|
|
continue;
|
2007-08-07 23:16:48 +02:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
start = [cursor dateByAddingYears: 0 months: 0 days: (dom - 1)];
|
|
|
|
doCont = [self _addInstanceWithStartDate: start
|
|
|
|
limitDate: until
|
|
|
|
limitRange: _r
|
|
|
|
toArray: ranges];
|
|
|
|
//NSLog(@"*** MONTHLY [%i/%i] adding %@%@ (count = %i)", dom, numDaysInMonth, start, (doCont?@"":@" .. NOT!"), count);
|
|
|
|
if (repeatCount > 0)
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
2010-04-19 23:05:35 +02:00
|
|
|
count++;
|
|
|
|
//NSLog(@"MONTHLY count = %i/%i", count, repeatCount);
|
|
|
|
doCont = (count < repeatCount);
|
2008-12-10 19:57:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!doCont) break; /* reached some limit */
|
2007-01-31 18:11:29 +01:00
|
|
|
}
|
|
|
|
return ranges;
|
|
|
|
}
|
|
|
|
|
2008-12-10 20:02:50 +01:00
|
|
|
- (NSCalendarDate *) lastInstanceStartDate
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
2008-12-10 20:02:50 +01:00
|
|
|
NSCalendarDate *firStart, *lastInstanceStartDate;
|
2010-04-19 23:05:35 +02:00
|
|
|
NGCalendarDateRange *r;
|
|
|
|
NSArray *instances;
|
2008-12-10 20:02:50 +01:00
|
|
|
|
2010-04-19 23:05:35 +02:00
|
|
|
lastInstanceStartDate = nil;
|
|
|
|
if ([rrule repeatCount] > 0)
|
2008-12-10 19:57:55 +01:00
|
|
|
{
|
2010-04-20 22:29:59 +02:00
|
|
|
firStart = [firstRange startDate];
|
2010-04-19 23:05:35 +02:00
|
|
|
if ([rrule hasByMask])
|
|
|
|
{
|
|
|
|
// Must perform the complete calculation
|
|
|
|
r = [NGCalendarDateRange calendarDateRangeWithStartDate: firStart
|
|
|
|
endDate: [NSCalendarDate distantFuture]];
|
|
|
|
instances = [self recurrenceRangesWithinCalendarDateRange: r];
|
|
|
|
if ([instances count])
|
|
|
|
lastInstanceStartDate = [(NGCalendarDateRange *)[instances lastObject] startDate];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// No BYxxx mask
|
|
|
|
lastInstanceStartDate = [firStart dateByAddingYears: 0
|
|
|
|
months: ([rrule repeatInterval]
|
|
|
|
* ([rrule repeatCount] - 1))
|
|
|
|
days: 0];
|
|
|
|
}
|
2008-12-10 19:57:55 +01:00
|
|
|
}
|
2008-12-10 20:02:50 +01:00
|
|
|
else
|
|
|
|
lastInstanceStartDate = [super lastInstanceStartDate];
|
|
|
|
|
|
|
|
return lastInstanceStartDate;
|
2007-01-31 18:11:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@end /* iCalMonthlyRecurrenceCalculator */
|