]> err.no Git - sope/blob - sope-ical/NGiCal/iCalMonthlyRecurrenceCalculator.m
7f04abb0ea233cb3d5601d021fd4fa181da66906
[sope] / sope-ical / NGiCal / iCalMonthlyRecurrenceCalculator.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3   
4   This file is part of SOPE.
5   
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10   
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15   
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "iCalRecurrenceCalculator.h"
23
24 @interface iCalMonthlyRecurrenceCalculator : iCalRecurrenceCalculator
25 @end
26
27 #include <NGExtensions/NGCalendarDateRange.h>
28 #include "iCalRecurrenceRule.h"
29 #include "NSCalendarDate+ICal.h"
30 #include "common.h"
31
32 @interface iCalRecurrenceCalculator(PrivateAPI)
33 - (NSCalendarDate *)lastInstanceStartDate;
34 @end
35
36 @implementation iCalMonthlyRecurrenceCalculator
37
38 - (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r{
39   /* main entry */
40   NSMutableArray *ranges;
41   NSTimeZone     *timeZone;
42   NSCalendarDate *firStart, *rStart, *rEnd, *until;
43   unsigned       i, count, interval;
44   int            diff;
45
46   firStart = [self->firstRange startDate];
47   timeZone = [firStart timeZone];
48   rStart   = [_r startDate];
49   rEnd     = [_r endDate];
50   interval = [self->rrule repeatInterval];
51   until    = [self lastInstanceStartDate]; // TODO: maybe replace
52   
53   if ([self->rrule byDayMask] != 0) {
54     [self errorWithFormat:@"cannot process byday part of rrule: %@", 
55             self->rrule];
56     return nil;
57   }
58   
59   /* check whether the range to be processed is beyond the 'until' date */
60   
61   if (until != nil) {
62     if ([until compare:rStart] == NSOrderedAscending) /* until before start */
63       return nil;
64     if ([until compare:rEnd] == NSOrderedDescending) /* end before until */
65       rEnd = until; // TODO: why is that? end is _before_ until?
66   }
67   
68   // TODO: I think the diff is to skip recurrence which are before the
69   //       requested range. Not sure whether this is actually possible, eg
70   //       the repeatCount must be processed from the start.
71   diff = [firStart monthsBetweenDate:rStart];
72   if ((diff != 0) && [rStart compare:firStart] == NSOrderedAscending)
73     diff = -diff;
74   
75   count  = [rStart monthsBetweenDate:rEnd] + 1;
76   ranges = [NSMutableArray arrayWithCapacity:count];
77   for (i = 0 ; i < count; i++) {
78     NSCalendarDate      *start, *end;
79     NGCalendarDateRange *r;
80     int test;
81     
82     test = diff + i;
83     
84     if (test < 0)
85       continue;
86     
87     if ((test % interval) != 0)
88       continue;
89     
90     start = [firStart dateByAddingYears:0 months:(diff + i) days:0];
91     [start setTimeZone:timeZone];
92     
93     /* check whether we are still in the limits */
94     
95     // TODO: I think we should check in here whether we succeeded the
96     //       repeatCount. Currently we precalculate that info in the
97     //       -lastInstanceStartDate method.
98     if (until != nil) {
99       /* Note: the 'until' in the rrule is inclusive as per spec */
100       if ([until compare:start] == NSOrderedAscending) /* start after until */
101         break; /* Note: we assume that the algorithm is sequential */
102     }
103     
104     /* create end date */
105
106     end   = [start addTimeInterval:[self->firstRange duration]];
107     [end setTimeZone:timeZone];
108     
109     /* create range and check whether its in the requested range */
110     
111     r = [[NGCalendarDateRange alloc] initWithStartDate:start endDate:end];
112     if ([_r containsDateRange:r])
113       [ranges addObject:r];
114     [r release]; r = nil;
115   }
116   return ranges;
117 }
118
119 - (NSCalendarDate *)lastInstanceStartDate {
120   if ([self->rrule repeatCount] > 0) {
121     NSCalendarDate *until;
122     unsigned       months, interval;
123     
124     interval = [self->rrule repeatInterval];
125     months   = ([self->rrule repeatCount] - 1) * interval;
126     until    = [[self->firstRange startDate] dateByAddingYears:0
127                                              months:months
128                                              days:0];
129     return until;
130   }
131   return [super lastInstanceStartDate];
132 }
133
134 @end /* iCalMonthlyRecurrenceCalculator */