]> err.no Git - sope/blob - sope-ical/NGiCal/iCalWeeklyRecurrenceCalculator.m
added synthesis XML sample
[sope] / sope-ical / NGiCal / iCalWeeklyRecurrenceCalculator.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 iCalWeeklyRecurrenceCalculator : 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
34 - (NSCalendarDate *)lastInstanceStartDate;
35
36 - (unsigned)offsetFromSundayForJulianNumber:(long)_jn;
37 - (unsigned)offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay;
38 - (unsigned)offsetFromSundayForCurrentWeekStart;
39   
40 - (iCalWeekDay)weekDayForJulianNumber:(long)_jn;
41
42 @end
43
44 /*
45    TODO: If BYDAY is specified, lastInstanceStartDate and recurrences will
46          differ significantly!
47 */
48 @implementation iCalWeeklyRecurrenceCalculator
49
50 - (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r {
51   NSMutableArray *ranges;
52   NSCalendarDate *firStart;
53   long           i, jnFirst, jnStart, jnEnd, startEndCount;
54   unsigned       interval, byDayMask;
55
56   firStart = [self->firstRange startDate];
57   jnFirst  = [firStart julianNumber];
58   jnEnd    = [[_r endDate] julianNumber];
59   
60   if (jnFirst > jnEnd)
61     return nil;
62   
63   jnStart  = [[_r startDate] julianNumber];
64   interval = [self->rrule repeatInterval];
65   
66   /* if rule is bound, check the bounds */
67   if (![self->rrule isInfinite]) {
68     NSCalendarDate *until;
69     long           jnRuleLast;
70     
71     until = [self->rrule untilDate];
72     if (until) {
73       if ([until compare:[_r startDate]] == NSOrderedAscending)
74         return nil;
75       jnRuleLast = [until julianNumber];
76     }
77     else {
78       jnRuleLast = (interval * [self->rrule repeatCount] * 7)
79       + jnFirst;
80       if (jnRuleLast < jnStart)
81         return nil;
82     }
83     /* jnStart < jnRuleLast < jnEnd ? */
84     if (jnEnd > jnRuleLast)
85       jnEnd = jnRuleLast;
86   }
87   
88   startEndCount = (jnEnd - jnStart) + 1;
89   ranges        = [NSMutableArray arrayWithCapacity:startEndCount];
90   byDayMask     = [self->rrule byDayMask];
91   if (!byDayMask) {
92     for (i = 0 ; i < startEndCount; i++) {
93       long jnCurrent;
94       
95       jnCurrent = jnStart + i;
96       if (jnCurrent >= jnFirst) {
97         long jnDiff;
98         
99         jnDiff = jnCurrent - jnFirst; /* difference in days */
100         if ((jnDiff % (interval * 7)) == 0) {
101           NSCalendarDate      *start, *end;
102           NGCalendarDateRange *r;
103           
104           start = [NSCalendarDate dateForJulianNumber:jnCurrent];
105           [start setTimeZone:[firStart timeZone]];
106           start = [start hour:  [firStart hourOfDay]
107                          minute:[firStart minuteOfHour]
108                          second:[firStart secondOfMinute]];
109           end   = [start addTimeInterval:[self->firstRange duration]];
110           r     = [NGCalendarDateRange calendarDateRangeWithStartDate:start
111                                        endDate:end];
112           if ([_r containsDateRange:r])
113             [ranges addObject:r];
114         }
115       }
116     }
117   }
118   else {
119     long jnFirstWeekStart, weekStartOffset;
120
121     /* calculate jnFirst's week start - this depends on our setting of week
122        start */
123     weekStartOffset = [self offsetFromSundayForJulianNumber:jnFirst] -
124                       [self offsetFromSundayForCurrentWeekStart];
125
126     jnFirstWeekStart = jnFirst - weekStartOffset;
127
128     for (i = 0 ; i < startEndCount; i++) {
129       long jnCurrent;
130
131       jnCurrent = jnStart + i;
132       if (jnCurrent >= jnFirst) {
133         long jnDiff;
134         
135         /* we need to calculate a difference in weeks */
136         jnDiff = (jnCurrent - jnFirstWeekStart) % 7;
137         if ((jnDiff % interval) == 0) {
138           BOOL isRecurrence = NO;
139             
140           if (jnCurrent == jnFirst) {
141             isRecurrence = YES;
142           }
143           else {
144             iCalWeekDay weekDay;
145
146             weekDay = [self weekDayForJulianNumber:jnCurrent];
147             isRecurrence = (weekDay & [self->rrule byDayMask]) ? YES : NO;
148           }
149           if (isRecurrence) {
150             NSCalendarDate      *start, *end;
151             NGCalendarDateRange *r;
152                 
153             start = [NSCalendarDate dateForJulianNumber:jnCurrent];
154             [start setTimeZone:[firStart timeZone]];
155             start = [start hour:  [firStart hourOfDay]
156                            minute:[firStart minuteOfHour]
157                            second:[firStart secondOfMinute]];
158             end   = [start addTimeInterval:[self->firstRange duration]];
159             r     = [NGCalendarDateRange calendarDateRangeWithStartDate:start
160                                          endDate:end];
161             if ([_r containsDateRange:r])
162               [ranges addObject:r];
163           }
164         }
165       }
166     }
167   }
168   return ranges;
169 }
170
171 - (NSCalendarDate *)lastInstanceStartDate {
172   if ([self->rrule repeatCount] > 0) {
173     long           jnFirst, jnRuleLast;
174     NSCalendarDate *firStart, *until;
175     
176     firStart   = [self->firstRange startDate];
177     jnFirst    = [firStart julianNumber];
178     jnRuleLast = ([self->rrule repeatInterval] *
179                   [self->rrule repeatCount] * 7) +
180                   jnFirst;
181     until      = [NSCalendarDate dateForJulianNumber:jnRuleLast];
182     until      = [until hour:  [firStart hourOfDay]
183                         minute:[firStart minuteOfHour]
184                         second:[firStart secondOfMinute]];
185     return until;
186   }
187   return [super lastInstanceStartDate];
188 }
189
190 @end /* iCalWeeklyRecurrenceCalculator */