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