From b8930b7bd077a55df029b6758fa3dfef963f1de1 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 23 Apr 2012 21:17:54 +0000 Subject: [PATCH 1/2] 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"; From f9d34ea6ec79ec208c88c2cbf6a3ce81b15e0195 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 23 Apr 2012 21:19:08 +0000 Subject: [PATCH 2/2] Monotone-Parent: 5626a85621c5f1c132d788ea29e164c8fc44cfdf Monotone-Revision: cd39fe0df424d1e5afd5dcfa4b1c94ad707fed24 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-04-23T21:19:08 --- Tests/Unit/TestiCalRecurrenceCalculator.m | 32 ----------------------- 1 file changed, 32 deletions(-) diff --git a/Tests/Unit/TestiCalRecurrenceCalculator.m b/Tests/Unit/TestiCalRecurrenceCalculator.m index f676d05e0..5d176bf74 100644 --- a/Tests/Unit/TestiCalRecurrenceCalculator.m +++ b/Tests/Unit/TestiCalRecurrenceCalculator.m @@ -90,7 +90,6 @@ startDate = [[currentRule objectAtIndex: 0] asCalendarDate]; endDate = [startDate dateByAddingYears: 0 months: 0 days: 0 hours: 1 minutes: 0 seconds: 0]; recurrenceRule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation: [currentRule objectAtIndex: 1]]; -// NSLog(@"%@: %@", startDate, recurrenceRule); firRange = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate endDate: endDate]; @@ -107,22 +106,12 @@ [[[occurrences objectAtIndex: j] startDate] descriptionWithCalendarFormat: dateFormat], [currentOccurrence descriptionWithCalendarFormat: dateFormat]]; testWithMessage([currentOccurrence isDateOnSameDay: [[occurrences objectAtIndex: j] startDate]], error); -// if ([currentOccurrence compare: [[occurrences objectAtIndex: j] startDate]] != NSOrderedSame) -// NSLog(@"Expected: %@ Obtained: %@", [currentOccurrence descriptionWithCalendarFormat: dateFormat], -// [[[occurrences objectAtIndex: j] startDate] descriptionWithCalendarFormat: dateFormat]); -// else -// NSLog(@" %@ Matched: %@", [currentOccurrence descriptionWithCalendarFormat: dateFormat], -// [[[occurrences objectAtIndex: j] startDate] descriptionWithCalendarFormat: dateFormat]); } error = [NSString stringWithFormat: @"Unexpected number of occurrences for recurrence rule %@ (found %i, expected %i)", [currentRule objectAtIndex: 1], [occurrences count], [currentRule count] - 2]; testWithMessage([currentRule count] - [occurrences count] == 2, error); -// for (; i < [currentRule count]; i++) -// NSLog(@"Expected additional date : %@", [[currentRule objectAtIndex: i] asCalendarDate]); -// for (; j < [occurrences count]; j++) -// NSLog(@"Found additional date : %@", [[occurrences objectAtIndex: j] startDate]); } } @@ -221,22 +210,12 @@ [[[occurrences objectAtIndex: j] startDate] descriptionWithCalendarFormat: dateFormat], [currentOccurrence descriptionWithCalendarFormat: dateFormat]]; testWithMessage([currentOccurrence isDateOnSameDay: [[occurrences objectAtIndex: j] startDate]], error); -// if ([currentOccurrence compare: [[occurrences objectAtIndex: j] startDate]] != NSOrderedSame) -// NSLog(@"Expected: %@ Obtained: %@", [currentOccurrence descriptionWithCalendarFormat: dateFormat], -// [[[occurrences objectAtIndex: j] startDate] descriptionWithCalendarFormat: dateFormat]); -// else -// NSLog(@" %@ Matched: %@", [currentOccurrence descriptionWithCalendarFormat: dateFormat], -// [[[occurrences objectAtIndex: j] startDate] descriptionWithCalendarFormat: dateFormat]); } error = [NSString stringWithFormat: @"Unexpected number of occurrences for recurrence rule %@ (found %i, expected %i)", [currentRule objectAtIndex: 1], [occurrences count], [currentRule count] - 2]; testWithMessage([currentRule count] - [occurrences count] == 2, error); -// for (; i < [currentRule count]; i++) -// NSLog(@"Expected additional date : %@", [[currentRule objectAtIndex: i] asCalendarDate]); -// for (; j < [occurrences count]; j++) -// NSLog(@"Found additional date : %@", [[occurrences objectAtIndex: j] startDate]); } } @@ -380,7 +359,6 @@ startDate = [[currentRule objectAtIndex: 0] asCalendarDate]; endDate = [startDate dateByAddingYears: 0 months: 0 days: 0 hours: 1 minutes: 0 seconds: 0]; recurrenceRule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation: [currentRule objectAtIndex: 1]]; -// NSLog(@"%@: %@", startDate, recurrenceRule); firRange = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate endDate: endDate]; @@ -397,22 +375,12 @@ [[[occurrences objectAtIndex: j] startDate] descriptionWithCalendarFormat: dateFormat], [currentOccurrence descriptionWithCalendarFormat: dateFormat]]; testWithMessage([currentOccurrence isDateOnSameDay: [[occurrences objectAtIndex: j] startDate]], error); -// if ([currentOccurrence compare: [[occurrences objectAtIndex: j] startDate]] != NSOrderedSame) -// NSLog(@"Expected: %@ Obtained: %@", [currentOccurrence descriptionWithCalendarFormat: dateFormat], -// [[[occurrences objectAtIndex: j] startDate] descriptionWithCalendarFormat: dateFormat]); -// else -// NSLog(@" %@ Matched: %@", [currentOccurrence descriptionWithCalendarFormat: dateFormat], -// [[[occurrences objectAtIndex: j] startDate] descriptionWithCalendarFormat: dateFormat]); } error = [NSString stringWithFormat: @"Unexpected number of occurrences for recurrence rule %@ (found %i, expected %i)", [currentRule objectAtIndex: 1], [occurrences count], [currentRule count] - 2]; testWithMessage([currentRule count] - [occurrences count] == 2, error); -// for (; i < [currentRule count]; i++) -// NSLog(@"Expected additional date : %@", [[currentRule objectAtIndex: i] asCalendarDate]); -// for (; j < [occurrences count]; j++) -// NSLog(@"Found additional date : %@", [[occurrences objectAtIndex: j] startDate]); } }