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
|
||||
obj
|
||||
err
|
||||
log
|
||||
build\.log
|
||||
imgs-.*
|
||||
diff
|
||||
|
|
4
NEWS
4
NEWS
|
@ -4,10 +4,14 @@
|
|||
- added support for LDAP password policies
|
||||
- added support for custom Sieve filters
|
||||
- fixed timezone issues occurring specifically in the southern hemisphere
|
||||
- updated ckeditor to version 3.2
|
||||
- tabs: enabled the scrolling when overflowing
|
||||
- updated Czech translation, thanks to Milos Wimmer
|
||||
- removed remaining .wo templates, thereby easing the effort for future translations
|
||||
- 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 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
|
||||
|
|
|
@ -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>
|
||||
|
||||
* iCalEvent.m (-propertyValue:): new method that accept a
|
||||
|
|
|
@ -31,6 +31,7 @@ libNGCards_HEADER_FILES = \
|
|||
NGCards.h \
|
||||
iCalAlarm.h \
|
||||
iCalAttachment.h \
|
||||
iCalByDayMask.h \
|
||||
iCalCalendar.h \
|
||||
iCalDataSource.h \
|
||||
iCalDateTime.h \
|
||||
|
@ -78,6 +79,7 @@ libNGCards_OBJC_FILES = \
|
|||
\
|
||||
iCalAlarm.m \
|
||||
iCalAttachment.m \
|
||||
iCalByDayMask.m \
|
||||
iCalCalendar.m \
|
||||
iCalDailyRecurrenceCalculator.m \
|
||||
iCalDateTime.m \
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2010 Inverse inc.
|
||||
|
||||
This file is part of SOPE.
|
||||
|
||||
|
@ -29,6 +30,7 @@
|
|||
|
||||
#import "iCalRecurrenceCalculator.h"
|
||||
#import "iCalRecurrenceRule.h"
|
||||
#import "iCalByDayMask.h"
|
||||
|
||||
@interface iCalDailyRecurrenceCalculator : iCalRecurrenceCalculator
|
||||
@end
|
||||
|
@ -39,17 +41,34 @@
|
|||
|
||||
@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 *)
|
||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||
{
|
||||
NSMutableArray *ranges;
|
||||
NSCalendarDate *firStart, *startDate, *endDate, *currentStartDate, *currentEndDate;
|
||||
long i;
|
||||
iCalByDayMask *dayMask;
|
||||
long i, count, repeatCount;
|
||||
unsigned interval;
|
||||
|
||||
firStart = [firstRange startDate];
|
||||
startDate = [_r startDate];
|
||||
endDate = [_r endDate];
|
||||
dayMask = nil;
|
||||
repeatCount = 0;
|
||||
|
||||
if ([endDate compare: firStart] == NSOrderedAscending)
|
||||
// Range ends before first occurrence
|
||||
|
@ -57,19 +76,33 @@
|
|||
|
||||
interval = [rrule repeatInterval];
|
||||
|
||||
if ([[rrule byDay] length])
|
||||
dayMask = [rrule byDayMask];
|
||||
|
||||
// If rule is bound, check the bounds
|
||||
if (![rrule isInfinite])
|
||||
{
|
||||
NSCalendarDate *until, *lastDate;
|
||||
|
||||
lastDate = nil;
|
||||
until = [rrule untilDate];
|
||||
if (until)
|
||||
{
|
||||
lastDate = until;
|
||||
}
|
||||
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
|
||||
days: (interval
|
||||
* ([rrule repeatCount] - 1))];
|
||||
* (repeatCount - 1))];
|
||||
}
|
||||
|
||||
if (lastDate != nil)
|
||||
{
|
||||
if ([lastDate compare: startDate] == NSOrderedAscending)
|
||||
// Range starts after last occurrence
|
||||
return nil;
|
||||
|
@ -78,32 +111,52 @@
|
|||
// Range ends after last occurence; adjust end date
|
||||
endDate = lastDate;
|
||||
}
|
||||
}
|
||||
|
||||
currentStartDate = [firStart copy];
|
||||
[currentStartDate autorelease];
|
||||
ranges = [NSMutableArray array];
|
||||
i = 1;
|
||||
count = 0;
|
||||
|
||||
while ([currentStartDate compare: endDate] == NSOrderedAscending ||
|
||||
[currentStartDate compare: endDate] == NSOrderedSame)
|
||||
{
|
||||
if ([startDate compare: currentStartDate] == NSOrderedAscending ||
|
||||
[startDate compare: currentStartDate] == NSOrderedSame)
|
||||
BOOL wrongDay, isFirStart;
|
||||
|
||||
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;
|
||||
|
||||
if ([rrule byDayMask])
|
||||
if (isFirStart == NO && dayMask && repeatCount == 0)
|
||||
{
|
||||
mask = ([currentStartDate dayOfWeek]
|
||||
? (unsigned int) 1 << ([currentStartDate dayOfWeek])
|
||||
: iCalWeekDaySunday);
|
||||
if (([rrule byDayMask] & mask) != mask)
|
||||
if (![dayMask occursOnDay: [currentStartDate dayOfWeek]])
|
||||
wrongDay = YES;
|
||||
}
|
||||
|
||||
if (wrongDay == NO)
|
||||
if (isFirStart == YES || wrongDay == NO)
|
||||
{
|
||||
currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]];
|
||||
r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate
|
||||
|
@ -115,6 +168,13 @@
|
|||
|
||||
currentStartDate = [firStart dateByAddingYears: 0 months: 0
|
||||
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++;
|
||||
}
|
||||
return ranges;
|
||||
|
@ -123,15 +183,30 @@
|
|||
- (NSCalendarDate *) lastInstanceStartDate
|
||||
{
|
||||
NSCalendarDate *firStart, *lastInstanceStartDate;
|
||||
NGCalendarDateRange *r;
|
||||
NSArray *instances;
|
||||
|
||||
lastInstanceStartDate = nil;
|
||||
if ([rrule repeatCount] > 0)
|
||||
{
|
||||
if ([rrule hasByMask])
|
||||
{
|
||||
// Must perform the complete calculation
|
||||
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
|
||||
days: ([rrule repeatInterval]
|
||||
* ([rrule repeatCount] - 1))];
|
||||
}
|
||||
}
|
||||
else
|
||||
lastInstanceStartDate = [super lastInstanceStartDate];
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright (C) 2004-2007 SKYRIX Software AG
|
||||
Copyright (C) 2007 Helge Hess
|
||||
Copyright (C) 2010 Inverse inc.
|
||||
|
||||
This file is part of SOPE.
|
||||
|
||||
|
@ -29,8 +30,11 @@
|
|||
|
||||
#import <NGExtensions/NGCalendarDateRange.h>
|
||||
#import "iCalRecurrenceRule.h"
|
||||
#import "iCalByDayMask.h"
|
||||
#import "NSCalendarDate+ICal.h"
|
||||
|
||||
#import <string.h>
|
||||
#import <math.h>
|
||||
|
||||
@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)
|
||||
|
||||
{
|
||||
/* list of days in the month */
|
||||
unsigned i, count;
|
||||
BOOL ok;
|
||||
|
||||
NGMonthDaySet_clear (daySet);
|
||||
NGMonthDaySet_clear (positiveDaySet);
|
||||
NGMonthDaySet_clear (negativeDaySet);
|
||||
|
||||
for (i = 0, count = [byMonthDay count], ok = YES; i < count; i++)
|
||||
{
|
||||
|
@ -107,12 +116,9 @@ static BOOL NGMonthDaySet_fillWithByMonthDay (NGMonthDaySet *daySet,
|
|||
/* adjust negative days */
|
||||
|
||||
if (dayInMonth < 0)
|
||||
{
|
||||
/* eg: -1 == last day in month, 30 days => 30 */
|
||||
dayInMonth = 32 - dayInMonth /* because we count from 1 */;
|
||||
}
|
||||
|
||||
(*daySet)[dayInMonth] = YES;
|
||||
(*negativeDaySet)[abs(dayInMonth)] = YES;
|
||||
else
|
||||
(*positiveDaySet)[dayInMonth] = YES;
|
||||
}
|
||||
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
|
||||
limitDate: (NSCalendarDate *)_until
|
||||
limitRange: (NGCalendarDateRange *)_r
|
||||
|
@ -238,9 +150,6 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
|||
|
||||
/* 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)
|
||||
{
|
||||
/* 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];
|
||||
if ([_r containsDateRange: r])
|
||||
[_ranges addObject: r];
|
||||
[r release]; r = nil;
|
||||
[r release];
|
||||
r = nil;
|
||||
|
||||
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 *)
|
||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||
{
|
||||
/* main entry */
|
||||
// TODO: check whether this is OK for multiday-events!
|
||||
NSMutableArray *ranges;
|
||||
NSTimeZone *timeZone;
|
||||
NSCalendarDate *eventStartDate, *rStart, *rEnd, *until;
|
||||
NSCalendarDate *eventStartDate, *rStart, *rEnd, *until, *referenceDate;
|
||||
int eventDayOfMonth;
|
||||
unsigned monthIdxInRange, numberOfMonthsInRange, interval;
|
||||
int diff;
|
||||
NGMonthSet byMonthList = { // TODO: fill from rrule, this is the default
|
||||
/* enable all months of the year */
|
||||
unsigned monthIdxInRange, numberOfMonthsInRange, interval, repeatCount;
|
||||
int diff, count;
|
||||
NGMonthSet byMonthList = {
|
||||
// Enable all months of the year
|
||||
YES, YES, YES, YES, YES, YES,
|
||||
YES, YES, YES, YES, YES, YES
|
||||
};
|
||||
NSArray *byMonthDay; // array of ints (-31..-1 and 1..31)
|
||||
NGMonthDaySet byMonthDaySet;
|
||||
NSArray *byMonth, *byMonthDay; // array of ints (-31..-1 and 1..31)
|
||||
NGMonthDaySet byPositiveMonthDaySet, byNegativeMonthDaySet;
|
||||
iCalByDayMask *byDayMask;
|
||||
|
||||
eventStartDate = [firstRange startDate];
|
||||
eventDayOfMonth = [eventStartDate dayOfMonth];
|
||||
|
@ -289,48 +211,81 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
|||
rStart = [_r startDate];
|
||||
rEnd = [_r endDate];
|
||||
interval = [rrule repeatInterval];
|
||||
until = [self lastInstanceStartDate]; // TODO: maybe replace
|
||||
until = nil;
|
||||
repeatCount = [rrule repeatCount];
|
||||
byMonth = [rrule byMonth];
|
||||
byMonthDay = [rrule byMonthDay];
|
||||
byDayMask = [rrule byDayMask];
|
||||
diff = 0;
|
||||
|
||||
|
||||
/* check whether the range to be processed is beyond the 'until' date */
|
||||
if (until)
|
||||
if (![rrule isInfinite])
|
||||
{
|
||||
if ([until compare: rStart] == NSOrderedAscending) /* until before start */
|
||||
return nil;
|
||||
if ([until compare: rEnd] == NSOrderedDescending) /* end before until */
|
||||
rEnd = until; // TODO: why is that? end is _before_ until?
|
||||
if (repeatCount > 0 && ![rrule hasByMask])
|
||||
{
|
||||
// When there's no BYxxx mask, we can find the date of the last
|
||||
// occurrence.
|
||||
until = [eventStartDate dateByAddingYears: 0
|
||||
months: (interval * (repeatCount - 1))
|
||||
days: 0];
|
||||
}
|
||||
else
|
||||
{
|
||||
until = [rrule untilDate];
|
||||
}
|
||||
}
|
||||
|
||||
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 HEAVY_DEBUG
|
||||
NSLog (@"byMonthDay: %@", byMonthDay);
|
||||
#endif
|
||||
NGMonthDaySet_fillWithByMonthDay (&byMonthDaySet, byMonthDay);
|
||||
NGMonthDaySet_fillWithByMonthDay (&byPositiveMonthDaySet, &byNegativeMonthDaySet, byMonthDay);
|
||||
}
|
||||
|
||||
|
||||
// TODO: I think the 'diff' is to skip recurrence which are before the
|
||||
// requested range. Not sure whether this is actually possible, eg
|
||||
// the repeatCount must be processed from the start.
|
||||
if (repeatCount > 0)
|
||||
{
|
||||
numberOfMonthsInRange = [eventStartDate monthsBetweenDate: rEnd] + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
diff = [eventStartDate monthsBetweenDate: rStart];
|
||||
if ((diff != 0) && [rStart compare: eventStartDate] == NSOrderedAscending)
|
||||
diff = -diff;
|
||||
|
||||
numberOfMonthsInRange = [rStart monthsBetweenDate: rEnd] + 1;
|
||||
}
|
||||
|
||||
ranges = [NSMutableArray arrayWithCapacity: numberOfMonthsInRange];
|
||||
|
||||
/*
|
||||
Note: we do not add 'eventStartDate', this is intentional, the event date
|
||||
itself is _not_ necessarily part of the sequence, eg with monthly
|
||||
byday recurrences.
|
||||
*/
|
||||
// There's a bug in GNUstep in [NSCalendarDate dateByAddingYears:months:days:]
|
||||
// that causes errors when adding subsequently a month. For this reason,
|
||||
// we set the day of the reference date to 1.
|
||||
referenceDate = [NSCalendarDate dateWithYear: [eventStartDate yearOfCommonEra]
|
||||
month: [eventStartDate monthOfYear]
|
||||
day: 1
|
||||
hour: [eventStartDate hourOfDay]
|
||||
minute: [eventStartDate minuteOfHour]
|
||||
second: 0
|
||||
timeZone: [eventStartDate timeZone]];
|
||||
|
||||
for (monthIdxInRange = 0; monthIdxInRange < numberOfMonthsInRange;
|
||||
for (monthIdxInRange = 0, count = 0;
|
||||
monthIdxInRange < numberOfMonthsInRange;
|
||||
monthIdxInRange++)
|
||||
{
|
||||
NSCalendarDate *cursor;
|
||||
|
@ -349,63 +304,95 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
|||
if ((monthIdxInRecurrence % interval) != 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
Then the sequence is:
|
||||
- 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)
|
||||
cursor = [referenceDate dateByAddingYears: 0
|
||||
months: monthIdxInRecurrence
|
||||
days: 0];
|
||||
[cursor setTimeZone: timeZone];
|
||||
numDaysInMonth = [cursor numberOfDaysInMonth];
|
||||
|
||||
|
||||
/* check whether we match the bymonth specification */
|
||||
/* check whether we match the BYMONTH constraint */
|
||||
|
||||
if (!byMonthList[[cursor monthOfYear] - 1])
|
||||
continue;
|
||||
|
||||
|
||||
/* check 'day level' byXYZ rules */
|
||||
/* check whether we match the BYMONTHDAY and BYDAY constraints */
|
||||
|
||||
didByFill = NO;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if ([rrule byDayMask] != 0)
|
||||
{ // TODO: replace the mask with an array
|
||||
NGMonthDaySet ruleset;
|
||||
unsigned firstDoWInMonth;
|
||||
if (byDayMask)
|
||||
{
|
||||
unsigned int firstDoWInMonth, currentWeekDay;
|
||||
unsigned int weekDaysCount[7], currentWeekDaysCount[7];
|
||||
int i, positiveOrder, negativeOrder;
|
||||
|
||||
firstDoWInMonth = [[cursor firstDayOfMonth] dayOfWeek];
|
||||
|
||||
NGMonthDaySet_fillWithByDayX (&ruleset,
|
||||
[rrule byDayMask],
|
||||
firstDoWInMonth,
|
||||
[cursor numberOfDaysInMonth],
|
||||
[rrule byDayOccurence1]);
|
||||
NGMonthDaySet_copyOrUnion (&monthDays, &ruleset, !didByFill);
|
||||
if (!didByFill)
|
||||
NGMonthDaySet_clear (&monthDays);
|
||||
|
||||
// Fill weekDaysCount to handle negative positions
|
||||
currentWeekDay = firstDoWInMonth;
|
||||
memset(weekDaysCount, 0, 7 * sizeof(unsigned int));
|
||||
for (i = 1; i <= numDaysInMonth; i++)
|
||||
{
|
||||
weekDaysCount[currentWeekDay]++;
|
||||
currentWeekDay++;
|
||||
currentWeekDay = 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;
|
||||
}
|
||||
|
||||
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);
|
||||
monthDays[eventDayOfMonth] = YES;
|
||||
}
|
||||
|
||||
// TODO: add processing of byhour/byminute/bysecond etc
|
||||
|
||||
/*
|
||||
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
|
||||
|
@ -427,57 +414,18 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
|||
if (!monthDays[dom])
|
||||
continue;
|
||||
|
||||
// TODO: what is this good for?
|
||||
/*
|
||||
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
|
||||
start = [cursor dateByAddingYears: 0 months: 0 days: (dom - 1)];
|
||||
doCont = [self _addInstanceWithStartDate: start
|
||||
limitDate: until
|
||||
limitRange: _r
|
||||
toArray: ranges];
|
||||
//NSLog(@"*** MONTHLY [%i/%i] adding %@%@ (count = %i)", dom, numDaysInMonth, start, (doCont?@"":@" .. NOT!"), count);
|
||||
if (repeatCount > 0)
|
||||
{
|
||||
count++;
|
||||
//NSLog(@"MONTHLY count = %i/%i", count, repeatCount);
|
||||
doCont = (count < repeatCount);
|
||||
}
|
||||
}
|
||||
if (!doCont) break; /* reached some limit */
|
||||
}
|
||||
|
@ -487,16 +435,31 @@ static void NGMonthDaySet_fillWithByDayX (NGMonthDaySet *daySet,
|
|||
- (NSCalendarDate *) lastInstanceStartDate
|
||||
{
|
||||
NSCalendarDate *firStart, *lastInstanceStartDate;
|
||||
NGCalendarDateRange *r;
|
||||
NSArray *instances;
|
||||
|
||||
lastInstanceStartDate = nil;
|
||||
if ([rrule repeatCount] > 0)
|
||||
{
|
||||
if ([rrule hasByMask])
|
||||
{
|
||||
// Must perform the complete calculation
|
||||
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: ([rrule repeatInterval]
|
||||
* ([rrule repeatCount] - 1))
|
||||
days: 0];
|
||||
}
|
||||
}
|
||||
else
|
||||
lastInstanceStartDate = [super lastInstanceStartDate];
|
||||
|
||||
|
|
|
@ -340,7 +340,6 @@ static Class yearlyCalcClass = Nil;
|
|||
/*
|
||||
NOTE: this is horribly inaccurate and doesn't even consider the use
|
||||
of repeatCount. It MUST be implemented by subclasses properly!
|
||||
However, it does the trick for SOGo 1.0 - that's why it's left here.
|
||||
*/
|
||||
return [rrule untilDate];
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2010 Inverse inc.
|
||||
|
||||
This file is part of SOPE.
|
||||
|
||||
|
@ -46,34 +47,25 @@ typedef enum {
|
|||
} iCalRecurrenceFrequency;
|
||||
|
||||
typedef enum {
|
||||
iCalWeekDaySunday = 1,
|
||||
iCalWeekDayMonday = 2,
|
||||
iCalWeekDayTuesday = 4,
|
||||
iCalWeekDayWednesday = 8,
|
||||
iCalWeekDayThursday = 16,
|
||||
iCalWeekDayFriday = 32,
|
||||
iCalWeekDaySaturday = 64,
|
||||
iCalWeekDayUnknown = -1,
|
||||
iCalWeekDaySunday = 0,
|
||||
iCalWeekDayMonday = 1,
|
||||
iCalWeekDayTuesday = 2,
|
||||
iCalWeekDayWednesday = 3,
|
||||
iCalWeekDayThursday = 4,
|
||||
iCalWeekDayFriday = 5,
|
||||
iCalWeekDaySaturday = 6
|
||||
} iCalWeekDay;
|
||||
|
||||
extern NSString *iCalWeekDayString[];
|
||||
|
||||
@class NSString, NSCalendarDate, NGCalendarDateRange, NSArray;
|
||||
@class iCalByDayMask;
|
||||
|
||||
@interface iCalRecurrenceRule : CardElement
|
||||
// {
|
||||
// iCalRecurrenceFrequency frequency;
|
||||
// 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;
|
||||
// }
|
||||
{
|
||||
iCalByDayMask *dayMask;
|
||||
}
|
||||
|
||||
+ (id) recurrenceRuleWithICalRepresentation: (NSString *) _iCalRep;
|
||||
- (id) initWithString: (NSString *) _str;
|
||||
|
@ -92,11 +84,13 @@ typedef enum {
|
|||
- (void) setWeekStart: (iCalWeekDay) _weekStart;
|
||||
- (iCalWeekDay) weekStart;
|
||||
|
||||
- (void) setByDayMask: (unsigned int) _mask;
|
||||
- (unsigned int) byDayMask;
|
||||
- (int) byDayOccurence1;
|
||||
|
||||
- (void) setByDay: (NSString *) newByDay;
|
||||
- (NSString *) byDay;
|
||||
- (void) setByDayMask: (iCalByDayMask *) newMask;
|
||||
- (iCalByDayMask *) byDayMask;
|
||||
- (NSArray *) byMonthDay;
|
||||
- (NSArray *) byMonth;
|
||||
- (BOOL) hasByMask;
|
||||
|
||||
/* count and untilDate are mutually exclusive */
|
||||
|
||||
|
@ -112,8 +106,6 @@ typedef enum {
|
|||
|
||||
- (void) setRrule: (NSString *) _rrule; // TODO: weird name? (better: RRule?)
|
||||
|
||||
// - (NSString *)iCalRepresentation;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __NGiCal_iCalRecurrenceRule_H_ */
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2010 Inverse inc.
|
||||
|
||||
This file is part of SOPE.
|
||||
|
||||
|
@ -19,6 +20,168 @@
|
|||
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/NSEnumerator.h>
|
||||
#import <Foundation/NSException.h>
|
||||
|
@ -27,13 +190,16 @@
|
|||
|
||||
#import <ctype.h>
|
||||
|
||||
#import "NSCalendarDate+ICal.h"
|
||||
#import "NSCalendarDate+NGCards.h"
|
||||
#import "NSString+NGCards.h"
|
||||
|
||||
#import "NSCalendarDate+ICal.h"
|
||||
|
||||
#import "iCalByDayMask.h"
|
||||
#import "iCalRecurrenceRule.h"
|
||||
|
||||
NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR",
|
||||
@"SA" };
|
||||
|
||||
/*
|
||||
freq = rrFreq;
|
||||
until = rrUntil;
|
||||
|
@ -60,11 +226,6 @@
|
|||
- (NSString *) wkst;
|
||||
- (NSString *) byDayList;
|
||||
|
||||
// - (void)_parseRuleString:(NSString *)_rrule;
|
||||
|
||||
/* currently used by parser, should be removed (replace with an -init..) */
|
||||
- (void)setByday:(NSString *)_byDayList;
|
||||
|
||||
@end
|
||||
|
||||
@implementation iCalRecurrenceRule
|
||||
|
@ -85,6 +246,7 @@
|
|||
if ((self = [super init]) != nil)
|
||||
{
|
||||
[self setTag: @"rrule"];
|
||||
dayMask = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -100,6 +262,12 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[dayMask release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) setRrule: (NSString *) _rrule
|
||||
{
|
||||
NSEnumerator *newValues;
|
||||
|
@ -273,54 +441,30 @@
|
|||
return [self weekDayFromICalRepresentation: [self wkst]];
|
||||
}
|
||||
|
||||
- (void) setByDayMask: (unsigned) _mask
|
||||
- (void) setByDay: (NSString *) newByDay
|
||||
{
|
||||
NSMutableArray *days;
|
||||
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: newByDay];
|
||||
}
|
||||
|
||||
[self setNamedValue: @"byday" to: [days componentsJoinedByString: @","]];
|
||||
- (NSString *) byDay
|
||||
{
|
||||
return [self namedValue: @"byday"];
|
||||
}
|
||||
|
||||
- (unsigned int) byDayMask
|
||||
- (void) setByDayMask: (iCalByDayMask *) newByDayMask
|
||||
{
|
||||
NSArray *days;
|
||||
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];
|
||||
}
|
||||
[self setByDay: [newByDayMask asRuleString]];
|
||||
}
|
||||
|
||||
return mask;
|
||||
- (iCalByDayMask *) byDayMask
|
||||
{
|
||||
if (dayMask == nil && [[self byDay] length])
|
||||
{
|
||||
dayMask = [iCalByDayMask byDayMaskWithRuleString: [self byDay]];
|
||||
[dayMask retain];
|
||||
}
|
||||
|
||||
#warning this is bad
|
||||
- (int) byDayOccurence1
|
||||
{
|
||||
return 0;
|
||||
// return byDayOccurence1;
|
||||
return dayMask;
|
||||
}
|
||||
|
||||
- (NSArray *) byMonthDay
|
||||
|
@ -337,6 +481,35 @@
|
|||
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
|
||||
{
|
||||
return !([self repeatCount] || [self untilDate]);
|
||||
|
@ -356,6 +529,7 @@
|
|||
dayLength = [_day length];
|
||||
if (dayLength > 1)
|
||||
{
|
||||
// Ignore any prefix, only consider last two characters
|
||||
[[_day uppercaseString] getCharacters: chars
|
||||
range: NSMakeRange (dayLength - 2, 2)];
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#import "iCalDateTime.h"
|
||||
#import "iCalRecurrenceRule.h"
|
||||
#import "iCalByDayMask.h"
|
||||
|
||||
#import "iCalTimeZonePeriod.h"
|
||||
|
||||
|
@ -88,16 +89,16 @@
|
|||
return ((negative) ? -seconds : seconds);
|
||||
}
|
||||
|
||||
- (unsigned int) dayOfWeekFromRruleDay: (iCalWeekDay) day
|
||||
{
|
||||
unsigned int dayOfWeek;
|
||||
// - (unsigned int) dayOfWeekFromRruleDay: (iCalWeekDay) day
|
||||
// {
|
||||
// unsigned int dayOfWeek;
|
||||
|
||||
dayOfWeek = 0;
|
||||
while (day >> (dayOfWeek + 1))
|
||||
dayOfWeek++;
|
||||
// dayOfWeek = 0;
|
||||
// while (day >> (dayOfWeek + 1))
|
||||
// dayOfWeek++;
|
||||
|
||||
return dayOfWeek;
|
||||
}
|
||||
// return dayOfWeek;
|
||||
// }
|
||||
|
||||
- (NSCalendarDate *) startDate
|
||||
{
|
||||
|
@ -105,22 +106,33 @@
|
|||
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
|
||||
byRRule: (iCalRecurrenceRule *) rrule
|
||||
{
|
||||
NSCalendarDate *tmpDate;
|
||||
NSString *byDay;
|
||||
iCalByDayMask *byDayMask;
|
||||
int dayOfWeek, dateDayOfWeek, offset, pos;
|
||||
NSCalendarDate *tzStart;
|
||||
|
||||
byDay = [rrule namedValue: @"byday"];
|
||||
dayOfWeek = [self dayOfWeekFromRruleDay: [rrule byDayMask]];
|
||||
pos = [[byDay substringToIndex: 2] intValue];
|
||||
if (!pos)
|
||||
/* if byday = "SU", instead of "1SU"... */
|
||||
byDayMask = [rrule byDayMask];
|
||||
dayOfWeek = 0;
|
||||
|
||||
if (byDayMask == nil)
|
||||
{
|
||||
dayOfWeek = 0;
|
||||
pos = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dayOfWeek = (int)[byDayMask firstDay];
|
||||
pos = [byDayMask firstOccurrence];
|
||||
}
|
||||
|
||||
tzStart = [self startDate];
|
||||
[tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2010 Inverse inc.
|
||||
|
||||
This file is part of SOPE.
|
||||
|
||||
|
@ -28,6 +29,7 @@
|
|||
|
||||
#import <NGExtensions/NGCalendarDateRange.h>
|
||||
#import "iCalRecurrenceRule.h"
|
||||
#import "iCalByDayMask.h"
|
||||
#import "NSCalendarDate+ICal.h"
|
||||
|
||||
@interface iCalRecurrenceCalculator (PrivateAPI)
|
||||
|
@ -42,22 +44,36 @@
|
|||
|
||||
@end
|
||||
|
||||
/*
|
||||
TODO: If BYDAY is specified, lastInstanceStartDate and recurrences will
|
||||
differ significantly!
|
||||
*/
|
||||
@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
|
||||
{
|
||||
NSMutableArray *ranges;
|
||||
NSCalendarDate *firStart, *startDate, *endDate, *currentStartDate, *currentEndDate;
|
||||
long i;
|
||||
unsigned interval, byDayMask;
|
||||
long i, repeatCount, count;
|
||||
unsigned interval;
|
||||
iCalByDayMask *dayMask;
|
||||
|
||||
firStart = [firstRange startDate];
|
||||
startDate = [_r startDate];
|
||||
endDate = [_r endDate];
|
||||
dayMask = nil;
|
||||
repeatCount = 0;
|
||||
|
||||
if ([endDate compare: firStart] == NSOrderedAscending)
|
||||
// Range ends before first occurrence
|
||||
|
@ -65,19 +81,31 @@
|
|||
|
||||
interval = [rrule repeatInterval];
|
||||
|
||||
if ([[rrule byDay] length])
|
||||
dayMask = [rrule byDayMask];
|
||||
|
||||
// If rule is bound, check the bounds
|
||||
if (![rrule isInfinite])
|
||||
{
|
||||
NSCalendarDate *until, *lastDate;
|
||||
|
||||
lastDate = nil;
|
||||
until = [rrule untilDate];
|
||||
if (until)
|
||||
lastDate = until;
|
||||
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
|
||||
days: (interval
|
||||
* ([rrule repeatCount] - 1) * 7)];
|
||||
* (repeatCount - 1) * 7)];
|
||||
}
|
||||
|
||||
if (lastDate != nil)
|
||||
{
|
||||
if ([lastDate compare: startDate] == NSOrderedAscending)
|
||||
// Range starts after last occurrence
|
||||
return nil;
|
||||
|
@ -85,13 +113,15 @@
|
|||
// Range ends after last occurence; adjust end date
|
||||
endDate = lastDate;
|
||||
}
|
||||
}
|
||||
|
||||
currentStartDate = [firStart copy];
|
||||
[currentStartDate autorelease];
|
||||
ranges = [NSMutableArray array];
|
||||
byDayMask = [rrule byDayMask];
|
||||
i = 1;
|
||||
if (!byDayMask)
|
||||
count = 0;
|
||||
|
||||
if (dayMask == nil)
|
||||
{
|
||||
while ([currentStartDate compare: endDate] == NSOrderedAscending ||
|
||||
[currentStartDate compare: endDate] == NSOrderedSame)
|
||||
|
@ -115,42 +145,41 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
unsigned dayOfWeek;
|
||||
NGCalendarDateRange *r;
|
||||
|
||||
while ([currentStartDate compare: endDate] == NSOrderedAscending ||
|
||||
[currentStartDate compare: endDate] == NSOrderedSame)
|
||||
{
|
||||
if ([startDate compare: currentStartDate] == NSOrderedAscending ||
|
||||
[startDate compare: currentStartDate] == NSOrderedSame)
|
||||
{
|
||||
BOOL isRecurrence = NO;
|
||||
int days, week;
|
||||
|
||||
if (repeatCount > 0 ||
|
||||
[startDate compare: currentStartDate] == NSOrderedAscending ||
|
||||
[startDate compare: currentStartDate] == NSOrderedSame)
|
||||
{
|
||||
// If the rule count is defined, stop once the count is reached.
|
||||
if (i == 1)
|
||||
{
|
||||
// Always add the start date the recurring event if within
|
||||
// the lookup range.
|
||||
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)
|
||||
{
|
||||
// Date is in the proper week with respect to the
|
||||
// week interval
|
||||
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.
|
||||
dayOfWeek = ([currentStartDate dayOfWeek]
|
||||
? (unsigned int) 1 << [currentStartDate dayOfWeek]
|
||||
: iCalWeekDaySunday);
|
||||
if (dayOfWeek & [rrule byDayMask])
|
||||
if ((week % interval) == 0 &&
|
||||
[dayMask occursOnDay: [currentStartDate dayOfWeek]])
|
||||
isRecurrence = YES;
|
||||
}
|
||||
|
||||
if (isRecurrence)
|
||||
{
|
||||
count++;
|
||||
if (repeatCount > 0 && count > repeatCount)
|
||||
break;
|
||||
currentEndDate = [currentStartDate addTimeInterval: [firstRange duration]];
|
||||
r = [NGCalendarDateRange calendarDateRangeWithStartDate: currentStartDate
|
||||
endDate: currentEndDate];
|
||||
|
@ -158,10 +187,10 @@
|
|||
[ranges addObject: r];
|
||||
}
|
||||
}
|
||||
}
|
||||
currentStartDate = [currentStartDate dateByAddingYears: 0
|
||||
months: 0
|
||||
days: 1];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,14 +200,18 @@
|
|||
- (NSCalendarDate *) lastInstanceStartDate
|
||||
{
|
||||
NSCalendarDate *firStart, *lastInstanceStartDate;
|
||||
NGCalendarDateRange *r;
|
||||
NSArray *instances;
|
||||
|
||||
lastInstanceStartDate = nil;
|
||||
if ([rrule repeatCount] > 0)
|
||||
{
|
||||
firStart = [firstRange startDate];
|
||||
|
||||
lastInstanceStartDate = [firStart dateByAddingYears: 0 months: 0
|
||||
days: (7 * [rrule repeatInterval]
|
||||
* ([rrule repeatCount] - 1))];
|
||||
r = [NGCalendarDateRange calendarDateRangeWithStartDate: firStart
|
||||
endDate: [NSCalendarDate distantFuture]];
|
||||
instances = [self recurrenceRangesWithinCalendarDateRange: r];
|
||||
if ([instances count])
|
||||
lastInstanceStartDate = [(NGCalendarDateRange *)[instances lastObject] startDate];
|
||||
}
|
||||
else
|
||||
lastInstanceStartDate = [super lastInstanceStartDate];
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright (C) 2004-2005 SKYRIX Software AG
|
||||
Copyright (C) 2006-2010 Inverse inc.
|
||||
|
||||
This file is part of SOPE.
|
||||
|
||||
|
@ -34,74 +35,231 @@
|
|||
- (NSCalendarDate *) lastInstanceStartDate;
|
||||
@end
|
||||
|
||||
@class iCalMonthlyRecurrenceCalculator;
|
||||
|
||||
@implementation iCalYearlyRecurrenceCalculator
|
||||
|
||||
- (NSArray *)
|
||||
recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
|
||||
{
|
||||
NSMutableArray *ranges;
|
||||
NSCalendarDate *firStart, *rStart, *rEnd, *until;
|
||||
unsigned i, count, interval;
|
||||
int diff;
|
||||
NSArray *byMonth;
|
||||
NSCalendarDate *firStart, *lastDate, *rStart, *rEnd, *until, *referenceDate;
|
||||
iCalMonthlyRecurrenceCalculator *monthlyCalc;
|
||||
unsigned j, yearIdxInRange, numberOfYearsInRange, count, interval, monthDiff;
|
||||
int diff, repeatCount, currentMonth;
|
||||
|
||||
firStart = [firstRange startDate];
|
||||
rStart = [_r startDate];
|
||||
rEnd = [_r endDate];
|
||||
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 compare: rStart] == NSOrderedAscending)
|
||||
return nil;
|
||||
if ([until compare: rEnd] == NSOrderedDescending)
|
||||
rEnd = until;
|
||||
lastDate = until;
|
||||
}
|
||||
if (repeatCount > 0)
|
||||
{
|
||||
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];
|
||||
if ((diff != 0) && [rStart compare: firStart] == NSOrderedAscending)
|
||||
diff = -diff;
|
||||
referenceDate = rStart;
|
||||
}
|
||||
|
||||
count = [rStart yearsBetweenDate: rEnd] + 1;
|
||||
ranges = [NSMutableArray arrayWithCapacity: count];
|
||||
for (i = 0 ; i < count; i++)
|
||||
// Initialize array to return with an approximation of the number total
|
||||
// number of possible matches, ie the number of years spawned by the period.
|
||||
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)
|
||||
{
|
||||
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;
|
||||
NGCalendarDateRange *r;
|
||||
|
||||
start = [firStart dateByAddingYears: diff + i
|
||||
start = [firStart dateByAddingYears: diff + yearIdxInRange
|
||||
months: 0
|
||||
days: 0];
|
||||
[start setTimeZone: [firStart timeZone]];
|
||||
end = [start addTimeInterval: [firstRange duration]];
|
||||
r = [NGCalendarDateRange calendarDateRangeWithStartDate: start
|
||||
endDate: end];
|
||||
if ([_r containsDateRange: r])
|
||||
if ([_r containsDateRange: r] && (repeatCount == 0 || count < repeatCount))
|
||||
{
|
||||
[ranges addObject: r];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (byMonth && repeatCount > 0)
|
||||
// Restore the repeat count
|
||||
[rrule setRepeatCount: repeatCount];
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
- (NSCalendarDate *) lastInstanceStartDate
|
||||
{
|
||||
NSCalendarDate *firStart, *lastInstanceStartDate;
|
||||
NGCalendarDateRange *r;
|
||||
NSArray *instances;
|
||||
|
||||
lastInstanceStartDate = nil;
|
||||
if ([rrule repeatCount] > 0)
|
||||
{
|
||||
if ([rrule hasByMask])
|
||||
{
|
||||
// Must perform the complete calculation
|
||||
firStart = [firstRange startDate];
|
||||
|
||||
lastInstanceStartDate
|
||||
= [firStart dateByAddingYears: ([rrule repeatInterval]
|
||||
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: ([rrule repeatInterval]
|
||||
* ([rrule repeatCount] - 1))
|
||||
months: 0
|
||||
days: 0];
|
||||
}
|
||||
}
|
||||
else
|
||||
lastInstanceStartDate = [super lastInstanceStartDate];
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* 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>
|
||||
*
|
||||
|
@ -419,13 +419,11 @@
|
|||
{
|
||||
WOResponse *result;
|
||||
NSDictionary *data;
|
||||
NSCalendarDate *firstDate, *eventDate;
|
||||
NSCalendarDate *eventDate;
|
||||
NSTimeZone *timeZone;
|
||||
SOGoUserDefaults *ud;
|
||||
SOGoCalendarComponent *co;
|
||||
iCalEvent *master;
|
||||
BOOL resetAlarm;
|
||||
signed int daylightOffset;
|
||||
|
||||
[self event];
|
||||
|
||||
|
@ -457,20 +455,6 @@
|
|||
[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:
|
||||
[componentCalendar displayName], @"calendar",
|
||||
[event tag], @"component",
|
||||
|
|
|
@ -273,6 +273,7 @@ static NSArray *tasksFields = nil;
|
|||
static NSString *fields[] = { @"startDate", @"c_startdate",
|
||||
@"endDate", @"c_enddate" };
|
||||
|
||||
if (dayBasedView)
|
||||
for (count = 0; count < 2; count++)
|
||||
{
|
||||
aDateField = fields[count * 2];
|
||||
|
@ -291,9 +292,9 @@ static NSArray *tasksFields = nil;
|
|||
|
||||
aDateValue = [aRecord objectForKey: @"c_recurrence_id"];
|
||||
aDate = [aRecord objectForKey: @"cycleStartDate"];
|
||||
aStartDate = [aRecord objectForKey: @"startDate"];
|
||||
if (aDateValue && aDate)
|
||||
{
|
||||
aStartDate = [aRecord objectForKey: @"startDate"];
|
||||
if ([userTimeZone isDaylightSavingTimeForDate: aStartDate] !=
|
||||
[userTimeZone isDaylightSavingTimeForDate: aDate])
|
||||
{
|
||||
|
@ -387,8 +388,6 @@ static NSArray *tasksFields = nil;
|
|||
forKey: @"c_owner"];
|
||||
if (![[newInfo objectForKey: @"c_title"] length])
|
||||
[self _fixComponentTitle: newInfo withType: component];
|
||||
if (dayBasedView
|
||||
|| [[newInfo objectForKey: @"c_isallday"] boolValue])
|
||||
// Possible improvement: only call _fixDates if event is recurrent
|
||||
// or the view range span a daylight saving time change
|
||||
[self _fixDates: newInfo];
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#import <Foundation/NSURL.h>
|
||||
|
||||
#import <NGCards/iCalAlarm.h>
|
||||
#import <NGCards/iCalByDayMask.h>
|
||||
#import <NGCards/iCalPerson.h>
|
||||
#import <NGCards/iCalRepeatableEntityObject.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
|
||||
{
|
||||
SOGoUserDefaults *ud;
|
||||
|
@ -326,15 +308,12 @@ iRANGE(2);
|
|||
|
||||
rule = [[component recurrenceRules] lastObject];
|
||||
|
||||
/* DAILY */
|
||||
if ([rule frequency] == iCalRecurrenceFrequenceDaily)
|
||||
{
|
||||
repeatType = @"0";
|
||||
|
||||
if ([rule byDayMask] == (iCalWeekDayMonday
|
||||
| iCalWeekDayTuesday
|
||||
| iCalWeekDayWednesday
|
||||
| iCalWeekDayThursday
|
||||
| iCalWeekDayFriday))
|
||||
if ([[rule byDayMask] isWeekDays])
|
||||
{
|
||||
if ([rule isInfinite])
|
||||
repeat = @"EVERY WEEKDAY";
|
||||
|
@ -350,12 +329,14 @@ iRANGE(2);
|
|||
[self setRepeat2: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
||||
}
|
||||
}
|
||||
|
||||
/* WEEKLY */
|
||||
else if ([rule frequency] == iCalRecurrenceFrequenceWeekly)
|
||||
{
|
||||
repeatType = @"1";
|
||||
[self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
||||
|
||||
if (![rule byDayMask])
|
||||
if (![[rule byDay] length])
|
||||
{
|
||||
if ([rule repeatInterval] == 1)
|
||||
repeat = @"WEEKLY";
|
||||
|
@ -364,45 +345,81 @@ iRANGE(2);
|
|||
}
|
||||
else
|
||||
{
|
||||
[self setRepeat2: [self _dayMaskToInteger: [rule byDayMask]]];
|
||||
[self setRepeat2: [[rule byDayMask] asRuleStringWithIntegers]];
|
||||
}
|
||||
}
|
||||
|
||||
/* MONTHLY */
|
||||
else if ([rule frequency] == iCalRecurrenceFrequenceMonthly)
|
||||
{
|
||||
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 setRepeat3: [NSString stringWithFormat: @"%d", firstOccurrence]];
|
||||
[self setRepeat4: [NSString stringWithFormat: @"%d", [dayMask firstDay]]];
|
||||
}
|
||||
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 setRepeat5: [[rule byMonthDay] componentsJoinedByString: @","]];
|
||||
}
|
||||
}
|
||||
else if ([rule repeatInterval] == 1)
|
||||
repeat = @"MONTHLY";
|
||||
|
||||
[self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]];
|
||||
}
|
||||
|
||||
/* YEARLY */
|
||||
else
|
||||
{
|
||||
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 setRepeat3: [rule namedValue: @"bymonthday"]];
|
||||
[self setRepeat4: [NSString stringWithFormat: @"%d", [[rule namedValue: @"bymonth"] intValue]-1]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
[self setRepeat2: @"1"];
|
||||
}
|
||||
}
|
||||
else if ([rule repeatInterval] == 1)
|
||||
repeat = @"YEARLY";
|
||||
|
@ -410,7 +427,7 @@ iRANGE(2);
|
|||
[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])
|
||||
{
|
||||
repeat = @"CUSTOM";
|
||||
|
@ -1639,9 +1656,9 @@ RANGE(2);
|
|||
|
||||
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
|
||||
// 1 -> Every weekday
|
||||
//
|
||||
|
@ -1653,11 +1670,7 @@ RANGE(2);
|
|||
|
||||
if ([[self repeat1] intValue] > 0)
|
||||
{
|
||||
[theRule setByDayMask: (iCalWeekDayMonday
|
||||
|iCalWeekDayTuesday
|
||||
|iCalWeekDayWednesday
|
||||
|iCalWeekDayThursday
|
||||
|iCalWeekDayFriday)];
|
||||
[theRule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1670,7 +1683,7 @@ RANGE(2);
|
|||
}
|
||||
break;
|
||||
|
||||
// WEEKLY
|
||||
// WEEKLY (1)
|
||||
//
|
||||
// repeat1 holds the value of "Every X week(s)"
|
||||
//
|
||||
|
@ -1683,7 +1696,8 @@ RANGE(2);
|
|||
if ([[self repeat1] intValue] > 0)
|
||||
{
|
||||
NSArray *v;
|
||||
int c, mask;
|
||||
int c, day;
|
||||
iCalWeekOccurrences days;
|
||||
|
||||
[theRule setFrequency: iCalRecurrenceFrequenceWeekly];
|
||||
[theRule setInterval: [self repeat1]];
|
||||
|
@ -1692,18 +1706,21 @@ RANGE(2);
|
|||
{
|
||||
v = [[self repeat2] componentsSeparatedByString: @","];
|
||||
c = [v count];
|
||||
mask = 0;
|
||||
memset(days, 0, 7 * sizeof(iCalWeekOccurrence));
|
||||
|
||||
while (c--)
|
||||
mask |= 1 << ([[v objectAtIndex: c] intValue]);
|
||||
|
||||
[theRule setByDayMask: mask];
|
||||
{
|
||||
day = [[v objectAtIndex: c] intValue];
|
||||
if (day >= 0 && day <= 7)
|
||||
days[day] = iCalWeekOccurrenceAll;
|
||||
}
|
||||
[theRule setByDayMask: [iCalByDayMask byDayMaskWithDays: days]];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// MONTHLY
|
||||
// MONTHLY (2)
|
||||
//
|
||||
// repeat1 holds the value of "Every X month(s)"
|
||||
//
|
||||
|
@ -1731,20 +1748,29 @@ RANGE(2);
|
|||
[theRule setInterval: [self repeat1]];
|
||||
|
||||
// We recur on specific days...
|
||||
if ([[self repeat2] intValue] == 1
|
||||
&& [[self repeat5] intValue] > 0)
|
||||
if ([[self repeat2] 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
|
||||
{
|
||||
// TODO
|
||||
if ([[self repeat5] intValue] > 0)
|
||||
[theRule setNamedValue: @"bymonthday" to: [self repeat5]];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// YEARLY
|
||||
// YEARLY (3)
|
||||
//
|
||||
// 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 ... )
|
||||
// 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..)
|
||||
// repeat7 holds the value of the MONTH parameter (0 -> January, 1 -> February ... )
|
||||
//
|
||||
|
@ -1771,7 +1797,19 @@ RANGE(2);
|
|||
// We recur Every .. of ..
|
||||
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
|
||||
{
|
||||
|
@ -1883,11 +1921,7 @@ RANGE(2);
|
|||
}
|
||||
else if ([repeat caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame)
|
||||
{
|
||||
[rule setByDayMask: (iCalWeekDayMonday
|
||||
|iCalWeekDayTuesday
|
||||
|iCalWeekDayWednesday
|
||||
|iCalWeekDayThursday
|
||||
|iCalWeekDayFriday)];
|
||||
[rule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
|
||||
[rule setFrequency: iCalRecurrenceFrequenceDaily];
|
||||
}
|
||||
else if ([repeat caseInsensitiveCompare: @"MONTHLY"] == NSOrderedSame
|
||||
|
|
|
@ -241,11 +241,6 @@ function handleMonthlyRecurrence() {
|
|||
|
||||
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
|
||||
var showError = true;
|
||||
|
||||
|
@ -265,7 +260,6 @@ function handleMonthlyRecurrence() {
|
|||
|
||||
if (showError)
|
||||
window.alert(monthFieldInvalid);
|
||||
}
|
||||
|
||||
return validate;
|
||||
}
|
||||
|
@ -279,16 +273,21 @@ function validateYearlyRecurrence() {
|
|||
// We check if the yearlyYearsField really contains an integer
|
||||
var v = parseInt(fieldValue);
|
||||
if (!isNaN(v) && v > 0) {
|
||||
var radioValue = $('recurrence_form').getRadioValue('yearlyRadioButtonName');
|
||||
if (radioValue == 0) {
|
||||
errorToShow = 1;
|
||||
fieldValue = "" + $('yearlyDayField').value;
|
||||
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);
|
||||
if (!isNaN(v) && v > 0) {
|
||||
errorToShow = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
errorToShow = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorToShow > -1)
|
||||
|
@ -300,16 +299,11 @@ function validateYearlyRecurrence() {
|
|||
function handleYearlyRecurrence() {
|
||||
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()) {
|
||||
var radioValue = $('recurrence_form').getRadioValue('yearlyRadioButtonName');
|
||||
var fieldValue = "" + $('yearlyYearsField').value;
|
||||
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);
|
||||
if (!isNaN(v) && v > 0) {
|
||||
validate = true;
|
||||
|
@ -326,7 +320,6 @@ function handleYearlyRecurrence() {
|
|||
}
|
||||
else
|
||||
validate = false;
|
||||
}
|
||||
|
||||
return validate;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue