Added proper support for BYxxx recurrent masks.
See ChangeLog Monotone-Parent: 1388a39a062a16e073dca526237e1b25d5fa19d7 Monotone-Revision: 0e9f9326c164b5f5513aeb461785100c6c18fd0e Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2010-04-19T21:05:35 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
97e9fb3e67
commit
6e1ee64101
|
@ -2,7 +2,6 @@ config.make
|
||||||
shared_debug_obj
|
shared_debug_obj
|
||||||
obj
|
obj
|
||||||
err
|
err
|
||||||
log
|
|
||||||
build\.log
|
build\.log
|
||||||
imgs-.*
|
imgs-.*
|
||||||
diff
|
diff
|
||||||
|
|
4
NEWS
4
NEWS
|
@ -4,10 +4,14 @@
|
||||||
- added support for LDAP password policies
|
- added support for LDAP password policies
|
||||||
- added support for custom Sieve filters
|
- added support for custom Sieve filters
|
||||||
- fixed timezone issues occurring specifically in the southern hemisphere
|
- fixed timezone issues occurring specifically in the southern hemisphere
|
||||||
|
- updated ckeditor to version 3.2
|
||||||
- tabs: enabled the scrolling when overflowing
|
- tabs: enabled the scrolling when overflowing
|
||||||
- updated Czech translation, thanks to Milos Wimmer
|
- updated Czech translation, thanks to Milos Wimmer
|
||||||
- removed remaining .wo templates, thereby easing the effort for future translations
|
- removed remaining .wo templates, thereby easing the effort for future translations
|
||||||
- fixed regressions with Courier IMAP and Dovecot
|
- fixed regressions with Courier IMAP and Dovecot
|
||||||
|
- added support for BYDAY with multiple values and negative positions
|
||||||
|
- added support for BYMONTHDAY with multiple values and negative positions
|
||||||
|
- added support for BYMONTH with multiple values
|
||||||
- added ability to delete events from a keypress
|
- added ability to delete events from a keypress
|
||||||
- added the "remove" command to "sogo-tool", in order to remove user data and settings
|
- added the "remove" command to "sogo-tool", in order to remove user data and settings
|
||||||
- added the ability to export address books in LDIF format from the web interface
|
- added the ability to export address books in LDIF format from the web interface
|
||||||
|
|
|
@ -1,3 +1,41 @@
|
||||||
|
2010-04-19 Francis Lachapelle <flachapelle@inverse.ca>
|
||||||
|
|
||||||
|
* iCalByDayMask.[h|m]: new class that computes complex BYDAY constraints.
|
||||||
|
|
||||||
|
* iCalRecurrenceRule.m (-byDayMask): new method that returns an
|
||||||
|
instance of iCalByDayMask.
|
||||||
|
(-byMonth): new method that returns an array with the values of
|
||||||
|
the BYMONTH constraint.
|
||||||
|
(-hasByMask): new method that returns true if the rule has at
|
||||||
|
least one BYxxx mask defined.
|
||||||
|
|
||||||
|
* iCalDailyRecurrenceCalculator.m
|
||||||
|
(-recurrenceRangesWithinCalendarDateRange:): make use of the new
|
||||||
|
iCalByDayMask class.
|
||||||
|
(-lastInstanceStartDate): make use of the previous method when a
|
||||||
|
BYxxx mask is defined in the rule.
|
||||||
|
|
||||||
|
* iCalWeeklyRecurrenceCalculator.m
|
||||||
|
(-recurrenceRangesWithinCalendarDateRange:): make use of the new
|
||||||
|
iCalByDayMask class.
|
||||||
|
(-lastInstanceStartDate): make use of the previous method when a
|
||||||
|
BYxxx mask is defined in the rule.
|
||||||
|
|
||||||
|
* iCalMonthlyRecurrenceCalculator.m
|
||||||
|
(-recurrenceRangesWithinCalendarDateRange:): added support for
|
||||||
|
BYMONTH, negative BYMONTHDAY, and BYDAY.
|
||||||
|
(-lastInstanceStartDate): make use of the previous method when a
|
||||||
|
BYxxx mask is defined in the rule.
|
||||||
|
|
||||||
|
* iCalYearlyRecurrenceCalculator.m
|
||||||
|
(-recurrenceRangesWithinCalendarDateRange:): added support for
|
||||||
|
BYxxx constraints using the iCalMonthlyRecurrenceCalculator class.
|
||||||
|
(-lastInstanceStartDate): make use of the previous method when a
|
||||||
|
BYxxx mask is defined in the rule.
|
||||||
|
|
||||||
|
* iCalTimeZonePeriod.m (-_occurenceForDate:byRRule:): make use of
|
||||||
|
the new iCalByDayMask class.
|
||||||
|
|
||||||
2010-04-09 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
2010-04-09 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
|
|
||||||
* iCalEvent.m (-propertyValue:): new method that accept a
|
* iCalEvent.m (-propertyValue:): new method that accept a
|
||||||
|
|
|
@ -31,6 +31,7 @@ libNGCards_HEADER_FILES = \
|
||||||
NGCards.h \
|
NGCards.h \
|
||||||
iCalAlarm.h \
|
iCalAlarm.h \
|
||||||
iCalAttachment.h \
|
iCalAttachment.h \
|
||||||
|
iCalByDayMask.h \
|
||||||
iCalCalendar.h \
|
iCalCalendar.h \
|
||||||
iCalDataSource.h \
|
iCalDataSource.h \
|
||||||
iCalDateTime.h \
|
iCalDateTime.h \
|
||||||
|
@ -78,6 +79,7 @@ libNGCards_OBJC_FILES = \
|
||||||
\
|
\
|
||||||
iCalAlarm.m \
|
iCalAlarm.m \
|
||||||
iCalAttachment.m \
|
iCalAttachment.m \
|
||||||
|
iCalByDayMask.m \
|
||||||
iCalCalendar.m \
|
iCalCalendar.m \
|
||||||
iCalDailyRecurrenceCalculator.m \
|
iCalDailyRecurrenceCalculator.m \
|
||||||
iCalDateTime.m \
|
iCalDateTime.m \
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
|
Copyright (C) 2006-2010 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOPE.
|
This file is part of SOPE.
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
|
|
||||||
#import "iCalRecurrenceCalculator.h"
|
#import "iCalRecurrenceCalculator.h"
|
||||||
#import "iCalRecurrenceRule.h"
|
#import "iCalRecurrenceRule.h"
|
||||||
|
#import "iCalByDayMask.h"
|
||||||
|
|
||||||
@interface iCalDailyRecurrenceCalculator : iCalRecurrenceCalculator
|
@interface iCalDailyRecurrenceCalculator : iCalRecurrenceCalculator
|
||||||
@end
|
@end
|
||||||
|
@ -39,17 +41,34 @@
|
||||||
|
|
||||||
@implementation iCalDailyRecurrenceCalculator
|
@implementation iCalDailyRecurrenceCalculator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO : Unsupported conditions for DAILY recurrences :
|
||||||
|
*
|
||||||
|
* BYYEAR
|
||||||
|
* BYYEARDAY
|
||||||
|
* BYWEEKNO
|
||||||
|
* BYMONTH
|
||||||
|
* BYMONTHDAY
|
||||||
|
* BYHOUR
|
||||||
|
* BYMINUTE
|
||||||
|
*
|
||||||
|
* There's no GUI to defined such conditions, so there's no
|
||||||
|
* problem for now.
|
||||||
|
*/
|
||||||
- (NSArray *)
|
- (NSArray *)
|
||||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||||
{
|
{
|
||||||
NSMutableArray *ranges;
|
NSMutableArray *ranges;
|
||||||
NSCalendarDate *firStart, *startDate, *endDate, *currentStartDate, *currentEndDate;
|
NSCalendarDate *firStart, *startDate, *endDate, *currentStartDate, *currentEndDate;
|
||||||
long i;
|
iCalByDayMask *dayMask;
|
||||||
|
long i, count, repeatCount;
|
||||||
unsigned interval;
|
unsigned interval;
|
||||||
|
|
||||||
firStart = [firstRange startDate];
|
firStart = [firstRange startDate];
|
||||||
startDate = [_r startDate];
|
startDate = [_r startDate];
|
||||||
endDate = [_r endDate];
|
endDate = [_r endDate];
|
||||||
|
dayMask = nil;
|
||||||
|
repeatCount = 0;
|
||||||
|
|
||||||
if ([endDate compare: firStart] == NSOrderedAscending)
|
if ([endDate compare: firStart] == NSOrderedAscending)
|
||||||
// Range ends before first occurrence
|
// Range ends before first occurrence
|
||||||
|
@ -57,19 +76,33 @@
|
||||||
|
|
||||||
interval = [rrule repeatInterval];
|
interval = [rrule repeatInterval];
|
||||||
|
|
||||||
|
if ([[rrule byDay] length])
|
||||||
|
dayMask = [rrule byDayMask];
|
||||||
|
|
||||||
// If rule is bound, check the bounds
|
// If rule is bound, check the bounds
|
||||||
if (![rrule isInfinite])
|
if (![rrule isInfinite])
|
||||||
{
|
{
|
||||||
NSCalendarDate *until, *lastDate;
|
NSCalendarDate *until, *lastDate;
|
||||||
|
|
||||||
|
lastDate = nil;
|
||||||
until = [rrule untilDate];
|
until = [rrule untilDate];
|
||||||
if (until)
|
if (until)
|
||||||
|
{
|
||||||
lastDate = until;
|
lastDate = until;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
repeatCount = [rrule repeatCount];
|
||||||
|
if (dayMask == nil)
|
||||||
|
// If there's no day mask, we can compute the date of the last
|
||||||
|
// occurrence of the recurrent rule.
|
||||||
lastDate = [firStart dateByAddingYears: 0 months: 0
|
lastDate = [firStart dateByAddingYears: 0 months: 0
|
||||||
days: (interval
|
days: (interval
|
||||||
* ([rrule repeatCount] - 1))];
|
* (repeatCount - 1))];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastDate != nil)
|
||||||
|
{
|
||||||
if ([lastDate compare: startDate] == NSOrderedAscending)
|
if ([lastDate compare: startDate] == NSOrderedAscending)
|
||||||
// Range starts after last occurrence
|
// Range starts after last occurrence
|
||||||
return nil;
|
return nil;
|
||||||
|
@ -78,32 +111,52 @@
|
||||||
// Range ends after last occurence; adjust end date
|
// Range ends after last occurence; adjust end date
|
||||||
endDate = lastDate;
|
endDate = lastDate;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
currentStartDate = [firStart copy];
|
currentStartDate = [firStart copy];
|
||||||
[currentStartDate autorelease];
|
[currentStartDate autorelease];
|
||||||
ranges = [NSMutableArray array];
|
ranges = [NSMutableArray array];
|
||||||
i = 1;
|
i = 1;
|
||||||
|
count = 0;
|
||||||
|
|
||||||
while ([currentStartDate compare: endDate] == NSOrderedAscending ||
|
while ([currentStartDate compare: endDate] == NSOrderedAscending ||
|
||||||
[currentStartDate compare: endDate] == NSOrderedSame)
|
[currentStartDate compare: endDate] == NSOrderedSame)
|
||||||
{
|
{
|
||||||
if ([startDate compare: currentStartDate] == NSOrderedAscending ||
|
BOOL wrongDay, isFirStart;
|
||||||
[startDate compare: currentStartDate] == NSOrderedSame)
|
|
||||||
|
wrongDay = NO;
|
||||||
|
isFirStart = NO;
|
||||||
|
|
||||||
|
if (i == 1)
|
||||||
|
{
|
||||||
|
isFirStart = YES;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
else if (repeatCount > 0 && dayMask)
|
||||||
|
{
|
||||||
|
// If the rule count is defined, stop once the count is reached.
|
||||||
|
if ([dayMask occursOnDay: [currentStartDate dayOfWeek]])
|
||||||
|
count++;
|
||||||
|
else
|
||||||
|
wrongDay = YES;
|
||||||
|
|
||||||
|
if (count > repeatCount)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrongDay == NO &&
|
||||||
|
([startDate compare: currentStartDate] == NSOrderedAscending ||
|
||||||
|
[startDate compare: currentStartDate] == NSOrderedSame))
|
||||||
{
|
{
|
||||||
BOOL wrongDay = NO;
|
|
||||||
unsigned int mask;
|
|
||||||
NGCalendarDateRange *r;
|
NGCalendarDateRange *r;
|
||||||
|
|
||||||
if ([rrule byDayMask])
|
if (isFirStart == NO && dayMask && repeatCount == 0)
|
||||||
{
|
{
|
||||||
mask = ([currentStartDate dayOfWeek]
|
if (![dayMask occursOnDay: [currentStartDate dayOfWeek]])
|
||||||
? (unsigned int) 1 << ([currentStartDate dayOfWeek])
|
|
||||||
: iCalWeekDaySunday);
|
|
||||||
if (([rrule byDayMask] & mask) != mask)
|
|
||||||
wrongDay = YES;
|
wrongDay = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrongDay == NO)
|
if (isFirStart == YES || wrongDay == NO)
|
||||||
{
|
{
|
||||||
currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]];
|
currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]];
|
||||||
r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate
|
r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate
|
||||||
|
@ -115,6 +168,13 @@
|
||||||
|
|
||||||
currentStartDate = [firStart dateByAddingYears: 0 months: 0
|
currentStartDate = [firStart dateByAddingYears: 0 months: 0
|
||||||
days: (interval * i)];
|
days: (interval * i)];
|
||||||
|
|
||||||
|
if (repeatCount > 0 && count == repeatCount)
|
||||||
|
// The count variable is only usefull when a BYDAY constraint is
|
||||||
|
// defined; when there's no BYDAY constraint, the endDate has been
|
||||||
|
// adjusted to match the repeat count, if defined.
|
||||||
|
break;
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return ranges;
|
return ranges;
|
||||||
|
@ -123,15 +183,30 @@
|
||||||
- (NSCalendarDate *) lastInstanceStartDate
|
- (NSCalendarDate *) lastInstanceStartDate
|
||||||
{
|
{
|
||||||
NSCalendarDate *firStart, *lastInstanceStartDate;
|
NSCalendarDate *firStart, *lastInstanceStartDate;
|
||||||
|
NGCalendarDateRange *r;
|
||||||
|
NSArray *instances;
|
||||||
|
|
||||||
|
lastInstanceStartDate = nil;
|
||||||
if ([rrule repeatCount] > 0)
|
if ([rrule repeatCount] > 0)
|
||||||
{
|
{
|
||||||
|
if ([rrule hasByMask])
|
||||||
|
{
|
||||||
|
// Must perform the complete calculation
|
||||||
firStart = [firstRange startDate];
|
firStart = [firstRange startDate];
|
||||||
|
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: 0
|
lastInstanceStartDate = [firStart dateByAddingYears: 0 months: 0
|
||||||
days: ([rrule repeatInterval]
|
days: ([rrule repeatInterval]
|
||||||
* ([rrule repeatCount] - 1))];
|
* ([rrule repeatCount] - 1))];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
lastInstanceStartDate = [super lastInstanceStartDate];
|
lastInstanceStartDate = [super lastInstanceStartDate];
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2007 SKYRIX Software AG
|
Copyright (C) 2004-2007 SKYRIX Software AG
|
||||||
Copyright (C) 2007 Helge Hess
|
Copyright (C) 2007 Helge Hess
|
||||||
|
Copyright (C) 2010 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOPE.
|
This file is part of SOPE.
|
||||||
|
|
||||||
|
@ -29,8 +30,11 @@
|
||||||
|
|
||||||
#import <NGExtensions/NGCalendarDateRange.h>
|
#import <NGExtensions/NGCalendarDateRange.h>
|
||||||
#import "iCalRecurrenceRule.h"
|
#import "iCalRecurrenceRule.h"
|
||||||
|
#import "iCalByDayMask.h"
|
||||||
#import "NSCalendarDate+ICal.h"
|
#import "NSCalendarDate+ICal.h"
|
||||||
|
|
||||||
#import <string.h>
|
#import <string.h>
|
||||||
|
#import <math.h>
|
||||||
|
|
||||||
@interface iCalRecurrenceCalculator (PrivateAPI)
|
@interface iCalRecurrenceCalculator (PrivateAPI)
|
||||||
|
|
||||||
|
@ -74,15 +78,20 @@ NGMonthDaySet_copyOrUnion (NGMonthDaySet *base, NGMonthDaySet *new,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL NGMonthDaySet_fillWithByMonthDay (NGMonthDaySet *daySet,
|
/**
|
||||||
|
* 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,
|
||||||
NSArray *byMonthDay)
|
NSArray *byMonthDay)
|
||||||
|
|
||||||
{
|
{
|
||||||
/* list of days in the month */
|
|
||||||
unsigned i, count;
|
unsigned i, count;
|
||||||
BOOL ok;
|
BOOL ok;
|
||||||
|
|
||||||
NGMonthDaySet_clear (daySet);
|
NGMonthDaySet_clear (positiveDaySet);
|
||||||
|
NGMonthDaySet_clear (negativeDaySet);
|
||||||
|
|
||||||
for (i = 0, count = [byMonthDay count], ok = YES; i < count; i++)
|
for (i = 0, count = [byMonthDay count], ok = YES; i < count; i++)
|
||||||
{
|
{
|
||||||
|
@ -107,12 +116,9 @@ static BOOL NGMonthDaySet_fillWithByMonthDay (NGMonthDaySet *daySet,
|
||||||
/* adjust negative days */
|
/* adjust negative days */
|
||||||
|
|
||||||
if (dayInMonth < 0)
|
if (dayInMonth < 0)
|
||||||
{
|
(*negativeDaySet)[abs(dayInMonth)] = YES;
|
||||||
/* eg: -1 == last day in month, 30 days => 30 */
|
else
|
||||||
dayInMonth = 32 - dayInMonth /* because we count from 1 */;
|
(*positiveDaySet)[dayInMonth] = YES;
|
||||||
}
|
|
||||||
|
|
||||||
(*daySet)[dayInMonth] = YES;
|
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
@ -133,100 +139,6 @@ static inline unsigned iCalDoWForNSDoW (int dow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HEAVY_DEBUG
|
|
||||||
static NSString *dowEN[8] = {
|
|
||||||
@"SU", @"MO", @"TU", @"WE", @"TH", @"FR", @"SA", @"SU-"
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
|
||||||
unsigned dayMask,
|
|
||||||
unsigned firstDoWInMonth,
|
|
||||||
unsigned numberOfDaysInMonth,
|
|
||||||
int occurrence1)
|
|
||||||
|
|
||||||
{
|
|
||||||
// TODO: this is called 'X' because the API doesn't allow for full iCalendar
|
|
||||||
// functionality. The daymask must be a list of occurence+dow
|
|
||||||
register unsigned dayInMonth;
|
|
||||||
register int dow; /* current day of the week */
|
|
||||||
int occurrences[7] = { 0, 0, 0, 0, 0, 0, 0 } ;
|
|
||||||
|
|
||||||
NGMonthDaySet_clear (daySet);
|
|
||||||
|
|
||||||
if (occurrence1 >= 0)
|
|
||||||
{
|
|
||||||
for (dayInMonth = 1, dow = firstDoWInMonth; dayInMonth <= 31; dayInMonth++)
|
|
||||||
{
|
|
||||||
// TODO: complete me
|
|
||||||
|
|
||||||
if (dayMask & iCalDoWForNSDoW (dow))
|
|
||||||
{
|
|
||||||
if (occurrence1 == 0)
|
|
||||||
(*daySet)[dayInMonth] = YES;
|
|
||||||
else { /* occurrence1 > 0 */
|
|
||||||
occurrences[dow] = occurrences[dow] + 1;
|
|
||||||
|
|
||||||
if (occurrences[dow] == occurrence1)
|
|
||||||
(*daySet)[dayInMonth] = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dow = (dow == 6 /* Sat */) ? 0 /* Sun */ : (dow + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int lastDoWInMonthSet;
|
|
||||||
|
|
||||||
/* get the last dow in the set (not necessarily the month!) */
|
|
||||||
for (dayInMonth = 1, dow = firstDoWInMonth;
|
|
||||||
dayInMonth < numberOfDaysInMonth;dayInMonth++)
|
|
||||||
dow = (dow == 6 /* Sat */) ? 0 /* Sun */ : (dow + 1);
|
|
||||||
lastDoWInMonthSet = dow;
|
|
||||||
|
|
||||||
#if HEAVY_DEBUG
|
|
||||||
NSLog (@"LAST DOW IN SET: %i / %@",
|
|
||||||
lastDoWInMonthSet, dowEN[lastDoWInMonthSet]);
|
|
||||||
#endif
|
|
||||||
/* start at the end of the set */
|
|
||||||
for (dayInMonth = numberOfDaysInMonth, dow = lastDoWInMonthSet;
|
|
||||||
dayInMonth >= 1; dayInMonth--)
|
|
||||||
{
|
|
||||||
// TODO: complete me
|
|
||||||
|
|
||||||
#if HEAVY_DEBUG
|
|
||||||
NSLog (@" CHECK day-of-month %02i, "
|
|
||||||
@" dow=%i/%@ (first=%i/%@, last=%i/%@)",
|
|
||||||
dayInMonth,
|
|
||||||
dow, dowEN[dow],
|
|
||||||
firstDoWInMonth, dowEN[firstDoWInMonth],
|
|
||||||
lastDoWInMonthSet, dowEN[lastDoWInMonthSet]
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (dayMask & iCalDoWForNSDoW (dow))
|
|
||||||
{
|
|
||||||
occurrences[dow] = occurrences[dow] + 1;
|
|
||||||
#if HEAVY_DEBUG
|
|
||||||
NSLog (@" MATCH %i/%@ count: %i occurences=%i",
|
|
||||||
dow, dowEN[dow], occurrences[dow], occurrence1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (occurrences[dow] == -occurrence1)
|
|
||||||
{
|
|
||||||
#if HEAVY_DEBUG
|
|
||||||
NSLog (@" COUNT MATCH");
|
|
||||||
#endif
|
|
||||||
(*daySet)[dayInMonth] = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dow = (dow == 0 /* Sun */) ? 6 /* Sat */ : (dow - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL) _addInstanceWithStartDate: (NSCalendarDate *)_startDate
|
- (BOOL) _addInstanceWithStartDate: (NSCalendarDate *)_startDate
|
||||||
limitDate: (NSCalendarDate *)_until
|
limitDate: (NSCalendarDate *)_until
|
||||||
limitRange: (NGCalendarDateRange *)_r
|
limitRange: (NGCalendarDateRange *)_r
|
||||||
|
@ -238,9 +150,6 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
||||||
|
|
||||||
/* check whether we are still in the limits */
|
/* check whether we are still in the limits */
|
||||||
|
|
||||||
// TODO: I think we should check in here whether we succeeded the
|
|
||||||
// repeatCount. Currently we precalculate that info in the
|
|
||||||
// -lastInstanceStartDate method.
|
|
||||||
if (_until != nil)
|
if (_until != nil)
|
||||||
{
|
{
|
||||||
/* Note: the 'until' in the rrule is inclusive as per spec */
|
/* Note: the 'until' in the rrule is inclusive as per spec */
|
||||||
|
@ -259,29 +168,42 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
||||||
r = [[NGCalendarDateRange alloc] initWithStartDate: _startDate endDate: end];
|
r = [[NGCalendarDateRange alloc] initWithStartDate: _startDate endDate: end];
|
||||||
if ([_r containsDateRange: r])
|
if ([_r containsDateRange: r])
|
||||||
[_ranges addObject: r];
|
[_ranges addObject: r];
|
||||||
[r release]; r = nil;
|
[r release];
|
||||||
|
r = nil;
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
- (NSArray *)
|
- (NSArray *)
|
||||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||||
{
|
{
|
||||||
/* main entry */
|
|
||||||
// TODO: check whether this is OK for multiday-events!
|
// TODO: check whether this is OK for multiday-events!
|
||||||
NSMutableArray *ranges;
|
NSMutableArray *ranges;
|
||||||
NSTimeZone *timeZone;
|
NSTimeZone *timeZone;
|
||||||
NSCalendarDate *eventStartDate, *rStart, *rEnd, *until;
|
NSCalendarDate *eventStartDate, *rStart, *rEnd, *until, *referenceDate;
|
||||||
int eventDayOfMonth;
|
int eventDayOfMonth;
|
||||||
unsigned monthIdxInRange, numberOfMonthsInRange, interval;
|
unsigned monthIdxInRange, numberOfMonthsInRange, interval, repeatCount;
|
||||||
int diff;
|
int diff, count;
|
||||||
NGMonthSet byMonthList = { // TODO: fill from rrule, this is the default
|
NGMonthSet byMonthList = {
|
||||||
/* enable all months of the year */
|
// Enable all months of the year
|
||||||
YES, YES, YES, YES, YES, YES,
|
YES, YES, YES, YES, YES, YES,
|
||||||
YES, YES, YES, YES, YES, YES
|
YES, YES, YES, YES, YES, YES
|
||||||
};
|
};
|
||||||
NSArray *byMonthDay; // array of ints (-31..-1 and 1..31)
|
NSArray *byMonth, *byMonthDay; // array of ints (-31..-1 and 1..31)
|
||||||
NGMonthDaySet byMonthDaySet;
|
NGMonthDaySet byPositiveMonthDaySet, byNegativeMonthDaySet;
|
||||||
|
iCalByDayMask *byDayMask;
|
||||||
|
|
||||||
eventStartDate = [firstRange startDate];
|
eventStartDate = [firstRange startDate];
|
||||||
eventDayOfMonth = [eventStartDate dayOfMonth];
|
eventDayOfMonth = [eventStartDate dayOfMonth];
|
||||||
|
@ -289,48 +211,81 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
||||||
rStart = [_r startDate];
|
rStart = [_r startDate];
|
||||||
rEnd = [_r endDate];
|
rEnd = [_r endDate];
|
||||||
interval = [rrule repeatInterval];
|
interval = [rrule repeatInterval];
|
||||||
until = [self lastInstanceStartDate]; // TODO: maybe replace
|
until = nil;
|
||||||
|
repeatCount = [rrule repeatCount];
|
||||||
|
byMonth = [rrule byMonth];
|
||||||
byMonthDay = [rrule byMonthDay];
|
byMonthDay = [rrule byMonthDay];
|
||||||
|
byDayMask = [rrule byDayMask];
|
||||||
|
diff = 0;
|
||||||
|
|
||||||
|
if (![rrule isInfinite])
|
||||||
/* check whether the range to be processed is beyond the 'until' date */
|
|
||||||
if (until)
|
|
||||||
{
|
{
|
||||||
if ([until compare: rStart] == NSOrderedAscending) /* until before start */
|
if (repeatCount > 0 && ![rrule hasByMask])
|
||||||
return nil;
|
{
|
||||||
if ([until compare: rEnd] == NSOrderedDescending) /* end before until */
|
// When there's no BYxxx mask, we can find the date of the last
|
||||||
rEnd = until; // TODO: why is that? end is _before_ until?
|
// occurrence.
|
||||||
|
until = [eventStartDate dateByAddingYears: 0
|
||||||
|
months: (interval * (repeatCount - 1))
|
||||||
|
days: 0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
until = [rrule untilDate];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (until != nil)
|
||||||
|
{
|
||||||
|
if ([until compare: rStart] == NSOrderedAscending)
|
||||||
|
// Range starts after last occurrence
|
||||||
|
return nil;
|
||||||
|
if ([until compare: rEnd] == NSOrderedDescending)
|
||||||
|
// Range ends after last occurence; adjust end date
|
||||||
|
rEnd = until;
|
||||||
|
}
|
||||||
|
|
||||||
/* precalculate month days (same for all instances) */
|
if (byMonth && [byMonth count] > 0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i <= 12; i++)
|
||||||
|
byMonthList[i] = [byMonth containsObject: [NSString stringWithFormat: @"%i", i + 1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* precalculate month days */
|
||||||
|
|
||||||
if (byMonthDay)
|
if (byMonthDay)
|
||||||
{
|
{
|
||||||
#if HEAVY_DEBUG
|
NGMonthDaySet_fillWithByMonthDay (&byPositiveMonthDaySet, &byNegativeMonthDaySet, byMonthDay);
|
||||||
NSLog (@"byMonthDay: %@", byMonthDay);
|
|
||||||
#endif
|
|
||||||
NGMonthDaySet_fillWithByMonthDay (&byMonthDaySet, byMonthDay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (repeatCount > 0)
|
||||||
// TODO: I think the 'diff' is to skip recurrence which are before the
|
{
|
||||||
// requested range. Not sure whether this is actually possible, eg
|
numberOfMonthsInRange = [eventStartDate monthsBetweenDate: rEnd] + 1;
|
||||||
// the repeatCount must be processed from the start.
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
diff = [eventStartDate monthsBetweenDate: rStart];
|
diff = [eventStartDate monthsBetweenDate: rStart];
|
||||||
if ((diff != 0) && [rStart compare: eventStartDate] == NSOrderedAscending)
|
if ((diff != 0) && [rStart compare: eventStartDate] == NSOrderedAscending)
|
||||||
diff = -diff;
|
diff = -diff;
|
||||||
|
|
||||||
numberOfMonthsInRange = [rStart monthsBetweenDate: rEnd] + 1;
|
numberOfMonthsInRange = [rStart monthsBetweenDate: rEnd] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
ranges = [NSMutableArray arrayWithCapacity: numberOfMonthsInRange];
|
ranges = [NSMutableArray arrayWithCapacity: numberOfMonthsInRange];
|
||||||
|
|
||||||
/*
|
// There's a bug in GNUstep in [NSCalendarDate dateByAddingYears:months:days:]
|
||||||
Note: we do not add 'eventStartDate', this is intentional, the event date
|
// that causes errors when adding subsequently a month. For this reason,
|
||||||
itself is _not_ necessarily part of the sequence, eg with monthly
|
// we set the day of the reference date to 1.
|
||||||
byday recurrences.
|
referenceDate = [NSCalendarDate dateWithYear: [eventStartDate yearOfCommonEra]
|
||||||
*/
|
month: [eventStartDate monthOfYear]
|
||||||
|
day: 1
|
||||||
|
hour: [eventStartDate hourOfDay]
|
||||||
|
minute: [eventStartDate minuteOfHour]
|
||||||
|
second: 0
|
||||||
|
timeZone: [eventStartDate timeZone]];
|
||||||
|
|
||||||
for (monthIdxInRange = 0; monthIdxInRange < numberOfMonthsInRange;
|
for (monthIdxInRange = 0, count = 0;
|
||||||
|
monthIdxInRange < numberOfMonthsInRange;
|
||||||
monthIdxInRange++)
|
monthIdxInRange++)
|
||||||
{
|
{
|
||||||
NSCalendarDate *cursor;
|
NSCalendarDate *cursor;
|
||||||
|
@ -349,63 +304,95 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
||||||
if ((monthIdxInRecurrence % interval) != 0)
|
if ((monthIdxInRecurrence % interval) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
cursor = [referenceDate dateByAddingYears: 0
|
||||||
Then the sequence is:
|
months: monthIdxInRecurrence
|
||||||
- check whether the month is in the BYMONTH list
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Note: the function below adds exactly a month, eg:
|
|
||||||
2007-01-30 + 1month => 2007-02-*28*!!
|
|
||||||
*/
|
|
||||||
cursor = [eventStartDate dateByAddingYears: 0
|
|
||||||
months: (diff + monthIdxInRange)
|
|
||||||
days: 0];
|
days: 0];
|
||||||
[cursor setTimeZone: timeZone];
|
[cursor setTimeZone: timeZone];
|
||||||
numDaysInMonth = [cursor numberOfDaysInMonth];
|
numDaysInMonth = [cursor numberOfDaysInMonth];
|
||||||
|
|
||||||
|
/* check whether we match the BYMONTH constraint */
|
||||||
/* check whether we match the bymonth specification */
|
|
||||||
|
|
||||||
if (!byMonthList[[cursor monthOfYear] - 1])
|
if (!byMonthList[[cursor monthOfYear] - 1])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* check whether we match the BYMONTHDAY and BYDAY constraints */
|
||||||
/* check 'day level' byXYZ rules */
|
|
||||||
|
|
||||||
didByFill = NO;
|
didByFill = NO;
|
||||||
|
|
||||||
if (byMonthDay)
|
if (byMonthDay)
|
||||||
{ /* list of days in the month */
|
{
|
||||||
NGMonthDaySet_copyOrUnion (&monthDays, &byMonthDaySet, !didByFill);
|
// 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;
|
||||||
didByFill = YES;
|
didByFill = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([rrule byDayMask] != 0)
|
if (byDayMask)
|
||||||
{ // TODO: replace the mask with an array
|
{
|
||||||
NGMonthDaySet ruleset;
|
unsigned int firstDoWInMonth, currentWeekDay;
|
||||||
unsigned firstDoWInMonth;
|
unsigned int weekDaysCount[7], currentWeekDaysCount[7];
|
||||||
|
int i, positiveOrder, negativeOrder;
|
||||||
|
|
||||||
firstDoWInMonth = [[cursor firstDayOfMonth] dayOfWeek];
|
firstDoWInMonth = [[cursor firstDayOfMonth] dayOfWeek];
|
||||||
|
|
||||||
NGMonthDaySet_fillWithByDayX (&ruleset,
|
if (!didByFill)
|
||||||
[rrule byDayMask],
|
NGMonthDaySet_clear (&monthDays);
|
||||||
firstDoWInMonth,
|
|
||||||
[cursor numberOfDaysInMonth],
|
// Fill weekDaysCount to handle negative positions
|
||||||
[rrule byDayOccurence1]);
|
currentWeekDay = firstDoWInMonth;
|
||||||
NGMonthDaySet_copyOrUnion (&monthDays, &ruleset, !didByFill);
|
memset(weekDaysCount, 0, 7 * sizeof(unsigned int));
|
||||||
|
for (i = 1; i <= numDaysInMonth; i++)
|
||||||
|
{
|
||||||
|
weekDaysCount[currentWeekDay]++;
|
||||||
|
currentWeekDay++;
|
||||||
|
currentWeekDay = fmod (currentWeekDay, 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 = fmod (currentWeekDay, 7);
|
||||||
|
}
|
||||||
didByFill = YES;
|
didByFill = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!didByFill)
|
if (didByFill)
|
||||||
{
|
{
|
||||||
/* no rules applied, take the dayOfMonth of the startDate */
|
if (diff + monthIdxInRange == 0)
|
||||||
|
{
|
||||||
|
// When dealing with the month of the first occurence, remove days
|
||||||
|
// that occur before the first occurrence.
|
||||||
|
int i;
|
||||||
|
for (i = 1; i < eventDayOfMonth; i++)
|
||||||
|
monthDays[i] = NO;
|
||||||
|
// The first occurrence must always be included.
|
||||||
|
monthDays[i] = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No rules applied, take the dayOfMonth of the startDate
|
||||||
NGMonthDaySet_clear (&monthDays);
|
NGMonthDaySet_clear (&monthDays);
|
||||||
monthDays[eventDayOfMonth] = YES;
|
monthDays[eventDayOfMonth] = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add processing of byhour/byminute/bysecond etc
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Next step is to create NSCalendarDate instances from our 'monthDays'
|
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 walk over each day of the 'monthDays' set. If its flag isn't
|
||||||
|
@ -427,57 +414,18 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
||||||
if (!monthDays[dom])
|
if (!monthDays[dom])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// TODO: what is this good for?
|
start = [cursor dateByAddingYears: 0 months: 0 days: (dom - 1)];
|
||||||
/*
|
|
||||||
Here we need to correct the date. Remember that the startdate given in
|
|
||||||
the event is not necessarily a date of the sequence!
|
|
||||||
|
|
||||||
The 'numDaysInMonth' localvar contains the number of days in the
|
|
||||||
current month (eg 31 for January, 28 for most February's, etc)
|
|
||||||
|
|
||||||
Eg: MONTHLY;BYDAY=-1WE (last wednesday, every month)
|
|
||||||
|
|
||||||
cursor: 2007-01-30 (eventDayOfMonth = 30)
|
|
||||||
=>start: 2007-01-31 (dom = 31)
|
|
||||||
cursor: 2007-02-28 (eventDayOfMonth = 30)
|
|
||||||
=>start: 2007-02-28 (dom = 28)
|
|
||||||
|
|
||||||
Note: in case the cursor already had an event-day overflow, that is the
|
|
||||||
'eventDayOfMonth' is bigger than the 'numDaysInMonth', the cursor
|
|
||||||
will already be corrected!
|
|
||||||
Eg:
|
|
||||||
start was: 2007-01-30
|
|
||||||
cursor will be: 2007-02-28
|
|
||||||
*/
|
|
||||||
if (eventDayOfMonth == dom)
|
|
||||||
{
|
|
||||||
start = cursor;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int maxDay =
|
|
||||||
eventDayOfMonth > numDaysInMonth ? numDaysInMonth : eventDayOfMonth;
|
|
||||||
|
|
||||||
start = [cursor dateByAddingYears: 0 months: 0 days: (dom - maxDay)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Setup for 2007-02-28, MONTHLY;BYDAY=-1WE.
|
|
||||||
dom: 28
|
|
||||||
eventDayOfMonth: 31
|
|
||||||
cursor: 2007-02-28
|
|
||||||
start: 2007-02-25 <== WRONG
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if HEAVY_DEBUG
|
|
||||||
NSLog (@"DOM %i EDOM %i NUMDAYS %i START: %@ CURSOR: %@",
|
|
||||||
dom, eventDayOfMonth, numDaysInMonth,
|
|
||||||
start, cursor);
|
|
||||||
#endif
|
|
||||||
doCont = [self _addInstanceWithStartDate: start
|
doCont = [self _addInstanceWithStartDate: start
|
||||||
limitDate: until
|
limitDate: until
|
||||||
limitRange: _r
|
limitRange: _r
|
||||||
toArray: ranges];
|
toArray: ranges];
|
||||||
|
//NSLog(@"*** MONTHLY [%i/%i] adding %@%@ (count = %i)", dom, numDaysInMonth, start, (doCont?@"":@" .. NOT!"), count);
|
||||||
|
if (repeatCount > 0)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
//NSLog(@"MONTHLY count = %i/%i", count, repeatCount);
|
||||||
|
doCont = (count < repeatCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!doCont) break; /* reached some limit */
|
if (!doCont) break; /* reached some limit */
|
||||||
}
|
}
|
||||||
|
@ -487,16 +435,31 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
||||||
- (NSCalendarDate *) lastInstanceStartDate
|
- (NSCalendarDate *) lastInstanceStartDate
|
||||||
{
|
{
|
||||||
NSCalendarDate *firStart, *lastInstanceStartDate;
|
NSCalendarDate *firStart, *lastInstanceStartDate;
|
||||||
|
NGCalendarDateRange *r;
|
||||||
|
NSArray *instances;
|
||||||
|
|
||||||
|
lastInstanceStartDate = nil;
|
||||||
if ([rrule repeatCount] > 0)
|
if ([rrule repeatCount] > 0)
|
||||||
{
|
{
|
||||||
|
if ([rrule hasByMask])
|
||||||
|
{
|
||||||
|
// Must perform the complete calculation
|
||||||
firStart = [firstRange startDate];
|
firStart = [firstRange startDate];
|
||||||
|
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
|
lastInstanceStartDate = [firStart dateByAddingYears: 0
|
||||||
months: ([rrule repeatInterval]
|
months: ([rrule repeatInterval]
|
||||||
* ([rrule repeatCount] - 1))
|
* ([rrule repeatCount] - 1))
|
||||||
days: 0];
|
days: 0];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
lastInstanceStartDate = [super lastInstanceStartDate];
|
lastInstanceStartDate = [super lastInstanceStartDate];
|
||||||
|
|
||||||
|
|
|
@ -340,7 +340,6 @@ static Class yearlyCalcClass = Nil;
|
||||||
/*
|
/*
|
||||||
NOTE: this is horribly inaccurate and doesn't even consider the use
|
NOTE: this is horribly inaccurate and doesn't even consider the use
|
||||||
of repeatCount. It MUST be implemented by subclasses properly!
|
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.
|
|
||||||
*/
|
*/
|
||||||
return [rrule untilDate];
|
return [rrule untilDate];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
|
Copyright (C) 2006-2010 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOPE.
|
This file is part of SOPE.
|
||||||
|
|
||||||
|
@ -46,34 +47,25 @@ typedef enum {
|
||||||
} iCalRecurrenceFrequency;
|
} iCalRecurrenceFrequency;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
iCalWeekDaySunday = 1,
|
iCalWeekDayUnknown = -1,
|
||||||
iCalWeekDayMonday = 2,
|
iCalWeekDaySunday = 0,
|
||||||
iCalWeekDayTuesday = 4,
|
iCalWeekDayMonday = 1,
|
||||||
iCalWeekDayWednesday = 8,
|
iCalWeekDayTuesday = 2,
|
||||||
iCalWeekDayThursday = 16,
|
iCalWeekDayWednesday = 3,
|
||||||
iCalWeekDayFriday = 32,
|
iCalWeekDayThursday = 4,
|
||||||
iCalWeekDaySaturday = 64,
|
iCalWeekDayFriday = 5,
|
||||||
|
iCalWeekDaySaturday = 6
|
||||||
} iCalWeekDay;
|
} iCalWeekDay;
|
||||||
|
|
||||||
|
extern NSString *iCalWeekDayString[];
|
||||||
|
|
||||||
@class NSString, NSCalendarDate, NGCalendarDateRange, NSArray;
|
@class NSString, NSCalendarDate, NGCalendarDateRange, NSArray;
|
||||||
|
@class iCalByDayMask;
|
||||||
|
|
||||||
@interface iCalRecurrenceRule : CardElement
|
@interface iCalRecurrenceRule : CardElement
|
||||||
// {
|
{
|
||||||
// iCalRecurrenceFrequency frequency;
|
iCalByDayMask *dayMask;
|
||||||
// int interval;
|
}
|
||||||
// unsigned repeatCount;
|
|
||||||
// NSCalendarDate *untilDate;
|
|
||||||
// struct {
|
|
||||||
// unsigned weekStart: 7;
|
|
||||||
// unsigned mask: 7;
|
|
||||||
// unsigned useOccurence:1;
|
|
||||||
// unsigned reserved:1;
|
|
||||||
// } byDay;
|
|
||||||
// int byDayOccurence1;
|
|
||||||
// NSArray *byMonthDay;
|
|
||||||
|
|
||||||
// NSString *rrule;
|
|
||||||
// }
|
|
||||||
|
|
||||||
+ (id) recurrenceRuleWithICalRepresentation: (NSString *) _iCalRep;
|
+ (id) recurrenceRuleWithICalRepresentation: (NSString *) _iCalRep;
|
||||||
- (id) initWithString: (NSString *) _str;
|
- (id) initWithString: (NSString *) _str;
|
||||||
|
@ -92,11 +84,13 @@ typedef enum {
|
||||||
- (void) setWeekStart: (iCalWeekDay) _weekStart;
|
- (void) setWeekStart: (iCalWeekDay) _weekStart;
|
||||||
- (iCalWeekDay) weekStart;
|
- (iCalWeekDay) weekStart;
|
||||||
|
|
||||||
- (void) setByDayMask: (unsigned int) _mask;
|
- (void) setByDay: (NSString *) newByDay;
|
||||||
- (unsigned int) byDayMask;
|
- (NSString *) byDay;
|
||||||
- (int) byDayOccurence1;
|
- (void) setByDayMask: (iCalByDayMask *) newMask;
|
||||||
|
- (iCalByDayMask *) byDayMask;
|
||||||
- (NSArray *) byMonthDay;
|
- (NSArray *) byMonthDay;
|
||||||
|
- (NSArray *) byMonth;
|
||||||
|
- (BOOL) hasByMask;
|
||||||
|
|
||||||
/* count and untilDate are mutually exclusive */
|
/* count and untilDate are mutually exclusive */
|
||||||
|
|
||||||
|
@ -112,8 +106,6 @@ typedef enum {
|
||||||
|
|
||||||
- (void) setRrule: (NSString *) _rrule; // TODO: weird name? (better: RRule?)
|
- (void) setRrule: (NSString *) _rrule; // TODO: weird name? (better: RRule?)
|
||||||
|
|
||||||
// - (NSString *)iCalRepresentation;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#endif /* __NGiCal_iCalRecurrenceRule_H_ */
|
#endif /* __NGiCal_iCalRecurrenceRule_H_ */
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
|
Copyright (C) 2006-2010 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOPE.
|
This file is part of SOPE.
|
||||||
|
|
||||||
|
@ -19,6 +20,168 @@
|
||||||
02111-1307, USA.
|
02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
See http://tools.ietf.org/html/rfc2445#section-4.3.10
|
||||||
|
|
||||||
|
4.3.10 Recurrence Rule
|
||||||
|
|
||||||
|
Value Name: RECUR
|
||||||
|
|
||||||
|
Purpose: This value type is used to identify properties that contain
|
||||||
|
a recurrence rule specification.
|
||||||
|
|
||||||
|
Formal Definition: The value type is defined by the following
|
||||||
|
notation:
|
||||||
|
|
||||||
|
recur = "FREQ"=freq *(
|
||||||
|
|
||||||
|
; either UNTIL or COUNT may appear in a 'recur',
|
||||||
|
; but UNTIL and COUNT MUST NOT occur in the same 'recur'
|
||||||
|
|
||||||
|
( ";" "UNTIL" "=" enddate ) /
|
||||||
|
( ";" "COUNT" "=" 1*DIGIT ) /
|
||||||
|
|
||||||
|
; the rest of these keywords are optional,
|
||||||
|
; but MUST NOT occur more than once
|
||||||
|
|
||||||
|
( ";" "INTERVAL" "=" 1*DIGIT ) /
|
||||||
|
( ";" "BYSECOND" "=" byseclist ) /
|
||||||
|
( ";" "BYMINUTE" "=" byminlist ) /
|
||||||
|
( ";" "BYHOUR" "=" byhrlist ) /
|
||||||
|
( ";" "BYDAY" "=" bywdaylist ) /
|
||||||
|
( ";" "BYMONTHDAY" "=" bymodaylist ) /
|
||||||
|
( ";" "BYYEARDAY" "=" byyrdaylist ) /
|
||||||
|
( ";" "BYWEEKNO" "=" bywknolist ) /
|
||||||
|
( ";" "BYMONTH" "=" bymolist ) /
|
||||||
|
( ";" "BYSETPOS" "=" bysplist ) /
|
||||||
|
( ";" "WKST" "=" weekday ) /
|
||||||
|
( ";" x-name "=" text )
|
||||||
|
)
|
||||||
|
|
||||||
|
freq = "SECONDLY" / "MINUTELY" / "HOURLY" / "DAILY"
|
||||||
|
/ "WEEKLY" / "MONTHLY" / "YEARLY"
|
||||||
|
|
||||||
|
enddate = date
|
||||||
|
enddate =/ date-time ;An UTC value
|
||||||
|
|
||||||
|
byseclist = seconds / ( seconds *("," seconds) )
|
||||||
|
|
||||||
|
seconds = 1DIGIT / 2DIGIT ;0 to 59
|
||||||
|
|
||||||
|
byminlist = minutes / ( minutes *("," minutes) )
|
||||||
|
|
||||||
|
minutes = 1DIGIT / 2DIGIT ;0 to 59
|
||||||
|
|
||||||
|
byhrlist = hour / ( hour *("," hour) )
|
||||||
|
|
||||||
|
hour = 1DIGIT / 2DIGIT ;0 to 23
|
||||||
|
|
||||||
|
bywdaylist = weekdaynum / ( weekdaynum *("," weekdaynum) )
|
||||||
|
|
||||||
|
weekdaynum = [([plus] ordwk / minus ordwk)] weekday
|
||||||
|
|
||||||
|
plus = "+"
|
||||||
|
|
||||||
|
minus = "-"
|
||||||
|
|
||||||
|
ordwk = 1DIGIT / 2DIGIT ;1 to 53
|
||||||
|
|
||||||
|
weekday = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA"
|
||||||
|
;Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
|
||||||
|
;FRIDAY, SATURDAY and SUNDAY days of the week.
|
||||||
|
|
||||||
|
bymodaylist = monthdaynum / ( monthdaynum *("," monthdaynum) )
|
||||||
|
|
||||||
|
monthdaynum = ([plus] ordmoday) / (minus ordmoday)
|
||||||
|
|
||||||
|
ordmoday = 1DIGIT / 2DIGIT ;1 to 31
|
||||||
|
|
||||||
|
byyrdaylist = yeardaynum / ( yeardaynum *("," yeardaynum) )
|
||||||
|
|
||||||
|
yeardaynum = ([plus] ordyrday) / (minus ordyrday)
|
||||||
|
|
||||||
|
ordyrday = 1DIGIT / 2DIGIT / 3DIGIT ;1 to 366
|
||||||
|
|
||||||
|
bywknolist = weeknum / ( weeknum *("," weeknum) )
|
||||||
|
|
||||||
|
weeknum = ([plus] ordwk) / (minus ordwk)
|
||||||
|
|
||||||
|
bymolist = monthnum / ( monthnum *("," monthnum) )
|
||||||
|
|
||||||
|
monthnum = 1DIGIT / 2DIGIT ;1 to 12
|
||||||
|
|
||||||
|
bysplist = setposday / ( setposday *("," setposday) )
|
||||||
|
|
||||||
|
setposday = yeardaynum
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Examples :
|
||||||
|
|
||||||
|
Every other week on Monday, Wednesday and Friday until December 24,
|
||||||
|
1997, but starting on Tuesday, September 2, 1997:
|
||||||
|
|
||||||
|
DTSTART;TZID=US-Eastern:19970902T090000
|
||||||
|
RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;
|
||||||
|
BYDAY=MO,WE,FR
|
||||||
|
==> (1997 9:00 AM EDT)September 2,3,5,15,17,19,29;October
|
||||||
|
1,3,13,15,17
|
||||||
|
(1997 9:00 AM EST)October 27,29,31;November 10,12,14,24,26,28;
|
||||||
|
December 8,10,12,22
|
||||||
|
|
||||||
|
Monthly on the 1st Friday for ten occurrences:
|
||||||
|
|
||||||
|
DTSTART;TZID=US-Eastern:19970905T090000
|
||||||
|
RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR
|
||||||
|
|
||||||
|
==> (1997 9:00 AM EDT)September 5;October 3
|
||||||
|
(1997 9:00 AM EST)November 7;Dec 5
|
||||||
|
(1998 9:00 AM EST)January 2;February 6;March 6;April 3
|
||||||
|
(1998 9:00 AM EDT)May 1;June 5
|
||||||
|
|
||||||
|
Every other month on the 1st and last Sunday of the month for 10
|
||||||
|
occurrences:
|
||||||
|
|
||||||
|
DTSTART;TZID=US-Eastern:19970907T090000
|
||||||
|
RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
|
||||||
|
|
||||||
|
==> (1997 9:00 AM EDT)September 7,28
|
||||||
|
(1997 9:00 AM EST)November 2,30
|
||||||
|
|
||||||
|
Monthly on the third to the last day of the month, forever:
|
||||||
|
|
||||||
|
DTSTART;TZID=US-Eastern:19970928T090000
|
||||||
|
RRULE:FREQ=MONTHLY;BYMONTHDAY=-3
|
||||||
|
|
||||||
|
==> (1997 9:00 AM EDT)September 28
|
||||||
|
(1997 9:00 AM EST)October 29;November 28;December 29
|
||||||
|
(1998 9:00 AM EST)January 29;February 26
|
||||||
|
...
|
||||||
|
|
||||||
|
Every other year on January, February, and March for 10 occurrences:
|
||||||
|
|
||||||
|
DTSTART;TZID=US-Eastern:19970310T090000
|
||||||
|
RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3
|
||||||
|
|
||||||
|
==> (1997 9:00 AM EST)March 10
|
||||||
|
(1999 9:00 AM EST)January 10;February 10;March 10
|
||||||
|
(2001 9:00 AM EST)January 10;February 10;March 10
|
||||||
|
(2003 9:00 AM EST)January 10;February 10;March 10
|
||||||
|
|
||||||
|
Everyday in January, for 3 years:
|
||||||
|
|
||||||
|
DTSTART;TZID=US-Eastern:19980101T090000
|
||||||
|
RRULE:FREQ=YEARLY;UNTIL=20000131T090000Z;
|
||||||
|
BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA
|
||||||
|
or
|
||||||
|
RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1
|
||||||
|
|
||||||
|
==> (1998 9:00 AM EDT)January 1-31
|
||||||
|
(1999 9:00 AM EDT)January 1-31
|
||||||
|
(2000 9:00 AM EDT)January 1-31
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#import <Foundation/NSArray.h>
|
#import <Foundation/NSArray.h>
|
||||||
#import <Foundation/NSEnumerator.h>
|
#import <Foundation/NSEnumerator.h>
|
||||||
#import <Foundation/NSException.h>
|
#import <Foundation/NSException.h>
|
||||||
|
@ -27,13 +190,16 @@
|
||||||
|
|
||||||
#import <ctype.h>
|
#import <ctype.h>
|
||||||
|
|
||||||
|
#import "NSCalendarDate+ICal.h"
|
||||||
#import "NSCalendarDate+NGCards.h"
|
#import "NSCalendarDate+NGCards.h"
|
||||||
#import "NSString+NGCards.h"
|
#import "NSString+NGCards.h"
|
||||||
|
|
||||||
#import "NSCalendarDate+ICal.h"
|
#import "iCalByDayMask.h"
|
||||||
|
|
||||||
#import "iCalRecurrenceRule.h"
|
#import "iCalRecurrenceRule.h"
|
||||||
|
|
||||||
|
NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR",
|
||||||
|
@"SA" };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
freq = rrFreq;
|
freq = rrFreq;
|
||||||
until = rrUntil;
|
until = rrUntil;
|
||||||
|
@ -60,11 +226,6 @@
|
||||||
- (NSString *) wkst;
|
- (NSString *) wkst;
|
||||||
- (NSString *) byDayList;
|
- (NSString *) byDayList;
|
||||||
|
|
||||||
// - (void)_parseRuleString:(NSString *)_rrule;
|
|
||||||
|
|
||||||
/* currently used by parser, should be removed (replace with an -init..) */
|
|
||||||
- (void)setByday:(NSString *)_byDayList;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation iCalRecurrenceRule
|
@implementation iCalRecurrenceRule
|
||||||
|
@ -85,6 +246,7 @@
|
||||||
if ((self = [super init]) != nil)
|
if ((self = [super init]) != nil)
|
||||||
{
|
{
|
||||||
[self setTag: @"rrule"];
|
[self setTag: @"rrule"];
|
||||||
|
dayMask = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -100,6 +262,12 @@
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) dealloc
|
||||||
|
{
|
||||||
|
[dayMask release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) setRrule: (NSString *) _rrule
|
- (void) setRrule: (NSString *) _rrule
|
||||||
{
|
{
|
||||||
NSEnumerator *newValues;
|
NSEnumerator *newValues;
|
||||||
|
@ -273,54 +441,30 @@
|
||||||
return [self weekDayFromICalRepresentation: [self wkst]];
|
return [self weekDayFromICalRepresentation: [self wkst]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setByDayMask: (unsigned) _mask
|
- (void) setByDay: (NSString *) newByDay
|
||||||
{
|
{
|
||||||
NSMutableArray *days;
|
[self setNamedValue: @"byday" to: newByDay];
|
||||||
unsigned int count;
|
|
||||||
unsigned char maskDays[] = { iCalWeekDaySunday, iCalWeekDayMonday,
|
|
||||||
iCalWeekDayTuesday, iCalWeekDayWednesday,
|
|
||||||
iCalWeekDayThursday, iCalWeekDayFriday,
|
|
||||||
iCalWeekDaySaturday };
|
|
||||||
days = [NSMutableArray arrayWithCapacity: 7];
|
|
||||||
if (_mask)
|
|
||||||
{
|
|
||||||
for (count = 0; count < 7; count++)
|
|
||||||
if (_mask & maskDays[count])
|
|
||||||
[days addObject:
|
|
||||||
[self iCalRepresentationForWeekDay: maskDays[count]]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setNamedValue: @"byday" to: [days componentsJoinedByString: @","]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned int) byDayMask
|
- (NSString *) byDay
|
||||||
{
|
{
|
||||||
NSArray *days;
|
return [self namedValue: @"byday"];
|
||||||
unsigned int mask, count, max;
|
|
||||||
NSString *day, *value;
|
|
||||||
|
|
||||||
mask = 0;
|
|
||||||
|
|
||||||
value = [self namedValue: @"byday"];
|
|
||||||
if ([value length] > 0)
|
|
||||||
{
|
|
||||||
days = [value componentsSeparatedByString: @","];
|
|
||||||
max = [days count];
|
|
||||||
for (count = 0; count < max; count++)
|
|
||||||
{
|
|
||||||
day = [days objectAtIndex: count];
|
|
||||||
mask |= [self weekDayFromICalRepresentation: day];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#warning this is bad
|
- (void) setByDayMask: (iCalByDayMask *) newByDayMask
|
||||||
- (int) byDayOccurence1
|
|
||||||
{
|
{
|
||||||
return 0;
|
[self setByDay: [newByDayMask asRuleString]];
|
||||||
// return byDayOccurence1;
|
}
|
||||||
|
|
||||||
|
- (iCalByDayMask *) byDayMask
|
||||||
|
{
|
||||||
|
if (dayMask == nil && [[self byDay] length])
|
||||||
|
{
|
||||||
|
dayMask = [iCalByDayMask byDayMaskWithRuleString: [self byDay]];
|
||||||
|
[dayMask retain];
|
||||||
|
}
|
||||||
|
|
||||||
|
return dayMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *) byMonthDay
|
- (NSArray *) byMonthDay
|
||||||
|
@ -337,6 +481,35 @@
|
||||||
return byMonthDay;
|
return byMonthDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray *) byMonth
|
||||||
|
{
|
||||||
|
NSArray *byMonth;
|
||||||
|
NSString *byMonthStr;
|
||||||
|
|
||||||
|
byMonthStr = [self namedValue: @"bymonth"];
|
||||||
|
if ([byMonthStr length])
|
||||||
|
byMonth = [byMonthStr componentsSeparatedByString: @","];
|
||||||
|
else
|
||||||
|
byMonth = nil;
|
||||||
|
|
||||||
|
return byMonth;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) hasByMask
|
||||||
|
{
|
||||||
|
/* There are more BYxxx rule parts but we don't support them yet :
|
||||||
|
* - BYYEARDAY
|
||||||
|
* - BYWEEKNO
|
||||||
|
* - BYHOUR
|
||||||
|
* - BYMINUTE
|
||||||
|
* - BYSECOND
|
||||||
|
* - BYSETPOS
|
||||||
|
*/
|
||||||
|
return ([[self namedValue: @"bymonthday"] length] ||
|
||||||
|
[[self namedValue: @"byday"] length] ||
|
||||||
|
[[self namedValue: @"bymonth"] length]);
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL) isInfinite
|
- (BOOL) isInfinite
|
||||||
{
|
{
|
||||||
return !([self repeatCount] || [self untilDate]);
|
return !([self repeatCount] || [self untilDate]);
|
||||||
|
@ -356,6 +529,7 @@
|
||||||
dayLength = [_day length];
|
dayLength = [_day length];
|
||||||
if (dayLength > 1)
|
if (dayLength > 1)
|
||||||
{
|
{
|
||||||
|
// Ignore any prefix, only consider last two characters
|
||||||
[[_day uppercaseString] getCharacters: chars
|
[[_day uppercaseString] getCharacters: chars
|
||||||
range: NSMakeRange (dayLength - 2, 2)];
|
range: NSMakeRange (dayLength - 2, 2)];
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#import "iCalDateTime.h"
|
#import "iCalDateTime.h"
|
||||||
#import "iCalRecurrenceRule.h"
|
#import "iCalRecurrenceRule.h"
|
||||||
|
#import "iCalByDayMask.h"
|
||||||
|
|
||||||
#import "iCalTimeZonePeriod.h"
|
#import "iCalTimeZonePeriod.h"
|
||||||
|
|
||||||
|
@ -88,16 +89,16 @@
|
||||||
return ((negative) ? -seconds : seconds);
|
return ((negative) ? -seconds : seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned int) dayOfWeekFromRruleDay: (iCalWeekDay) day
|
// - (unsigned int) dayOfWeekFromRruleDay: (iCalWeekDay) day
|
||||||
{
|
// {
|
||||||
unsigned int dayOfWeek;
|
// unsigned int dayOfWeek;
|
||||||
|
|
||||||
dayOfWeek = 0;
|
// dayOfWeek = 0;
|
||||||
while (day >> (dayOfWeek + 1))
|
// while (day >> (dayOfWeek + 1))
|
||||||
dayOfWeek++;
|
// dayOfWeek++;
|
||||||
|
|
||||||
return dayOfWeek;
|
// return dayOfWeek;
|
||||||
}
|
// }
|
||||||
|
|
||||||
- (NSCalendarDate *) startDate
|
- (NSCalendarDate *) startDate
|
||||||
{
|
{
|
||||||
|
@ -105,22 +106,33 @@
|
||||||
dateTime];
|
dateTime];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This method returns the date corresponding for to the start of the period
|
/**
|
||||||
in the year of the reference date. */
|
* This method returns the date corresponding for to the start of the period
|
||||||
|
* in the year of the reference date.
|
||||||
|
* We assume that a RRULE for a timezone will always be YEARLY with a BYMONTH
|
||||||
|
* and a BYDAY rule.
|
||||||
|
*/
|
||||||
- (NSCalendarDate *) _occurenceForDate: (NSCalendarDate *) refDate
|
- (NSCalendarDate *) _occurenceForDate: (NSCalendarDate *) refDate
|
||||||
byRRule: (iCalRecurrenceRule *) rrule
|
byRRule: (iCalRecurrenceRule *) rrule
|
||||||
{
|
{
|
||||||
NSCalendarDate *tmpDate;
|
NSCalendarDate *tmpDate;
|
||||||
NSString *byDay;
|
iCalByDayMask *byDayMask;
|
||||||
int dayOfWeek, dateDayOfWeek, offset, pos;
|
int dayOfWeek, dateDayOfWeek, offset, pos;
|
||||||
NSCalendarDate *tzStart;
|
NSCalendarDate *tzStart;
|
||||||
|
|
||||||
byDay = [rrule namedValue: @"byday"];
|
byDayMask = [rrule byDayMask];
|
||||||
dayOfWeek = [self dayOfWeekFromRruleDay: [rrule byDayMask]];
|
dayOfWeek = 0;
|
||||||
pos = [[byDay substringToIndex: 2] intValue];
|
|
||||||
if (!pos)
|
if (byDayMask == nil)
|
||||||
/* if byday = "SU", instead of "1SU"... */
|
{
|
||||||
|
dayOfWeek = 0;
|
||||||
pos = 1;
|
pos = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dayOfWeek = (int)[byDayMask firstDay];
|
||||||
|
pos = [byDayMask firstOccurrence];
|
||||||
|
}
|
||||||
|
|
||||||
tzStart = [self startDate];
|
tzStart = [self startDate];
|
||||||
[tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
|
[tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
|
Copyright (C) 2006-2010 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOPE.
|
This file is part of SOPE.
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
|
|
||||||
#import <NGExtensions/NGCalendarDateRange.h>
|
#import <NGExtensions/NGCalendarDateRange.h>
|
||||||
#import "iCalRecurrenceRule.h"
|
#import "iCalRecurrenceRule.h"
|
||||||
|
#import "iCalByDayMask.h"
|
||||||
#import "NSCalendarDate+ICal.h"
|
#import "NSCalendarDate+ICal.h"
|
||||||
|
|
||||||
@interface iCalRecurrenceCalculator (PrivateAPI)
|
@interface iCalRecurrenceCalculator (PrivateAPI)
|
||||||
|
@ -42,22 +44,36 @@
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: If BYDAY is specified, lastInstanceStartDate and recurrences will
|
|
||||||
differ significantly!
|
|
||||||
*/
|
|
||||||
@implementation iCalWeeklyRecurrenceCalculator
|
@implementation iCalWeeklyRecurrenceCalculator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO : Unsupported conditions for WEEKLY recurrences :
|
||||||
|
*
|
||||||
|
* BYYEAR
|
||||||
|
* BYYEARDAY
|
||||||
|
* BYWEEKNO
|
||||||
|
* BYMONTH
|
||||||
|
* BYMONTHDAY
|
||||||
|
* BYHOUR
|
||||||
|
* BYMINUTE
|
||||||
|
* WKST
|
||||||
|
*
|
||||||
|
* There's no GUI to defined such conditions, so there's no
|
||||||
|
* problem for now.
|
||||||
|
*/
|
||||||
- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||||
{
|
{
|
||||||
NSMutableArray *ranges;
|
NSMutableArray *ranges;
|
||||||
NSCalendarDate *firStart, *startDate, *endDate, *currentStartDate, *currentEndDate;
|
NSCalendarDate *firStart, *startDate, *endDate, *currentStartDate, *currentEndDate;
|
||||||
long i;
|
long i, repeatCount, count;
|
||||||
unsigned interval, byDayMask;
|
unsigned interval;
|
||||||
|
iCalByDayMask *dayMask;
|
||||||
|
|
||||||
firStart = [firstRange startDate];
|
firStart = [firstRange startDate];
|
||||||
startDate = [_r startDate];
|
startDate = [_r startDate];
|
||||||
endDate = [_r endDate];
|
endDate = [_r endDate];
|
||||||
|
dayMask = nil;
|
||||||
|
repeatCount = 0;
|
||||||
|
|
||||||
if ([endDate compare: firStart] == NSOrderedAscending)
|
if ([endDate compare: firStart] == NSOrderedAscending)
|
||||||
// Range ends before first occurrence
|
// Range ends before first occurrence
|
||||||
|
@ -65,19 +81,31 @@
|
||||||
|
|
||||||
interval = [rrule repeatInterval];
|
interval = [rrule repeatInterval];
|
||||||
|
|
||||||
|
if ([[rrule byDay] length])
|
||||||
|
dayMask = [rrule byDayMask];
|
||||||
|
|
||||||
// If rule is bound, check the bounds
|
// If rule is bound, check the bounds
|
||||||
if (![rrule isInfinite])
|
if (![rrule isInfinite])
|
||||||
{
|
{
|
||||||
NSCalendarDate *until, *lastDate;
|
NSCalendarDate *until, *lastDate;
|
||||||
|
|
||||||
|
lastDate = nil;
|
||||||
until = [rrule untilDate];
|
until = [rrule untilDate];
|
||||||
if (until)
|
if (until)
|
||||||
lastDate = until;
|
lastDate = until;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
repeatCount = [rrule repeatCount];
|
||||||
|
if (dayMask == nil)
|
||||||
|
// When there's no BYxxx mask, we can find the date of the last
|
||||||
|
// occurrence.
|
||||||
lastDate = [firStart dateByAddingYears: 0 months: 0
|
lastDate = [firStart dateByAddingYears: 0 months: 0
|
||||||
days: (interval
|
days: (interval
|
||||||
* ([rrule repeatCount] - 1) * 7)];
|
* (repeatCount - 1) * 7)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastDate != nil)
|
||||||
|
{
|
||||||
if ([lastDate compare: startDate] == NSOrderedAscending)
|
if ([lastDate compare: startDate] == NSOrderedAscending)
|
||||||
// Range starts after last occurrence
|
// Range starts after last occurrence
|
||||||
return nil;
|
return nil;
|
||||||
|
@ -85,13 +113,15 @@
|
||||||
// Range ends after last occurence; adjust end date
|
// Range ends after last occurence; adjust end date
|
||||||
endDate = lastDate;
|
endDate = lastDate;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
currentStartDate = [firStart copy];
|
currentStartDate = [firStart copy];
|
||||||
[currentStartDate autorelease];
|
[currentStartDate autorelease];
|
||||||
ranges = [NSMutableArray array];
|
ranges = [NSMutableArray array];
|
||||||
byDayMask = [rrule byDayMask];
|
|
||||||
i = 1;
|
i = 1;
|
||||||
if (!byDayMask)
|
count = 0;
|
||||||
|
|
||||||
|
if (dayMask == nil)
|
||||||
{
|
{
|
||||||
while ([currentStartDate compare: endDate] == NSOrderedAscending ||
|
while ([currentStartDate compare: endDate] == NSOrderedAscending ||
|
||||||
[currentStartDate compare: endDate] == NSOrderedSame)
|
[currentStartDate compare: endDate] == NSOrderedSame)
|
||||||
|
@ -115,42 +145,41 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsigned dayOfWeek;
|
|
||||||
NGCalendarDateRange *r;
|
NGCalendarDateRange *r;
|
||||||
|
|
||||||
while ([currentStartDate compare: endDate] == NSOrderedAscending ||
|
while ([currentStartDate compare: endDate] == NSOrderedAscending ||
|
||||||
[currentStartDate compare: endDate] == NSOrderedSame)
|
[currentStartDate compare: endDate] == NSOrderedSame)
|
||||||
{
|
{
|
||||||
if ([startDate compare: currentStartDate] == NSOrderedAscending ||
|
BOOL isRecurrence = NO;
|
||||||
[startDate compare: currentStartDate] == NSOrderedSame)
|
|
||||||
{
|
|
||||||
int days, week;
|
int days, week;
|
||||||
|
|
||||||
[currentStartDate years:NULL months:NULL days:(int *)&days hours:NULL
|
if (repeatCount > 0 ||
|
||||||
minutes:NULL seconds:NULL sinceDate:firStart];
|
[startDate compare: currentStartDate] == NSOrderedAscending ||
|
||||||
week = days / 7;
|
[startDate compare: currentStartDate] == NSOrderedSame)
|
||||||
|
|
||||||
if ((week % interval) == 0)
|
|
||||||
{
|
{
|
||||||
// Date is in the proper week with respect to the
|
// If the rule count is defined, stop once the count is reached.
|
||||||
// week interval
|
if (i == 1)
|
||||||
BOOL isRecurrence = NO;
|
|
||||||
|
|
||||||
if ([currentStartDate compare: firStart] == NSOrderedSame)
|
|
||||||
// Always add the event of the start date of
|
|
||||||
// the recurring event.
|
|
||||||
isRecurrence = YES;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Only consider events that matches the day mask.
|
// Always add the start date the recurring event if within
|
||||||
dayOfWeek = ([currentStartDate dayOfWeek]
|
// the lookup range.
|
||||||
? (unsigned int) 1 << [currentStartDate dayOfWeek]
|
|
||||||
: iCalWeekDaySunday);
|
|
||||||
if (dayOfWeek & [rrule byDayMask])
|
|
||||||
isRecurrence = YES;
|
isRecurrence = YES;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[currentStartDate years:NULL months:NULL days:(int *)&days hours:NULL
|
||||||
|
minutes:NULL seconds:NULL sinceDate: firStart];
|
||||||
|
week = days / 7;
|
||||||
|
|
||||||
|
if ((week % interval) == 0 &&
|
||||||
|
[dayMask occursOnDay: [currentStartDate dayOfWeek]])
|
||||||
|
isRecurrence = YES;
|
||||||
|
}
|
||||||
|
|
||||||
if (isRecurrence)
|
if (isRecurrence)
|
||||||
{
|
{
|
||||||
|
count++;
|
||||||
|
if (repeatCount > 0 && count > repeatCount)
|
||||||
|
break;
|
||||||
currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]];
|
currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]];
|
||||||
r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate
|
r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate
|
||||||
endDate: currentEndDate];
|
endDate: currentEndDate];
|
||||||
|
@ -158,10 +187,10 @@
|
||||||
[ranges addObject: r];
|
[ranges addObject: r];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
currentStartDate = [currentStartDate dateByAddingYears: 0
|
currentStartDate = [currentStartDate dateByAddingYears: 0
|
||||||
months: 0
|
months: 0
|
||||||
days: 1];
|
days: 1];
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,14 +200,18 @@
|
||||||
- (NSCalendarDate *) lastInstanceStartDate
|
- (NSCalendarDate *) lastInstanceStartDate
|
||||||
{
|
{
|
||||||
NSCalendarDate *firStart, *lastInstanceStartDate;
|
NSCalendarDate *firStart, *lastInstanceStartDate;
|
||||||
|
NGCalendarDateRange *r;
|
||||||
|
NSArray *instances;
|
||||||
|
|
||||||
|
lastInstanceStartDate = nil;
|
||||||
if ([rrule repeatCount] > 0)
|
if ([rrule repeatCount] > 0)
|
||||||
{
|
{
|
||||||
firStart = [firstRange startDate];
|
firStart = [firstRange startDate];
|
||||||
|
r = [NGCalendarDateRange calendarDateRangeWithStartDate: firStart
|
||||||
lastInstanceStartDate = [firStart dateByAddingYears: 0 months: 0
|
endDate: [NSCalendarDate distantFuture]];
|
||||||
days: (7 * [rrule repeatInterval]
|
instances = [self recurrenceRangesWithinCalendarDateRange: r];
|
||||||
* ([rrule repeatCount] - 1))];
|
if ([instances count])
|
||||||
|
lastInstanceStartDate = [(NGCalendarDateRange *)[instances lastObject] startDate];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
lastInstanceStartDate = [super lastInstanceStartDate];
|
lastInstanceStartDate = [super lastInstanceStartDate];
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||||
|
Copyright (C) 2006-2010 Inverse inc.
|
||||||
|
|
||||||
This file is part of SOPE.
|
This file is part of SOPE.
|
||||||
|
|
||||||
|
@ -34,74 +35,231 @@
|
||||||
- (NSCalendarDate *) lastInstanceStartDate;
|
- (NSCalendarDate *) lastInstanceStartDate;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@class iCalMonthlyRecurrenceCalculator;
|
||||||
|
|
||||||
@implementation iCalYearlyRecurrenceCalculator
|
@implementation iCalYearlyRecurrenceCalculator
|
||||||
|
|
||||||
- (NSArray *)
|
- (NSArray *)
|
||||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||||
{
|
{
|
||||||
NSMutableArray *ranges;
|
NSMutableArray *ranges;
|
||||||
NSCalendarDate *firStart, *rStart, *rEnd, *until;
|
NSArray *byMonth;
|
||||||
unsigned i, count, interval;
|
NSCalendarDate *firStart, *lastDate, *rStart, *rEnd, *until, *referenceDate;
|
||||||
int diff;
|
iCalMonthlyRecurrenceCalculator *monthlyCalc;
|
||||||
|
unsigned j, yearIdxInRange, numberOfYearsInRange, count, interval, monthDiff;
|
||||||
|
int diff, repeatCount, currentMonth;
|
||||||
|
|
||||||
firStart = [firstRange startDate];
|
firStart = [firstRange startDate];
|
||||||
rStart = [_r startDate];
|
rStart = [_r startDate];
|
||||||
rEnd = [_r endDate];
|
rEnd = [_r endDate];
|
||||||
interval = [rrule repeatInterval];
|
interval = [rrule repeatInterval];
|
||||||
until = [self lastInstanceStartDate];
|
byMonth = [rrule byMonth];
|
||||||
|
diff = 0;
|
||||||
|
repeatCount = 0;
|
||||||
|
count = 0;
|
||||||
|
referenceDate = nil;
|
||||||
|
|
||||||
|
if ([rEnd compare: firStart] == NSOrderedAscending)
|
||||||
|
// Range ends before first occurrence
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
// If rule is bound, check the bounds
|
||||||
|
if (![rrule isInfinite])
|
||||||
|
{
|
||||||
|
lastDate = nil;
|
||||||
|
until = [rrule untilDate];
|
||||||
|
repeatCount = [rrule repeatCount];
|
||||||
|
|
||||||
if (until)
|
if (until)
|
||||||
{
|
{
|
||||||
if ([until compare: rStart] == NSOrderedAscending)
|
lastDate = until;
|
||||||
return nil;
|
}
|
||||||
if ([until compare: rEnd] == NSOrderedDescending)
|
if (repeatCount > 0)
|
||||||
rEnd = until;
|
{
|
||||||
|
if (lastDate == nil && ![rrule hasByMask])
|
||||||
|
// When there's no BYxxx mask, we can find the date of the last
|
||||||
|
// occurrence.
|
||||||
|
lastDate = [firStart dateByAddingYears: (interval * (repeatCount - 1))
|
||||||
|
months: 0
|
||||||
|
days: 0];
|
||||||
|
referenceDate = firStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastDate != nil)
|
||||||
|
{
|
||||||
|
if ([lastDate compare: rStart] == NSOrderedAscending)
|
||||||
|
// Range starts after last occurrence
|
||||||
|
return nil;
|
||||||
|
if ([lastDate compare: rEnd] == NSOrderedDescending)
|
||||||
|
// Range ends after last occurence; adjust end date
|
||||||
|
rEnd = lastDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (referenceDate == nil)
|
||||||
|
{
|
||||||
diff = [firStart yearsBetweenDate: rStart];
|
diff = [firStart yearsBetweenDate: rStart];
|
||||||
if ((diff != 0) && [rStart compare: firStart] == NSOrderedAscending)
|
if ((diff != 0) && [rStart compare: firStart] == NSOrderedAscending)
|
||||||
diff = -diff;
|
diff = -diff;
|
||||||
|
referenceDate = rStart;
|
||||||
|
}
|
||||||
|
|
||||||
count = [rStart yearsBetweenDate: rEnd] + 1;
|
// Initialize array to return with an approximation of the number total
|
||||||
ranges = [NSMutableArray arrayWithCapacity: count];
|
// number of possible matches, ie the number of years spawned by the period.
|
||||||
for (i = 0 ; i < count; i++)
|
numberOfYearsInRange = [referenceDate yearsBetweenDate: rEnd] + 1;
|
||||||
|
ranges = [NSMutableArray arrayWithCapacity: numberOfYearsInRange];
|
||||||
|
|
||||||
|
if (byMonth)
|
||||||
{
|
{
|
||||||
int test;
|
/*
|
||||||
|
* WARNING/TODO : if there's no BYMONTH rule but there's a BYMONTHDAY
|
||||||
|
* rule we should implicitely define a BYMONTH rule by extracting the
|
||||||
|
* month from the DTSTART field. However, this kind of definition is
|
||||||
|
* uncommon.
|
||||||
|
*/
|
||||||
|
|
||||||
test = diff + i;
|
// Instantiate a MONTHLY calculator
|
||||||
|
if (repeatCount > 0)
|
||||||
|
// Fool the monthly calculator, otherwise it will verify the COUNT
|
||||||
|
// constraint and perform the calculation from the first occurence of
|
||||||
|
// the recurrence. This calculation is performed by the current method.
|
||||||
|
[rrule setRepeatCount: 0];
|
||||||
|
|
||||||
|
monthlyCalc = [[iCalMonthlyRecurrenceCalculator alloc]
|
||||||
|
initWithRecurrenceRule: rrule
|
||||||
|
firstInstanceCalendarDateRange: firstRange];
|
||||||
|
[monthlyCalc autorelease];
|
||||||
|
|
||||||
|
// 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: [referenceDate yearOfCommonEra]
|
||||||
|
month: [referenceDate monthOfYear]
|
||||||
|
day: 1
|
||||||
|
hour: [referenceDate hourOfDay]
|
||||||
|
minute: [referenceDate minuteOfHour]
|
||||||
|
second: 0
|
||||||
|
timeZone: [referenceDate timeZone]];
|
||||||
|
|
||||||
|
// If the BYMONTH constraints exclude the month of the event DTSTART, we
|
||||||
|
// add the corresponding range manually if it is included in the period.
|
||||||
|
// Otherwise, it will be included by the monthly calculator in the loop
|
||||||
|
// bellow.
|
||||||
|
int month = [firStart monthOfYear];
|
||||||
|
if (![byMonth containsObject: [NSString stringWithFormat: @"%i", month]])
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
if ([_r containsDateRange: firstRange])
|
||||||
|
{
|
||||||
|
[ranges addObject: firstRange];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
monthDiff = 0;
|
||||||
|
currentMonth = [referenceDate monthOfYear];
|
||||||
|
for (yearIdxInRange = 0 ; yearIdxInRange < numberOfYearsInRange; yearIdxInRange++)
|
||||||
|
{
|
||||||
|
int test, year;
|
||||||
|
|
||||||
|
test = diff + yearIdxInRange;
|
||||||
if ((test >= 0) && (test % interval) == 0)
|
if ((test >= 0) && (test % interval) == 0)
|
||||||
{
|
{
|
||||||
|
year = yearIdxInRange + [referenceDate yearOfCommonEra];
|
||||||
|
|
||||||
|
if (byMonth)
|
||||||
|
{
|
||||||
|
// When there's a BYMONTH constraint, evaluate each month of the constraint using
|
||||||
|
// the monthly calculator.
|
||||||
|
for (j = 0; currentMonth < 13 && j <= 12; j++, currentMonth++, monthDiff++)
|
||||||
|
{
|
||||||
|
if ([byMonth containsObject: [NSString stringWithFormat: @"%i", currentMonth]])
|
||||||
|
{
|
||||||
|
NGCalendarDateRange *rangeForMonth;
|
||||||
|
NSArray *rangesInMonth;
|
||||||
|
|
||||||
|
rStart = [referenceDate dateByAddingYears: 0
|
||||||
|
months: monthDiff
|
||||||
|
days: 0];
|
||||||
|
rEnd = [rStart dateByAddingYears: 0
|
||||||
|
months: 0
|
||||||
|
days: [rStart numberOfDaysInMonth] - 1];
|
||||||
|
rangeForMonth = [NGCalendarDateRange calendarDateRangeWithStartDate: rStart
|
||||||
|
endDate: rEnd];
|
||||||
|
rangesInMonth = [monthlyCalc recurrenceRangesWithinCalendarDateRange: rangeForMonth];
|
||||||
|
|
||||||
|
int k;
|
||||||
|
for (k = 0; k < [rangesInMonth count] && (repeatCount == 0 || count < repeatCount); k++) {
|
||||||
|
//NSLog(@"*** YEARLY found %@ (count = %i)", [[rangesInMonth objectAtIndex: k] startDate], count);
|
||||||
|
count++;
|
||||||
|
if ([_r containsDateRange: [rangesInMonth objectAtIndex: k]])
|
||||||
|
{
|
||||||
|
[ranges addObject: [rangesInMonth objectAtIndex: k]];
|
||||||
|
//NSLog(@"*** YEARLY adding %@ (count = %i)", [[rangesInMonth objectAtIndex: k] startDate], count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Done with the current year; start the next iteration from January
|
||||||
|
currentMonth = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No BYxxx mask
|
||||||
NSCalendarDate *start, *end;
|
NSCalendarDate *start, *end;
|
||||||
NGCalendarDateRange *r;
|
NGCalendarDateRange *r;
|
||||||
|
|
||||||
start = [firStart dateByAddingYears: diff + i
|
start = [firStart dateByAddingYears: diff + yearIdxInRange
|
||||||
months: 0
|
months: 0
|
||||||
days: 0];
|
days: 0];
|
||||||
[start setTimeZone: [firStart timeZone]];
|
[start setTimeZone: [firStart timeZone]];
|
||||||
end = [start addTimeInterval: [firstRange duration]];
|
end = [start addTimeInterval: [firstRange duration]];
|
||||||
r = [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
r = [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
||||||
endDate: end];
|
endDate: end];
|
||||||
if ([_r containsDateRange: r])
|
if ([_r containsDateRange: r] && (repeatCount == 0 || count < repeatCount))
|
||||||
|
{
|
||||||
[ranges addObject: r];
|
[ranges addObject: r];
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byMonth && repeatCount > 0)
|
||||||
|
// Restore the repeat count
|
||||||
|
[rrule setRepeatCount: repeatCount];
|
||||||
|
|
||||||
return ranges;
|
return ranges;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSCalendarDate *) lastInstanceStartDate
|
- (NSCalendarDate *) lastInstanceStartDate
|
||||||
{
|
{
|
||||||
NSCalendarDate *firStart, *lastInstanceStartDate;
|
NSCalendarDate *firStart, *lastInstanceStartDate;
|
||||||
|
NGCalendarDateRange *r;
|
||||||
|
NSArray *instances;
|
||||||
|
|
||||||
|
lastInstanceStartDate = nil;
|
||||||
if ([rrule repeatCount] > 0)
|
if ([rrule repeatCount] > 0)
|
||||||
{
|
{
|
||||||
|
if ([rrule hasByMask])
|
||||||
|
{
|
||||||
|
// Must perform the complete calculation
|
||||||
firStart = [firstRange startDate];
|
firStart = [firstRange startDate];
|
||||||
|
r = [NGCalendarDateRange calendarDateRangeWithStartDate: firStart
|
||||||
lastInstanceStartDate
|
endDate: [NSCalendarDate distantFuture]];
|
||||||
= [firStart dateByAddingYears: ([rrule repeatInterval]
|
instances = [self recurrenceRangesWithinCalendarDateRange: r];
|
||||||
|
if ([instances count])
|
||||||
|
lastInstanceStartDate = [(NGCalendarDateRange *)[instances lastObject] startDate];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No BYxxx mask
|
||||||
|
lastInstanceStartDate = [firStart dateByAddingYears: ([rrule repeatInterval]
|
||||||
* ([rrule repeatCount] - 1))
|
* ([rrule repeatCount] - 1))
|
||||||
months: 0
|
months: 0
|
||||||
days: 0];
|
days: 0];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
lastInstanceStartDate = [super lastInstanceStartDate];
|
lastInstanceStartDate = [super lastInstanceStartDate];
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* UIxAppointmentEditor.m - this file is part of SOGo
|
/* UIxAppointmentEditor.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007-2009 Inverse inc.
|
* Copyright (C) 2007-2010 Inverse inc.
|
||||||
*
|
*
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||||
*
|
*
|
||||||
|
@ -419,13 +419,11 @@
|
||||||
{
|
{
|
||||||
WOResponse *result;
|
WOResponse *result;
|
||||||
NSDictionary *data;
|
NSDictionary *data;
|
||||||
NSCalendarDate *firstDate, *eventDate;
|
NSCalendarDate *eventDate;
|
||||||
NSTimeZone *timeZone;
|
NSTimeZone *timeZone;
|
||||||
SOGoUserDefaults *ud;
|
SOGoUserDefaults *ud;
|
||||||
SOGoCalendarComponent *co;
|
SOGoCalendarComponent *co;
|
||||||
iCalEvent *master;
|
|
||||||
BOOL resetAlarm;
|
BOOL resetAlarm;
|
||||||
signed int daylightOffset;
|
|
||||||
|
|
||||||
[self event];
|
[self event];
|
||||||
|
|
||||||
|
@ -457,20 +455,6 @@
|
||||||
[co saveComponent: event];
|
[co saveComponent: event];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([co isNew] && [co isKindOfClass: [SOGoAppointmentOccurence class]])
|
|
||||||
{
|
|
||||||
// This is a new exception in a recurrent event -- compute the daylight
|
|
||||||
// saving time with respect to the first occurrence of the recurrent event.
|
|
||||||
master = (iCalEvent*)[[event parent] firstChildWithTag: @"vevent"];
|
|
||||||
firstDate = [master startDate];
|
|
||||||
|
|
||||||
if ([timeZone isDaylightSavingTimeForDate: eventDate] != [timeZone isDaylightSavingTimeForDate: firstDate])
|
|
||||||
{
|
|
||||||
daylightOffset = (signed int)[timeZone secondsFromGMTForDate: firstDate]
|
|
||||||
- (signed int)[timeZone secondsFromGMTForDate: eventDate];
|
|
||||||
eventDate = [eventDate dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:daylightOffset];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data = [NSDictionary dictionaryWithObjectsAndKeys:
|
data = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||||
[componentCalendar displayName], @"calendar",
|
[componentCalendar displayName], @"calendar",
|
||||||
[event tag], @"component",
|
[event tag], @"component",
|
||||||
|
|
|
@ -273,6 +273,7 @@ static NSArray *tasksFields = nil;
|
||||||
static NSString *fields[] = { @"startDate", @"c_startdate",
|
static NSString *fields[] = { @"startDate", @"c_startdate",
|
||||||
@"endDate", @"c_enddate" };
|
@"endDate", @"c_enddate" };
|
||||||
|
|
||||||
|
if (dayBasedView)
|
||||||
for (count = 0; count < 2; count++)
|
for (count = 0; count < 2; count++)
|
||||||
{
|
{
|
||||||
aDateField = fields[count * 2];
|
aDateField = fields[count * 2];
|
||||||
|
@ -291,9 +292,9 @@ static NSArray *tasksFields = nil;
|
||||||
|
|
||||||
aDateValue = [aRecord objectForKey: @"c_recurrence_id"];
|
aDateValue = [aRecord objectForKey: @"c_recurrence_id"];
|
||||||
aDate = [aRecord objectForKey: @"cycleStartDate"];
|
aDate = [aRecord objectForKey: @"cycleStartDate"];
|
||||||
aStartDate = [aRecord objectForKey: @"startDate"];
|
|
||||||
if (aDateValue && aDate)
|
if (aDateValue && aDate)
|
||||||
{
|
{
|
||||||
|
aStartDate = [aRecord objectForKey: @"startDate"];
|
||||||
if ([userTimeZone isDaylightSavingTimeForDate: aStartDate] !=
|
if ([userTimeZone isDaylightSavingTimeForDate: aStartDate] !=
|
||||||
[userTimeZone isDaylightSavingTimeForDate: aDate])
|
[userTimeZone isDaylightSavingTimeForDate: aDate])
|
||||||
{
|
{
|
||||||
|
@ -387,8 +388,6 @@ static NSArray *tasksFields = nil;
|
||||||
forKey: @"c_owner"];
|
forKey: @"c_owner"];
|
||||||
if (![[newInfo objectForKey: @"c_title"] length])
|
if (![[newInfo objectForKey: @"c_title"] length])
|
||||||
[self _fixComponentTitle: newInfo withType: component];
|
[self _fixComponentTitle: newInfo withType: component];
|
||||||
if (dayBasedView
|
|
||||||
|| [[newInfo objectForKey: @"c_isallday"] boolValue])
|
|
||||||
// Possible improvement: only call _fixDates if event is recurrent
|
// Possible improvement: only call _fixDates if event is recurrent
|
||||||
// or the view range span a daylight saving time change
|
// or the view range span a daylight saving time change
|
||||||
[self _fixDates: newInfo];
|
[self _fixDates: newInfo];
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#import <Foundation/NSURL.h>
|
#import <Foundation/NSURL.h>
|
||||||
|
|
||||||
#import <NGCards/iCalAlarm.h>
|
#import <NGCards/iCalAlarm.h>
|
||||||
|
#import <NGCards/iCalByDayMask.h>
|
||||||
#import <NGCards/iCalPerson.h>
|
#import <NGCards/iCalPerson.h>
|
||||||
#import <NGCards/iCalRepeatableEntityObject.h>
|
#import <NGCards/iCalRepeatableEntityObject.h>
|
||||||
#import <NGCards/iCalRecurrenceRule.h>
|
#import <NGCards/iCalRecurrenceRule.h>
|
||||||
|
@ -294,25 +295,6 @@ iRANGE(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *) _dayMaskToInteger: (unsigned int) theMask
|
|
||||||
{
|
|
||||||
iCalWeekDay maskDays[] = {iCalWeekDaySunday, iCalWeekDayMonday,
|
|
||||||
iCalWeekDayTuesday, iCalWeekDayWednesday,
|
|
||||||
iCalWeekDayThursday, iCalWeekDayFriday,
|
|
||||||
iCalWeekDaySaturday};
|
|
||||||
unsigned int i;
|
|
||||||
NSMutableString *s;
|
|
||||||
|
|
||||||
s = [NSMutableString string];
|
|
||||||
|
|
||||||
for (i = 0; i < 7; i++)
|
|
||||||
if ((theMask & maskDays[i]))
|
|
||||||
[s appendFormat: @"%d,", i];
|
|
||||||
[s deleteSuffix: @","];
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) _loadRRules
|
- (void) _loadRRules
|
||||||
{
|
{
|
||||||
SOGoUserDefaults *ud;
|
SOGoUserDefaults *ud;
|
||||||
|
@ -326,15 +308,12 @@ iRANGE(2);
|
||||||
|
|
||||||
rule = [[component recurrenceRules] lastObject];
|
rule = [[component recurrenceRules] lastObject];
|
||||||
|
|
||||||
|
/* DAILY */
|
||||||
if ([rule frequency] == iCalRecurrenceFrequenceDaily)
|
if ([rule frequency] == iCalRecurrenceFrequenceDaily)
|
||||||
{
|
{
|
||||||
repeatType = @"0";
|
repeatType = @"0";
|
||||||
|
|
||||||
if ([rule byDayMask] == (iCalWeekDayMonday
|
if ([[rule byDayMask] isWeekDays])
|
||||||
| iCalWeekDayTuesday
|
|
||||||
| iCalWeekDayWednesday
|
|
||||||
| iCalWeekDayThursday
|
|
||||||
| iCalWeekDayFriday))
|
|
||||||
{
|
{
|
||||||
if ([rule isInfinite])
|
if ([rule isInfinite])
|
||||||
repeat = @"EVERY WEEKDAY";
|
repeat = @"EVERY WEEKDAY";
|
||||||
|
@ -350,12 +329,14 @@ iRANGE(2);
|
||||||
[self setRepeat2: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
[self setRepeat2: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* WEEKLY */
|
||||||
else if ([rule frequency] == iCalRecurrenceFrequenceWeekly)
|
else if ([rule frequency] == iCalRecurrenceFrequenceWeekly)
|
||||||
{
|
{
|
||||||
repeatType = @"1";
|
repeatType = @"1";
|
||||||
[self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
[self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
||||||
|
|
||||||
if (![rule byDayMask])
|
if (![[rule byDay] length])
|
||||||
{
|
{
|
||||||
if ([rule repeatInterval] == 1)
|
if ([rule repeatInterval] == 1)
|
||||||
repeat = @"WEEKLY";
|
repeat = @"WEEKLY";
|
||||||
|
@ -364,45 +345,81 @@ iRANGE(2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[self setRepeat2: [self _dayMaskToInteger: [rule byDayMask]]];
|
[self setRepeat2: [[rule byDayMask] asRuleStringWithIntegers]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MONTHLY */
|
||||||
else if ([rule frequency] == iCalRecurrenceFrequenceMonthly)
|
else if ([rule frequency] == iCalRecurrenceFrequenceMonthly)
|
||||||
{
|
{
|
||||||
repeatType = @"2";
|
repeatType = @"2";
|
||||||
|
|
||||||
if ([rule byDayMask])
|
if ([[rule byDay] length])
|
||||||
{
|
{
|
||||||
// TODO
|
int firstOccurrence;
|
||||||
|
iCalByDayMask *dayMask;
|
||||||
|
|
||||||
|
dayMask = [rule byDayMask];
|
||||||
|
firstOccurrence = [dayMask firstOccurrence] - 1;
|
||||||
|
if (firstOccurrence < 0)
|
||||||
|
firstOccurrence = 5;
|
||||||
|
|
||||||
[self setRepeat2: @"0"];
|
[self setRepeat2: @"0"];
|
||||||
|
[self setRepeat3: [NSString stringWithFormat: @"%d", firstOccurrence]];
|
||||||
|
[self setRepeat4: [NSString stringWithFormat: @"%d", [dayMask firstDay]]];
|
||||||
}
|
}
|
||||||
else if ([[rule byMonthDay] count])
|
else if ([[rule byMonthDay] count])
|
||||||
|
{
|
||||||
|
NSArray *days;
|
||||||
|
|
||||||
|
days = [rule byMonthDay];
|
||||||
|
if ([days count] > 0 && [[days objectAtIndex: 0] intValue] < 0)
|
||||||
|
{
|
||||||
|
// BYMONTHDAY=-1
|
||||||
|
[self setRepeat2: @"0"];
|
||||||
|
[self setRepeat3: @"5"]; // last ..
|
||||||
|
[self setRepeat4: @"7"]; // .. day of the month
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
[self setRepeat2: @"1"];
|
[self setRepeat2: @"1"];
|
||||||
[self setRepeat5: [[rule byMonthDay] componentsJoinedByString: @","]];
|
[self setRepeat5: [[rule byMonthDay] componentsJoinedByString: @","]];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if ([rule repeatInterval] == 1)
|
else if ([rule repeatInterval] == 1)
|
||||||
repeat = @"MONTHLY";
|
repeat = @"MONTHLY";
|
||||||
|
|
||||||
[self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
[self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* YEARLY */
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
repeatType = @"3";
|
repeatType = @"3";
|
||||||
|
|
||||||
if ([rule namedValue: @"bymonth"])
|
if ([[rule namedValue: @"bymonth"] length])
|
||||||
{
|
{
|
||||||
if (![rule byDayMask])
|
if ([[rule byDay] length])
|
||||||
|
{
|
||||||
|
int firstOccurrence;
|
||||||
|
iCalByDayMask *dayMask;
|
||||||
|
|
||||||
|
dayMask = [rule byDayMask];
|
||||||
|
firstOccurrence = [dayMask firstOccurrence] - 1;
|
||||||
|
if (firstOccurrence < 0)
|
||||||
|
firstOccurrence = 5;
|
||||||
|
|
||||||
|
[self setRepeat2: @"1"];
|
||||||
|
[self setRepeat5: [NSString stringWithFormat: @"%d", firstOccurrence]];
|
||||||
|
[self setRepeat6: [NSString stringWithFormat: @"%d", [dayMask firstDay]]];
|
||||||
|
[self setRepeat7: [NSString stringWithFormat: @"%d", [[rule namedValue: @"bymonth"] intValue]-1]];
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
[self setRepeat2: @"0"];
|
[self setRepeat2: @"0"];
|
||||||
[self setRepeat3: [rule namedValue: @"bymonthday"]];
|
[self setRepeat3: [rule namedValue: @"bymonthday"]];
|
||||||
[self setRepeat4: [NSString stringWithFormat: @"%d", [[rule namedValue: @"bymonth"] intValue]-1]];
|
[self setRepeat4: [NSString stringWithFormat: @"%d", [[rule namedValue: @"bymonth"] intValue]-1]];
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
[self setRepeat2: @"1"];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if ([rule repeatInterval] == 1)
|
else if ([rule repeatInterval] == 1)
|
||||||
repeat = @"YEARLY";
|
repeat = @"YEARLY";
|
||||||
|
@ -410,7 +427,7 @@ iRANGE(2);
|
||||||
[self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
[self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// We decode the proper end date, recurrences count, etc.
|
/* We decode the proper end date, recurrences count, etc. */
|
||||||
if ([rule repeatCount])
|
if ([rule repeatCount])
|
||||||
{
|
{
|
||||||
repeat = @"CUSTOM";
|
repeat = @"CUSTOM";
|
||||||
|
@ -1639,9 +1656,9 @@ RANGE(2);
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
// DAILY:
|
// DAILY (0)
|
||||||
//
|
//
|
||||||
// repeat1 holds the value of the radio button:
|
// repeat1 holds the value of the frequency radio button:
|
||||||
// 0 -> Every X days
|
// 0 -> Every X days
|
||||||
// 1 -> Every weekday
|
// 1 -> Every weekday
|
||||||
//
|
//
|
||||||
|
@ -1653,11 +1670,7 @@ RANGE(2);
|
||||||
|
|
||||||
if ([[self repeat1] intValue] > 0)
|
if ([[self repeat1] intValue] > 0)
|
||||||
{
|
{
|
||||||
[theRule setByDayMask: (iCalWeekDayMonday
|
[theRule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
|
||||||
|iCalWeekDayTuesday
|
|
||||||
|iCalWeekDayWednesday
|
|
||||||
|iCalWeekDayThursday
|
|
||||||
|iCalWeekDayFriday)];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1670,7 +1683,7 @@ RANGE(2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// WEEKLY
|
// WEEKLY (1)
|
||||||
//
|
//
|
||||||
// repeat1 holds the value of "Every X week(s)"
|
// repeat1 holds the value of "Every X week(s)"
|
||||||
//
|
//
|
||||||
|
@ -1683,7 +1696,8 @@ RANGE(2);
|
||||||
if ([[self repeat1] intValue] > 0)
|
if ([[self repeat1] intValue] > 0)
|
||||||
{
|
{
|
||||||
NSArray *v;
|
NSArray *v;
|
||||||
int c, mask;
|
int c, day;
|
||||||
|
iCalWeekOccurrences days;
|
||||||
|
|
||||||
[theRule setFrequency: iCalRecurrenceFrequenceWeekly];
|
[theRule setFrequency: iCalRecurrenceFrequenceWeekly];
|
||||||
[theRule setInterval: [self repeat1]];
|
[theRule setInterval: [self repeat1]];
|
||||||
|
@ -1692,18 +1706,21 @@ RANGE(2);
|
||||||
{
|
{
|
||||||
v = [[self repeat2] componentsSeparatedByString: @","];
|
v = [[self repeat2] componentsSeparatedByString: @","];
|
||||||
c = [v count];
|
c = [v count];
|
||||||
mask = 0;
|
memset(days, 0, 7 * sizeof(iCalWeekOccurrence));
|
||||||
|
|
||||||
while (c--)
|
while (c--)
|
||||||
mask |= 1 << ([[v objectAtIndex: c] intValue]);
|
{
|
||||||
|
day = [[v objectAtIndex: c] intValue];
|
||||||
[theRule setByDayMask: mask];
|
if (day >= 0 && day <= 7)
|
||||||
|
days[day] = iCalWeekOccurrenceAll;
|
||||||
|
}
|
||||||
|
[theRule setByDayMask: [iCalByDayMask byDayMaskWithDays: days]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// MONTHLY
|
// MONTHLY (2)
|
||||||
//
|
//
|
||||||
// repeat1 holds the value of "Every X month(s)"
|
// repeat1 holds the value of "Every X month(s)"
|
||||||
//
|
//
|
||||||
|
@ -1731,20 +1748,29 @@ RANGE(2);
|
||||||
[theRule setInterval: [self repeat1]];
|
[theRule setInterval: [self repeat1]];
|
||||||
|
|
||||||
// We recur on specific days...
|
// We recur on specific days...
|
||||||
if ([[self repeat2] intValue] == 1
|
if ([[self repeat2] intValue] == 0)
|
||||||
&& [[self repeat5] intValue] > 0)
|
|
||||||
{
|
{
|
||||||
[theRule setNamedValue: @"bymonthday" to: [self repeat5]];
|
NSString *day;
|
||||||
|
int occurence;
|
||||||
|
|
||||||
|
day = [theRule iCalRepresentationForWeekDay: [[self repeat4] intValue]];
|
||||||
|
occurence = [[self repeat3] intValue] + 1;
|
||||||
|
if (occurence > 5) // the first/second/third/fourth/fifth ..
|
||||||
|
occurence = -1; // the last ..
|
||||||
|
[theRule setNamedValue: @"byday"
|
||||||
|
to: [NSString stringWithFormat: @"%d%@",
|
||||||
|
occurence, day]];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO
|
if ([[self repeat5] intValue] > 0)
|
||||||
|
[theRule setNamedValue: @"bymonthday" to: [self repeat5]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// YEARLY
|
// YEARLY (3)
|
||||||
//
|
//
|
||||||
// repeat1 holds the value of "Every X year(s)"
|
// repeat1 holds the value of "Every X year(s)"
|
||||||
//
|
//
|
||||||
|
@ -1756,7 +1782,7 @@ RANGE(2);
|
||||||
// repeat4 holds the value of the MONTH parameter (0 -> January, 1 -> February ... )
|
// repeat4 holds the value of the MONTH parameter (0 -> January, 1 -> February ... )
|
||||||
// ex: 3 February
|
// ex: 3 February
|
||||||
//
|
//
|
||||||
// repeat5 holds the value of the OCCURENCE parameter (0 -> First, 1 -> Second ..)
|
// repeat5 holds the value of the OCCURENCE parameter (0 -> First, 1 -> Second .., 5 -> Last)
|
||||||
// repeat6 holds the value of the DAY parameter (0 -> Sunday, 1 -> Monday, etc..)
|
// repeat6 holds the value of the DAY parameter (0 -> Sunday, 1 -> Monday, etc..)
|
||||||
// repeat7 holds the value of the MONTH parameter (0 -> January, 1 -> February ... )
|
// repeat7 holds the value of the MONTH parameter (0 -> January, 1 -> February ... )
|
||||||
//
|
//
|
||||||
|
@ -1771,7 +1797,19 @@ RANGE(2);
|
||||||
// We recur Every .. of ..
|
// We recur Every .. of ..
|
||||||
if ([[self repeat2] intValue] == 1)
|
if ([[self repeat2] intValue] == 1)
|
||||||
{
|
{
|
||||||
// TODO
|
NSString *day;
|
||||||
|
int occurence;
|
||||||
|
|
||||||
|
day = [theRule iCalRepresentationForWeekDay: [[self repeat6] intValue]];
|
||||||
|
occurence = [[self repeat5] intValue] + 1;
|
||||||
|
if (occurence > 5) // the first/second/third/fourth/fifth ..
|
||||||
|
occurence = -1; // the last ..
|
||||||
|
[theRule setNamedValue: @"byday"
|
||||||
|
to: [NSString stringWithFormat: @"%d%@",
|
||||||
|
occurence, day]];
|
||||||
|
[theRule setNamedValue: @"bymonth"
|
||||||
|
to: [NSString stringWithFormat: @"%d",
|
||||||
|
[[self repeat7] intValue] + 1]];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1883,11 +1921,7 @@ RANGE(2);
|
||||||
}
|
}
|
||||||
else if ([repeat caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame)
|
else if ([repeat caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame)
|
||||||
{
|
{
|
||||||
[rule setByDayMask: (iCalWeekDayMonday
|
[rule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
|
||||||
|iCalWeekDayTuesday
|
|
||||||
|iCalWeekDayWednesday
|
|
||||||
|iCalWeekDayThursday
|
|
||||||
|iCalWeekDayFriday)];
|
|
||||||
[rule setFrequency: iCalRecurrenceFrequenceDaily];
|
[rule setFrequency: iCalRecurrenceFrequenceDaily];
|
||||||
}
|
}
|
||||||
else if ([repeat caseInsensitiveCompare: @"MONTHLY"] == NSOrderedSame
|
else if ([repeat caseInsensitiveCompare: @"MONTHLY"] == NSOrderedSame
|
||||||
|
|
|
@ -241,11 +241,6 @@ function handleMonthlyRecurrence() {
|
||||||
|
|
||||||
var radioValue = $('recurrence_form').getRadioValue('monthlyRadioButtonName');
|
var radioValue = $('recurrence_form').getRadioValue('monthlyRadioButtonName');
|
||||||
|
|
||||||
// FIXME - right now we do not support rules
|
|
||||||
// such as The Second Tuesday...
|
|
||||||
if (radioValue == 0)
|
|
||||||
window.alert(recurrenceUnsupported);
|
|
||||||
else {
|
|
||||||
// We check if the monthlyMonthsField really contains an integer
|
// We check if the monthlyMonthsField really contains an integer
|
||||||
var showError = true;
|
var showError = true;
|
||||||
|
|
||||||
|
@ -265,7 +260,6 @@ function handleMonthlyRecurrence() {
|
||||||
|
|
||||||
if (showError)
|
if (showError)
|
||||||
window.alert(monthFieldInvalid);
|
window.alert(monthFieldInvalid);
|
||||||
}
|
|
||||||
|
|
||||||
return validate;
|
return validate;
|
||||||
}
|
}
|
||||||
|
@ -279,16 +273,21 @@ function validateYearlyRecurrence() {
|
||||||
// We check if the yearlyYearsField really contains an integer
|
// We check if the yearlyYearsField really contains an integer
|
||||||
var v = parseInt(fieldValue);
|
var v = parseInt(fieldValue);
|
||||||
if (!isNaN(v) && v > 0) {
|
if (!isNaN(v) && v > 0) {
|
||||||
|
var radioValue = $('recurrence_form').getRadioValue('yearlyRadioButtonName');
|
||||||
|
if (radioValue == 0) {
|
||||||
errorToShow = 1;
|
errorToShow = 1;
|
||||||
fieldValue = "" + $('yearlyDayField').value;
|
fieldValue = "" + $('yearlyDayField').value;
|
||||||
if (fieldValue.length > 0) {
|
if (fieldValue.length > 0) {
|
||||||
// We check if the yearlyYearsField really contains an integer
|
// We check if the yearlyDayField really contains an integer
|
||||||
var v = parseInt(fieldValue);
|
var v = parseInt(fieldValue);
|
||||||
if (!isNaN(v) && v > 0) {
|
if (!isNaN(v) && v > 0) {
|
||||||
errorToShow = -1;
|
errorToShow = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
errorToShow = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorToShow > -1)
|
if (errorToShow > -1)
|
||||||
|
@ -300,16 +299,11 @@ function validateYearlyRecurrence() {
|
||||||
function handleYearlyRecurrence() {
|
function handleYearlyRecurrence() {
|
||||||
var validate = false;
|
var validate = false;
|
||||||
|
|
||||||
var radioValue = $('recurrence_form').getRadioValue('yearlyRadioButtonName');
|
|
||||||
// FIXME - right now we do not support rules
|
|
||||||
// such as Every Second Tuesday of February
|
|
||||||
if (radioValue == 1)
|
|
||||||
window.alert(recurrenceUnsupported);
|
|
||||||
else {
|
|
||||||
if (validateYearlyRecurrence()) {
|
if (validateYearlyRecurrence()) {
|
||||||
|
var radioValue = $('recurrence_form').getRadioValue('yearlyRadioButtonName');
|
||||||
var fieldValue = "" + $('yearlyYearsField').value;
|
var fieldValue = "" + $('yearlyYearsField').value;
|
||||||
if (fieldValue.length > 0) {
|
if (fieldValue.length > 0) {
|
||||||
// We check if the yearlyYearsField really contains an integer
|
// We check if the yearlyYearsField (interval) really contains an integer
|
||||||
var v = parseInt(fieldValue);
|
var v = parseInt(fieldValue);
|
||||||
if (!isNaN(v) && v > 0) {
|
if (!isNaN(v) && v > 0) {
|
||||||
validate = true;
|
validate = true;
|
||||||
|
@ -326,7 +320,6 @@ function handleYearlyRecurrence() {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
validate = false;
|
validate = false;
|
||||||
}
|
|
||||||
|
|
||||||
return validate;
|
return validate;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue