]> err.no Git - sope/blobdiff - sope-ical/NGiCal/iCalRecurrenceCalculator.m
improved API, bugfix for recurrent dates
[sope] / sope-ical / NGiCal / iCalRecurrenceCalculator.m
index 2931f5a67c8a944709b12b9f359069158c51c52b..d51e845a8b24d0056f46b3866a73ef77c1bc75e9 100644 (file)
@@ -61,6 +61,8 @@
 
 @implementation iCalRecurrenceCalculator
 
+static Class NSCalendarDateClass     = Nil;
+static Class iCalRecurrenceRuleClass = Nil;
 static Class dailyCalcClass   = Nil;
 static Class weeklyCalcClass  = Nil;
 static Class monthlyCalcClass = Nil;
@@ -72,12 +74,17 @@ static Class yearlyCalcClass  = Nil;
   if (didInit) return;
   didInit = YES;
 
+  NSCalendarDateClass     = [NSCalendarDate class];
+  iCalRecurrenceRuleClass = [iCalRecurrenceRule class];
+
   dailyCalcClass   = [iCalDailyRecurrenceCalculator   class];
   weeklyCalcClass  = [iCalWeeklyRecurrenceCalculator  class];
   monthlyCalcClass = [iCalMonthlyRecurrenceCalculator class];
   yearlyCalcClass  = [iCalYearlyRecurrenceCalculator  class];
 }
 
+/* factory */
+
 + (id)recurrenceCalculatorForRecurrenceRule:(iCalRecurrenceRule *)_rrule
          withFirstInstanceCalendarDateRange:(NGCalendarDateRange *)_range
 {
@@ -85,6 +92,80 @@ static Class yearlyCalcClass  = Nil;
                         firstInstanceCalendarDateRange:_range] autorelease];
 }
 
+/* complex calculation convenience */
+
++ (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r
+  firstInstanceCalendarDateRange:(NGCalendarDateRange *)_fir
+  recurrenceRules:(NSArray *)_rRules
+  exceptionRules:(NSArray *)_exRules
+  exceptionDates:(NSArray *)_exDates
+{
+  id                       rule;
+  iCalRecurrenceCalculator *calc;
+  NSMutableArray           *ranges;
+  unsigned                 i, count, rCount;
+  
+  ranges = [NSMutableArray array];
+  count  = [_rRules count];
+  for (i = 0; i < count; i++) {
+    NSArray *rs;
+
+    rule = [_rRules objectAtIndex:i];
+    if (![rule isKindOfClass:iCalRecurrenceRuleClass])
+      rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule];
+  
+    calc = [self recurrenceCalculatorForRecurrenceRule:rule
+                 withFirstInstanceCalendarDateRange:_fir];
+    rs   = [calc recurrenceRangesWithinCalendarDateRange:_r];
+    [ranges addObjectsFromArray:rs];
+  }
+  
+  if (![ranges count])
+    return nil;
+  
+  /* test if any exceptions do match */
+  count = [_exRules count];
+  for (i = 0; i < count; i++) {
+    NSArray *rs;
+
+    rule = [_exRules objectAtIndex:i];
+    if (![rule isKindOfClass:iCalRecurrenceRuleClass])
+      rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule];
+
+    calc = [self recurrenceCalculatorForRecurrenceRule:rule
+                 withFirstInstanceCalendarDateRange:_fir];
+    rs   = [calc recurrenceRangesWithinCalendarDateRange:_r];
+    [ranges removeObjectsInArray:rs];
+  }
+  
+  if (![ranges count])
+    return nil;
+  
+  /* exception dates are also possible */
+  rCount = [ranges count];
+  count  = [_exDates count];
+  for (i = 0; i < count; i++) {
+    id                  exDate;
+    NGCalendarDateRange *r;
+    unsigned            k;
+
+    exDate = [_exDates objectAtIndex:i];
+    if (![exDate isKindOfClass:NSCalendarDateClass]) {
+      exDate = [NSCalendarDate calendarDateWithICalRepresentation:exDate];
+    }
+    for (k = 0; k < rCount; k++) {
+      r = [ranges objectAtIndex:(rCount - k) - 1];
+      if ([r containsDate:exDate]) {
+        [ranges removeObjectAtIndex:k];
+      }
+    }
+  }
+  return ranges;
+}
+
+
+/* init */
+
 - (id)initWithRecurrenceRule:(iCalRecurrenceRule *)_rrule
   firstInstanceCalendarDateRange:(NGCalendarDateRange *)_range
 {
@@ -252,6 +333,7 @@ static Class yearlyCalcClass  = Nil;
         NGCalendarDateRange *r;
       
         start = [NSCalendarDate dateForJulianNumber:jnCurrent];
+        [start setTimeZone:[firStart timeZone]];
         start = [start hour:  [firStart hourOfDay]
                        minute:[firStart minuteOfHour]
                        second:[firStart secondOfMinute]];
@@ -348,6 +430,7 @@ static Class yearlyCalcClass  = Nil;
           NGCalendarDateRange *r;
           
           start = [NSCalendarDate dateForJulianNumber:jnCurrent];
+          [start setTimeZone:[firStart timeZone]];
           start = [start hour:  [firStart hourOfDay]
                          minute:[firStart minuteOfHour]
                          second:[firStart secondOfMinute]];
@@ -395,6 +478,7 @@ static Class yearlyCalcClass  = Nil;
             NGCalendarDateRange *r;
                 
             start = [NSCalendarDate dateForJulianNumber:jnCurrent];
+            [start setTimeZone:[firStart timeZone]];
             start = [start hour:  [firStart hourOfDay]
                            minute:[firStart minuteOfHour]
                            second:[firStart secondOfMinute]];
@@ -465,6 +549,7 @@ static Class yearlyCalcClass  = Nil;
       start = [firStart dateByAddingYears:0
                         months:diff + i
                         days:0];
+      [start setTimeZone:[firStart timeZone]];
       end   = [start addTimeInterval:[self->firstRange duration]];
       r     = [NGCalendarDateRange calendarDateRangeWithStartDate:start
                                    endDate:end];
@@ -525,6 +610,7 @@ static Class yearlyCalcClass  = Nil;
       start = [firStart dateByAddingYears:diff + i
                         months:0
                         days:0];
+      [start setTimeZone:[firStart timeZone]];
       end   = [start addTimeInterval:[self->firstRange duration]];
       r     = [NGCalendarDateRange calendarDateRangeWithStartDate:start
                                    endDate:end];