From b8930b7bd077a55df029b6758fa3dfef963f1de1 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 23 Apr 2012 21:17:54 +0000 Subject: [PATCH] Monotone-Parent: f44990050ab038029a3fa83c4918912da401bd4b Monotone-Revision: 5626a85621c5f1c132d788ea29e164c8fc44cfdf Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-04-23T21:17:54 --- SOPE/NGCards/ChangeLog | 7 ++ .../NGCards/iCalMonthlyRecurrenceCalculator.m | 118 ++++++++++++------ SOPE/NGCards/iCalRecurrenceRule.h | 2 + SOPE/NGCards/iCalRecurrenceRule.m | 14 +++ Tests/Unit/TestiCalRecurrenceCalculator.m | 17 +++ 5 files changed, 117 insertions(+), 41 deletions(-) diff --git a/SOPE/NGCards/ChangeLog b/SOPE/NGCards/ChangeLog index ec7083fbe..5e7f0c1ca 100644 --- a/SOPE/NGCards/ChangeLog +++ b/SOPE/NGCards/ChangeLog @@ -1,3 +1,10 @@ +2012-04-23 Wolfgang Sourdeau + + * iCalMonthlyRecurrenceCalculator.m (NGMonthDaySet_clear): make + use of memset, which is expected to be faster. + (-recurrenceRangesWithinCalendarDateRange:): added handling of + "BYSETPOS" for "BYDAY" sets. + 2012-04-20 Wolfgang Sourdeau * iCalTrigger.m (-nextAlarmDate): new method including most of diff --git a/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m b/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m index 67be4069a..3b6577ee8 100644 --- a/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m +++ b/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m @@ -21,6 +21,7 @@ 02111-1307, USA. */ +#import #import #import "iCalRecurrenceCalculator.h" @@ -34,7 +35,6 @@ #import "NSCalendarDate+ICal.h" #import -#import @interface iCalRecurrenceCalculator (PrivateAPI) @@ -49,14 +49,11 @@ typedef BOOL NGMonthSet[12]; typedef BOOL NGMonthDaySet[32]; // 0 is unused -static void +static inline void NGMonthDaySet_clear (NGMonthDaySet *daySet) { - register unsigned i; - - for (i = 0; i <= 31; i++) - (*daySet)[i] = NO; + memset (daySet, 0, sizeof (NGMonthDaySet)); } static void @@ -201,7 +198,7 @@ static inline unsigned iCalDoWForNSDoW (int dow) YES, YES, YES, YES, YES, YES, YES, YES, YES, YES, YES, YES }; - NSArray *byMonth, *byMonthDay; // array of ints (-31..-1 and 1..31) + NSArray *byMonth, *byMonthDay, *bySetPos; // array of ints (-31..-1 and 1..31) NGMonthDaySet byPositiveMonthDaySet, byNegativeMonthDaySet; iCalByDayMask *byDayMask; @@ -216,6 +213,7 @@ static inline unsigned iCalDoWForNSDoW (int dow) byMonth = [rrule byMonth]; byMonthDay = [rrule byMonthDay]; byDayMask = [rrule byDayMask]; + bySetPos = [rrule bySetPos]; diff = 0; if (![rrule isInfinite]) @@ -239,7 +237,7 @@ static inline unsigned iCalDoWForNSDoW (int dow) if ([until compare: rStart] == NSOrderedAscending) // Range starts after last occurrence return nil; - if ([until compare: rEnd] == NSOrderedDescending) + if ([until compare: rEnd] == NSOrderedAscending) // Range ends after last occurence; adjust end date rEnd = until; } @@ -334,43 +332,81 @@ static inline unsigned iCalDoWForNSDoW (int dow) if (byDayMask) { - unsigned int firstDoWInMonth, currentWeekDay; - unsigned int weekDaysCount[7], currentWeekDaysCount[7]; - int i, positiveOrder, negativeOrder; + if (!didByFill) + NGMonthDaySet_clear (&monthDays); - firstDoWInMonth = [[cursor firstDayOfMonth] dayOfWeek]; + if (bySetPos) + { + NSUInteger monthDay; + NSInteger currentPos; + iCalWeekDay currentWeekDay; - if (!didByFill) - NGMonthDaySet_clear (&monthDays); + currentWeekDay = [[cursor firstDayOfMonth] dayOfWeek]; + currentPos = 1; + for (monthDay = 0; monthDay <= numDaysInMonth; monthDay++) + { + if ([byDayMask occursOnDay: currentWeekDay]) + { + if ([bySetPos containsObject: + [NSString stringWithFormat: @"%d", currentPos]]) + monthDays[monthDay+1] = YES; + currentPos++; + } + currentWeekDay = (currentWeekDay + 1) % 7; + } - // 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 = [[cursor lastDayOfMonth] dayOfWeek]; + currentPos = -1; + for (monthDay = numDaysInMonth; monthDay > 0; monthDay--) + { + if ([byDayMask occursOnDay: currentWeekDay]) + { + if ([bySetPos containsObject: + [NSString stringWithFormat: @"%d", currentPos]]) + monthDays[monthDay] = YES; + currentPos--; + } + if (currentWeekDay > 0) + currentWeekDay--; + else + currentWeekDay = 6; + } + } + else + { + unsigned int firstDoWInMonth, currentWeekDay; + unsigned int weekDaysCount[7], currentWeekDaysCount[7]; + int i, positiveOrder, negativeOrder; - 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; + firstDoWInMonth = [[cursor firstDayOfMonth] dayOfWeek]; + + // Fill weekDaysCount to handle negative positions + currentWeekDay = firstDoWInMonth; + memset(weekDaysCount, 0, 7 * sizeof(unsigned int)); + for (i = 1; i <= numDaysInMonth; i++) + { + weekDaysCount[currentWeekDay]++; + currentWeekDay = (currentWeekDay + 1) % 7; + } + + currentWeekDay = firstDoWInMonth; + memset(currentWeekDaysCount, 0, 7 * sizeof(unsigned int)); + for (i = 1; i <= numDaysInMonth; i++) + { + if (!didByFill || monthDays[i]) + { + positiveOrder = currentWeekDaysCount[currentWeekDay] + 1; + negativeOrder = currentWeekDaysCount[currentWeekDay] - weekDaysCount[currentWeekDay]; + monthDays[i] = (([byDayMask occursOnDay: (iCalWeekDay)currentWeekDay + withWeekNumber: positiveOrder]) || + ([byDayMask occursOnDay: (iCalWeekDay)currentWeekDay + withWeekNumber: negativeOrder])); + } + currentWeekDaysCount[currentWeekDay]++; + currentWeekDay = (currentWeekDay + 1) % 7; + } + } + didByFill = YES; } if (didByFill) diff --git a/SOPE/NGCards/iCalRecurrenceRule.h b/SOPE/NGCards/iCalRecurrenceRule.h index 0b70506aa..dc5088930 100644 --- a/SOPE/NGCards/iCalRecurrenceRule.h +++ b/SOPE/NGCards/iCalRecurrenceRule.h @@ -92,6 +92,8 @@ extern NSString *iCalWeekDayString[]; - (NSArray *) byMonth; - (BOOL) hasByMask; +- (NSArray *) bySetPos; + /* count and untilDate are mutually exclusive */ - (void) setRepeatCount: (int) _repeatCount; diff --git a/SOPE/NGCards/iCalRecurrenceRule.m b/SOPE/NGCards/iCalRecurrenceRule.m index fdc3a54bd..5e99429e7 100644 --- a/SOPE/NGCards/iCalRecurrenceRule.m +++ b/SOPE/NGCards/iCalRecurrenceRule.m @@ -183,6 +183,7 @@ */ #import +#import #import #import #import @@ -583,6 +584,19 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", } } +- (NSArray *) bySetPos +{ + NSArray *lists, *bySetPos; + + lists = [self valuesForKey: @"bysetpos"]; + if ([lists count] > 0) + bySetPos = [lists objectAtIndex: 0]; + else + bySetPos = nil; + + return bySetPos; +} + // - (iCalWeekDay) weekDayForiCalRepre: (NSString *) _weekDay // { // iCalWeekDay day; diff --git a/Tests/Unit/TestiCalRecurrenceCalculator.m b/Tests/Unit/TestiCalRecurrenceCalculator.m index bca529ba5..f676d05e0 100644 --- a/Tests/Unit/TestiCalRecurrenceCalculator.m +++ b/Tests/Unit/TestiCalRecurrenceCalculator.m @@ -169,6 +169,23 @@ @"19980129T090000Z", @"19980226T090000Z", nil], + // Second friday of the month, until Feb 26 1998 + [NSArray arrayWithObjects: @"19980101T090000Z", + @"FREQ=MONTHLY;BYDAY=FR;BYSETPOS=2;UNTIL=19980428T090000Z", + @"19980101T090000Z", + @"19980109T090000Z", + @"19980213T090000Z", + @"19980313T090000Z", + @"19980410fT090000Z", + nil], + // Last friday of the month, until Feb 26 1998 + [NSArray arrayWithObjects: @"19980101T090000Z", + @"FREQ=MONTHLY;BYDAY=MO,WE;BYSETPOS=-2;UNTIL=19980331T090000Z", + @"19980101T090000Z", + @"19980126T090000Z", + @"19980223T090000Z", + @"19980325T090000Z", + nil], nil]; NSString *dateFormat = @"%a %Y-%m-%d %H:%M";