summaryrefslogtreecommitdiffabout
path: root/libkcal/recurrence.cpp
Unidiff
Diffstat (limited to 'libkcal/recurrence.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--libkcal/recurrence.cpp17
1 files changed, 15 insertions, 2 deletions
diff --git a/libkcal/recurrence.cpp b/libkcal/recurrence.cpp
index 5fc5d1f..dd74e10 100644
--- a/libkcal/recurrence.cpp
+++ b/libkcal/recurrence.cpp
@@ -1,3360 +1,3373 @@
1/* 1/*
2 This file is part of libkcal. 2 This file is part of libkcal.
3 Copyright (c) 1998 Preston Brown 3 Copyright (c) 1998 Preston Brown
4 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> 4 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
5 Copyright (c) 2002 David Jarvie <software@astrojar.org.uk> 5 Copyright (c) 2002 David Jarvie <software@astrojar.org.uk>
6 6
7 This library is free software; you can redistribute it and/or 7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public 8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either 9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version. 10 version 2 of the License, or (at your option) any later version.
11 11
12 This library is distributed in the hope that it will be useful, 12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details. 15 Library General Public License for more details.
16 16
17 You should have received a copy of the GNU Library General Public License 17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to 18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. 20 Boston, MA 02111-1307, USA.
21*/ 21*/
22 22
23#include <limits.h> 23#include <limits.h>
24 24
25#include <kdebug.h> 25#include <kdebug.h>
26#include <kglobal.h> 26#include <kglobal.h>
27#include <klocale.h> 27#include <klocale.h>
28 28
29#include "incidence.h" 29#include "incidence.h"
30 30
31#include "recurrence.h" 31#include "recurrence.h"
32 32
33using namespace KCal; 33using namespace KCal;
34 34
35Recurrence::Feb29Type Recurrence::mFeb29YearlyDefaultType = Recurrence::rMar1; 35Recurrence::Feb29Type Recurrence::mFeb29YearlyDefaultType = Recurrence::rMar1;
36 36
37 37
38Recurrence::Recurrence(Incidence *parent, int compatVersion) 38Recurrence::Recurrence(Incidence *parent, int compatVersion)
39: recurs(rNone), // by default, it's not a recurring event 39: recurs(rNone), // by default, it's not a recurring event
40 rWeekStart(1), // default is Monday 40 rWeekStart(1), // default is Monday
41 rDays(7), 41 rDays(7),
42 mFloats(parent ? parent->doesFloat() : false), 42 mFloats(parent ? parent->doesFloat() : false),
43 mRecurReadOnly(false), 43 mRecurReadOnly(false),
44 mRecurExDatesCount(0), 44 mRecurExDatesCount(0),
45 mFeb29YearlyType(mFeb29YearlyDefaultType), 45 mFeb29YearlyType(mFeb29YearlyDefaultType),
46 mCompatVersion(compatVersion ? compatVersion : INT_MAX), 46 mCompatVersion(compatVersion ? compatVersion : INT_MAX),
47 mCompatRecurs(rNone), 47 mCompatRecurs(rNone),
48 mCompatDuration(0), 48 mCompatDuration(0),
49 mParent(parent) 49 mParent(parent)
50{ 50{
51 rMonthDays.setAutoDelete( true ); 51 rMonthDays.setAutoDelete( true );
52 rMonthPositions.setAutoDelete( true ); 52 rMonthPositions.setAutoDelete( true );
53 rYearNums.setAutoDelete( true ); 53 rYearNums.setAutoDelete( true );
54} 54}
55 55
56Recurrence::Recurrence(const Recurrence &r, Incidence *parent) 56Recurrence::Recurrence(const Recurrence &r, Incidence *parent)
57: recurs(r.recurs), 57: recurs(r.recurs),
58 rWeekStart(r.rWeekStart), 58 rWeekStart(r.rWeekStart),
59 rDays(r.rDays.copy()), 59 rDays(r.rDays.copy()),
60 rFreq(r.rFreq), 60 rFreq(r.rFreq),
61 rDuration(r.rDuration), 61 rDuration(r.rDuration),
62 rEndDateTime(r.rEndDateTime), 62 rEndDateTime(r.rEndDateTime),
63 mRecurStart(r.mRecurStart), 63 mRecurStart(r.mRecurStart),
64 mFloats(r.mFloats), 64 mFloats(r.mFloats),
65 mRecurReadOnly(r.mRecurReadOnly), 65 mRecurReadOnly(r.mRecurReadOnly),
66 mRecurExDatesCount(r.mRecurExDatesCount), 66 mRecurExDatesCount(r.mRecurExDatesCount),
67 mFeb29YearlyType(r.mFeb29YearlyType), 67 mFeb29YearlyType(r.mFeb29YearlyType),
68 mCompatVersion(r.mCompatVersion), 68 mCompatVersion(r.mCompatVersion),
69 mCompatRecurs(r.mCompatRecurs), 69 mCompatRecurs(r.mCompatRecurs),
70 mCompatDuration(r.mCompatDuration), 70 mCompatDuration(r.mCompatDuration),
71 mParent(parent) 71 mParent(parent)
72{ 72{
73 for (QPtrListIterator<rMonthPos> mp(r.rMonthPositions); mp.current(); ++mp) { 73 for (QPtrListIterator<rMonthPos> mp(r.rMonthPositions); mp.current(); ++mp) {
74 rMonthPos *tmp = new rMonthPos; 74 rMonthPos *tmp = new rMonthPos;
75 tmp->rPos = mp.current()->rPos; 75 tmp->rPos = mp.current()->rPos;
76 tmp->negative = mp.current()->negative; 76 tmp->negative = mp.current()->negative;
77 tmp->rDays = mp.current()->rDays.copy(); 77 tmp->rDays = mp.current()->rDays.copy();
78 rMonthPositions.append(tmp); 78 rMonthPositions.append(tmp);
79 } 79 }
80 for (QPtrListIterator<int> md(r.rMonthDays); md.current(); ++md) { 80 for (QPtrListIterator<int> md(r.rMonthDays); md.current(); ++md) {
81 int *tmp = new int; 81 int *tmp = new int;
82 *tmp = *md.current(); 82 *tmp = *md.current();
83 rMonthDays.append(tmp); 83 rMonthDays.append(tmp);
84 } 84 }
85 for (QPtrListIterator<int> yn(r.rYearNums); yn.current(); ++yn) { 85 for (QPtrListIterator<int> yn(r.rYearNums); yn.current(); ++yn) {
86 int *tmp = new int; 86 int *tmp = new int;
87 *tmp = *yn.current(); 87 *tmp = *yn.current();
88 rYearNums.append(tmp); 88 rYearNums.append(tmp);
89 } 89 }
90 rMonthDays.setAutoDelete( true ); 90 rMonthDays.setAutoDelete( true );
91 rMonthPositions.setAutoDelete( true ); 91 rMonthPositions.setAutoDelete( true );
92 rYearNums.setAutoDelete( true ); 92 rYearNums.setAutoDelete( true );
93} 93}
94 94
95Recurrence::~Recurrence() 95Recurrence::~Recurrence()
96{ 96{
97} 97}
98 98
99 99
100bool Recurrence::operator==( const Recurrence& r2 ) const 100bool Recurrence::operator==( const Recurrence& r2 ) const
101{ 101{
102 102
103 // the following line is obvious 103 // the following line is obvious
104 if ( recurs == rNone && r2.recurs == rNone ) 104 if ( recurs == rNone && r2.recurs == rNone )
105 return true; 105 return true;
106 // we need the above line, because two non recurring events may 106 // we need the above line, because two non recurring events may
107 // differ in the other settings, because one (or both) 107 // differ in the other settings, because one (or both)
108 // may be not initialized properly 108 // may be not initialized properly
109 if ( recurs != r2.recurs 109 if ( recurs != r2.recurs
110 || rFreq != r2.rFreq 110 || rFreq != r2.rFreq
111 || rDuration != r2.rDuration 111 || rDuration != r2.rDuration
112 || !rDuration && rEndDateTime != r2.rEndDateTime 112 || !rDuration && rEndDateTime != r2.rEndDateTime
113 || mRecurStart != r2.mRecurStart 113 || mRecurStart != r2.mRecurStart
114 || mFloats != r2.mFloats 114 || mFloats != r2.mFloats
115 || mRecurReadOnly != r2.mRecurReadOnly 115 || mRecurReadOnly != r2.mRecurReadOnly
116 || mRecurExDatesCount != r2.mRecurExDatesCount ) 116 || mRecurExDatesCount != r2.mRecurExDatesCount )
117 return false; 117 return false;
118 // no need to compare mCompat* and mParent 118 // no need to compare mCompat* and mParent
119 // OK to compare the pointers 119 // OK to compare the pointers
120 switch ( recurs ) 120 switch ( recurs )
121 { 121 {
122 case rWeekly: 122 case rWeekly:
123 return rDays == r2.rDays 123 return rDays == r2.rDays
124 && rWeekStart == r2.rWeekStart; 124 && rWeekStart == r2.rWeekStart;
125 case rMonthlyPos: 125 case rMonthlyPos:
126 return rMonthPositions.count() == r2.rMonthPositions.count(); 126 return rMonthPositions.count() == r2.rMonthPositions.count();
127 case rMonthlyDay: 127 case rMonthlyDay:
128 return rMonthDays.count() == r2.rMonthDays.count(); 128 return rMonthDays.count() == r2.rMonthDays.count();
129 case rYearlyPos: 129 case rYearlyPos:
130 return rYearNums.count() == r2.rYearNums.count() 130 return rYearNums.count() == r2.rYearNums.count()
131 && rMonthPositions.count() == r2.rMonthPositions.count(); 131 && rMonthPositions.count() == r2.rMonthPositions.count();
132 case rYearlyMonth: 132 case rYearlyMonth:
133 return rYearNums.count() == r2.rYearNums.count() 133 return rYearNums.count() == r2.rYearNums.count()
134 && mFeb29YearlyType == r2.mFeb29YearlyType; 134 && mFeb29YearlyType == r2.mFeb29YearlyType;
135 case rYearlyDay: 135 case rYearlyDay:
136 return rYearNums == r2.rYearNums; 136 return rYearNums == r2.rYearNums;
137 case rNone: 137 case rNone:
138 case rMinutely: 138 case rMinutely:
139 case rHourly: 139 case rHourly:
140 case rDaily: 140 case rDaily:
141 default: 141 default:
142 return true; 142 return true;
143 } 143 }
144} 144}
145/* 145/*
146bool Recurrence::compareLists( const QPtrList<int> &l1 ,const QPtrList<int> &l2) 146bool Recurrence::compareLists( const QPtrList<int> &l1 ,const QPtrList<int> &l2)
147{ 147{
148 if ( l1.count() != l2.count() ) 148 if ( l1.count() != l2.count() )
149 return false; 149 return false;
150 int count = l1.count(); 150 int count = l1.count();
151 int i; 151 int i;
152 for ( i = 0; i < count ; ++i ) { 152 for ( i = 0; i < count ; ++i ) {
153 // if ( l1.at(i) != l2.at(i) ) 153 // if ( l1.at(i) != l2.at(i) )
154 return false; 154 return false;
155 qDebug("compüare "); 155 qDebug("compüare ");
156 } 156 }
157 return true; 157 return true;
158} 158}
159*/ 159*/
160QString Recurrence::recurrenceText() const 160QString Recurrence::recurrenceText() const
161{ 161{
162 QString recurText = i18n("No"); 162 QString recurText = i18n("No");
163 if ( recurs == Recurrence::rMinutely ) 163 if ( recurs == Recurrence::rMinutely )
164 recurText = i18n("minutely"); 164 recurText = i18n("minutely");
165 else if ( recurs == Recurrence::rHourly ) 165 else if ( recurs == Recurrence::rHourly )
166 recurText = i18n("hourly"); 166 recurText = i18n("hourly");
167 else if ( recurs == Recurrence::rDaily ) 167 else if ( recurs == Recurrence::rDaily )
168 recurText = i18n("daily"); 168 recurText = i18n("daily");
169 else if ( recurs == Recurrence::rWeekly ) 169 else if ( recurs == Recurrence::rWeekly )
170 recurText = i18n("weekly"); 170 recurText = i18n("weekly");
171 else if ( recurs == Recurrence::rMonthlyPos ) 171 else if ( recurs == Recurrence::rMonthlyPos )
172 recurText = i18n("monthly"); 172 recurText = i18n("monthly");
173 else if ( recurs == Recurrence::rMonthlyDay ) 173 else if ( recurs == Recurrence::rMonthlyDay )
174 recurText = i18n("day-monthly"); 174 recurText = i18n("day-monthly");
175 else if ( recurs == Recurrence::rYearlyMonth ) 175 else if ( recurs == Recurrence::rYearlyMonth )
176 recurText = i18n("month-yearly"); 176 recurText = i18n("month-yearly");
177 else if ( recurs == Recurrence::rYearlyDay ) 177 else if ( recurs == Recurrence::rYearlyDay )
178 recurText = i18n("day-yearly"); 178 recurText = i18n("day-yearly");
179 else if ( recurs == Recurrence::rYearlyPos ) 179 else if ( recurs == Recurrence::rYearlyPos )
180 recurText = i18n("position-yearly"); 180 recurText = i18n("position-yearly");
181 return recurText; 181 return recurText;
182} 182}
183 183
184void Recurrence::setCompatVersion(int version) 184void Recurrence::setCompatVersion(int version)
185{ 185{
186 mCompatVersion = version ? version : INT_MAX; 186 mCompatVersion = version ? version : INT_MAX;
187} 187}
188 188
189ushort Recurrence::doesRecur() const 189ushort Recurrence::doesRecur() const
190{ 190{
191 return recurs; 191 return recurs;
192} 192}
193 193
194bool Recurrence::recursOnPure(const QDate &qd) const 194bool Recurrence::recursOnPure(const QDate &qd) const
195{ 195{
196 switch(recurs) { 196 switch(recurs) {
197 case rMinutely: 197 case rMinutely:
198 return recursSecondly(qd, rFreq*60); 198 return recursSecondly(qd, rFreq*60);
199 case rHourly: 199 case rHourly:
200 return recursSecondly(qd, rFreq*3600); 200 return recursSecondly(qd, rFreq*3600);
201 case rDaily: 201 case rDaily:
202 return recursDaily(qd); 202 return recursDaily(qd);
203 case rWeekly: 203 case rWeekly:
204 return recursWeekly(qd); 204 return recursWeekly(qd);
205 case rMonthlyPos: 205 case rMonthlyPos:
206 case rMonthlyDay: 206 case rMonthlyDay:
207 return recursMonthly(qd); 207 return recursMonthly(qd);
208 case rYearlyMonth: 208 case rYearlyMonth:
209 return recursYearlyByMonth(qd); 209 return recursYearlyByMonth(qd);
210 case rYearlyDay: 210 case rYearlyDay:
211 return recursYearlyByDay(qd); 211 return recursYearlyByDay(qd);
212 case rYearlyPos: 212 case rYearlyPos:
213 return recursYearlyByPos(qd); 213 return recursYearlyByPos(qd);
214 default: 214 default:
215 return false; 215 return false;
216 case rNone: 216 case rNone:
217 return false; 217 return false;
218 } // case 218 } // case
219 return false; 219 return false;
220} 220}
221 221
222bool Recurrence::recursAtPure(const QDateTime &dt) const 222bool Recurrence::recursAtPure(const QDateTime &dt) const
223{ 223{
224 switch(recurs) { 224 switch(recurs) {
225 case rMinutely: 225 case rMinutely:
226 return recursMinutelyAt(dt, rFreq); 226 return recursMinutelyAt(dt, rFreq);
227 case rHourly: 227 case rHourly:
228 return recursMinutelyAt(dt, rFreq*60); 228 return recursMinutelyAt(dt, rFreq*60);
229 default: 229 default:
230 if (dt.time() != mRecurStart.time()) 230 if (dt.time() != mRecurStart.time())
231 return false; 231 return false;
232 switch(recurs) { 232 switch(recurs) {
233 case rDaily: 233 case rDaily:
234 return recursDaily(dt.date()); 234 return recursDaily(dt.date());
235 case rWeekly: 235 case rWeekly:
236 return recursWeekly(dt.date()); 236 return recursWeekly(dt.date());
237 case rMonthlyPos: 237 case rMonthlyPos:
238 case rMonthlyDay: 238 case rMonthlyDay:
239 return recursMonthly(dt.date()); 239 return recursMonthly(dt.date());
240 case rYearlyMonth: 240 case rYearlyMonth:
241 return recursYearlyByMonth(dt.date()); 241 return recursYearlyByMonth(dt.date());
242 case rYearlyDay: 242 case rYearlyDay:
243 return recursYearlyByDay(dt.date()); 243 return recursYearlyByDay(dt.date());
244 case rYearlyPos: 244 case rYearlyPos:
245 return recursYearlyByPos(dt.date()); 245 return recursYearlyByPos(dt.date());
246 default: 246 default:
247 return false; 247 return false;
248 case rNone: 248 case rNone:
249 return false; 249 return false;
250 } 250 }
251 } // case 251 } // case
252 return false; 252 return false;
253} 253}
254 254
255QDate Recurrence::endDate() const 255QDate Recurrence::endDate() const
256{ 256{
257 int count = 0; 257 int count = 0;
258 QDate end; 258 QDate end;
259 if (recurs != rNone) { 259 if (recurs != rNone) {
260 if (rDuration < 0) 260 if (rDuration < 0)
261 return QDate(); // infinite recurrence 261 return QDate(); // infinite recurrence
262 if (rDuration == 0) 262 if (rDuration == 0)
263 return rEndDateTime.date(); 263 return rEndDateTime.date();
264 264
265 // The end date is determined by the recurrence count 265 // The end date is determined by the recurrence count
266 QDate dStart = mRecurStart.date(); 266 QDate dStart = mRecurStart.date();
267 switch (recurs) 267 switch (recurs)
268 { 268 {
269 case rMinutely: 269 case rMinutely:
270 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60).date(); 270 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60).date();
271 case rHourly: 271 case rHourly:
272 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600).date(); 272 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600).date();
273 case rDaily: 273 case rDaily:
274 return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq); 274 return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq);
275 275
276 case rWeekly: 276 case rWeekly:
277 count = weeklyCalc(END_DATE_AND_COUNT, end); 277 count = weeklyCalc(END_DATE_AND_COUNT, end);
278 break; 278 break;
279 case rMonthlyPos: 279 case rMonthlyPos:
280 case rMonthlyDay: 280 case rMonthlyDay:
281 count = monthlyCalc(END_DATE_AND_COUNT, end); 281 count = monthlyCalc(END_DATE_AND_COUNT, end);
282 break; 282 break;
283 case rYearlyMonth: 283 case rYearlyMonth:
284 count = yearlyMonthCalc(END_DATE_AND_COUNT, end); 284 count = yearlyMonthCalc(END_DATE_AND_COUNT, end);
285 break; 285 break;
286 case rYearlyDay: 286 case rYearlyDay:
287 count = yearlyDayCalc(END_DATE_AND_COUNT, end); 287 count = yearlyDayCalc(END_DATE_AND_COUNT, end);
288 break; 288 break;
289 case rYearlyPos: 289 case rYearlyPos:
290 count = yearlyPosCalc(END_DATE_AND_COUNT, end); 290 count = yearlyPosCalc(END_DATE_AND_COUNT, end);
291 break; 291 break;
292 default: 292 default:
293 // catch-all. Should never get here. 293 // catch-all. Should never get here.
294 kdDebug(5800) << "Control should never reach here in endDate()!" << endl; 294 kdDebug(5800) << "Control should never reach here in endDate()!" << endl;
295 break; 295 break;
296 } 296 }
297 } 297 }
298 if (!count) 298 if (!count)
299 return QDate(); // error - there is no recurrence 299 return QDate(); // error - there is no recurrence
300 return end; 300 return end;
301} 301}
302 302
303QDateTime Recurrence::endDateTime() const 303QDateTime Recurrence::endDateTime() const
304{ 304{
305 int count = 0; 305 int count = 0;
306 QDate end; 306 QDate end;
307 if (recurs != rNone) { 307 if (recurs != rNone) {
308 if (rDuration < 0) 308 if (rDuration < 0)
309 return QDateTime(); // infinite recurrence 309 return QDateTime(); // infinite recurrence
310 if (rDuration == 0) 310 if (rDuration == 0)
311 return rEndDateTime; 311 return rEndDateTime;
312 312
313 // The end date is determined by the recurrence count 313 // The end date is determined by the recurrence count
314 QDate dStart = mRecurStart.date(); 314 QDate dStart = mRecurStart.date();
315 switch (recurs) 315 switch (recurs)
316 { 316 {
317 case rMinutely: 317 case rMinutely:
318 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60); 318 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60);
319 case rHourly: 319 case rHourly:
320 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600); 320 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600);
321 case rDaily: 321 case rDaily:
322 return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq); 322 return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq);
323 323
324 case rWeekly: 324 case rWeekly:
325 count = weeklyCalc(END_DATE_AND_COUNT, end); 325 count = weeklyCalc(END_DATE_AND_COUNT, end);
326 break; 326 break;
327 case rMonthlyPos: 327 case rMonthlyPos:
328 case rMonthlyDay: 328 case rMonthlyDay:
329 count = monthlyCalc(END_DATE_AND_COUNT, end); 329 count = monthlyCalc(END_DATE_AND_COUNT, end);
330 break; 330 break;
331 case rYearlyMonth: 331 case rYearlyMonth:
332 count = yearlyMonthCalc(END_DATE_AND_COUNT, end); 332 count = yearlyMonthCalc(END_DATE_AND_COUNT, end);
333 break; 333 break;
334 case rYearlyDay: 334 case rYearlyDay:
335 count = yearlyDayCalc(END_DATE_AND_COUNT, end); 335 count = yearlyDayCalc(END_DATE_AND_COUNT, end);
336 break; 336 break;
337 case rYearlyPos: 337 case rYearlyPos:
338 count = yearlyPosCalc(END_DATE_AND_COUNT, end); 338 count = yearlyPosCalc(END_DATE_AND_COUNT, end);
339 break; 339 break;
340 default: 340 default:
341 // catch-all. Should never get here. 341 // catch-all. Should never get here.
342 kdDebug(5800) << "Control should never reach here in endDate()!" << endl; 342 kdDebug(5800) << "Control should never reach here in endDate()!" << endl;
343 break; 343 break;
344 } 344 }
345 } 345 }
346 if (!count) 346 if (!count)
347 return QDateTime(); // error - there is no recurrence 347 return QDateTime(); // error - there is no recurrence
348 return QDateTime(end, mRecurStart.time()); 348 return QDateTime(end, mRecurStart.time());
349} 349}
350 350
351int Recurrence::durationTo(const QDate &date) const 351int Recurrence::durationTo(const QDate &date) const
352{ 352{
353 QDate d = date; 353 QDate d = date;
354 return recurCalc(COUNT_TO_DATE, d); 354 return recurCalc(COUNT_TO_DATE, d);
355} 355}
356 356
357int Recurrence::durationTo(const QDateTime &datetime) const 357int Recurrence::durationTo(const QDateTime &datetime) const
358{ 358{
359 QDateTime dt = datetime; 359 QDateTime dt = datetime;
360 return recurCalc(COUNT_TO_DATE, dt); 360 return recurCalc(COUNT_TO_DATE, dt);
361} 361}
362 362
363void Recurrence::unsetRecurs() 363void Recurrence::unsetRecurs()
364{ 364{
365 if (mRecurReadOnly) return; 365 if (mRecurReadOnly) return;
366 recurs = rNone; 366 recurs = rNone;
367 rMonthPositions.clear(); 367 rMonthPositions.clear();
368 rMonthDays.clear(); 368 rMonthDays.clear();
369 rYearNums.clear(); 369 rYearNums.clear();
370} 370}
371 371
372void Recurrence::setRecurStart(const QDateTime &start) 372void Recurrence::setRecurStart(const QDateTime &start)
373{ 373{
374 mRecurStart = start; 374 mRecurStart = start;
375 mFloats = false; 375 mFloats = false;
376 switch (recurs) 376 switch (recurs)
377 { 377 {
378 case rMinutely: 378 case rMinutely:
379 case rHourly: 379 case rHourly:
380 break; 380 break;
381 case rDaily: 381 case rDaily:
382 case rWeekly: 382 case rWeekly:
383 case rMonthlyPos: 383 case rMonthlyPos:
384 case rMonthlyDay: 384 case rMonthlyDay:
385 case rYearlyMonth: 385 case rYearlyMonth:
386 case rYearlyDay: 386 case rYearlyDay:
387 case rYearlyPos: 387 case rYearlyPos:
388 default: 388 default:
389 rEndDateTime.setTime(start.time()); 389 rEndDateTime.setTime(start.time());
390 break; 390 break;
391 } 391 }
392} 392}
393 393
394void Recurrence::setRecurStart(const QDate &start) 394void Recurrence::setRecurStart(const QDate &start)
395{ 395{
396 mRecurStart.setDate(start); 396 mRecurStart.setDate(start);
397 mRecurStart.setTime(QTime(0,0,0)); 397 mRecurStart.setTime(QTime(0,0,0));
398 switch (recurs) 398 switch (recurs)
399 { 399 {
400 case rMinutely: 400 case rMinutely:
401 case rHourly: 401 case rHourly:
402 break; 402 break;
403 case rDaily: 403 case rDaily:
404 case rWeekly: 404 case rWeekly:
405 case rMonthlyPos: 405 case rMonthlyPos:
406 case rMonthlyDay: 406 case rMonthlyDay:
407 case rYearlyMonth: 407 case rYearlyMonth:
408 case rYearlyDay: 408 case rYearlyDay:
409 case rYearlyPos: 409 case rYearlyPos:
410 default: 410 default:
411 mFloats = true; 411 mFloats = true;
412 break; 412 break;
413 } 413 }
414} 414}
415 415
416void Recurrence::setFloats(bool f) 416void Recurrence::setFloats(bool f)
417{ 417{
418 switch (recurs) 418 switch (recurs)
419 { 419 {
420 case rDaily: 420 case rDaily:
421 case rWeekly: 421 case rWeekly:
422 case rMonthlyPos: 422 case rMonthlyPos:
423 case rMonthlyDay: 423 case rMonthlyDay:
424 case rYearlyMonth: 424 case rYearlyMonth:
425 case rYearlyDay: 425 case rYearlyDay:
426 case rYearlyPos: 426 case rYearlyPos:
427 break; 427 break;
428 case rMinutely: 428 case rMinutely:
429 case rHourly: 429 case rHourly:
430 default: 430 default:
431 return; // can't set sub-daily to floating 431 return; // can't set sub-daily to floating
432 } 432 }
433 mFloats = f; 433 mFloats = f;
434 if (f) { 434 if (f) {
435 mRecurStart.setTime(QTime(0,0,0)); 435 mRecurStart.setTime(QTime(0,0,0));
436 rEndDateTime.setTime(QTime(0,0,0)); 436 rEndDateTime.setTime(QTime(0,0,0));
437 } 437 }
438} 438}
439 439
440int Recurrence::frequency() const 440int Recurrence::frequency() const
441{ 441{
442 return rFreq; 442 return rFreq;
443} 443}
444 444
445int Recurrence::duration() const 445int Recurrence::duration() const
446{ 446{
447 return rDuration; 447 return rDuration;
448} 448}
449 449
450void Recurrence::setDuration(int _rDuration) 450void Recurrence::setDuration(int _rDuration)
451{ 451{
452 if (mRecurReadOnly) return; 452 if (mRecurReadOnly) return;
453 if (_rDuration > 0) { 453 if (_rDuration > 0) {
454 rDuration = _rDuration; 454 rDuration = _rDuration;
455 // Compatibility mode is only needed when reading the calendar in ICalFormatImpl, 455 // Compatibility mode is only needed when reading the calendar in ICalFormatImpl,
456 // so explicitly setting the duration means no backwards compatibility is needed. 456 // so explicitly setting the duration means no backwards compatibility is needed.
457 mCompatDuration = 0; 457 mCompatDuration = 0;
458 } 458 }
459} 459}
460 460
461QString Recurrence::endDateStr(bool shortfmt) const 461QString Recurrence::endDateStr(bool shortfmt) const
462{ 462{
463 return KGlobal::locale()->formatDate(rEndDateTime.date(),shortfmt); 463 return KGlobal::locale()->formatDate(rEndDateTime.date(),shortfmt);
464} 464}
465 465
466const QBitArray &Recurrence::days() const 466const QBitArray &Recurrence::days() const
467{ 467{
468 return rDays; 468 return rDays;
469} 469}
470 470
471const QPtrList<Recurrence::rMonthPos> &Recurrence::monthPositions() const 471const QPtrList<Recurrence::rMonthPos> &Recurrence::monthPositions() const
472{ 472{
473 return rMonthPositions; 473 return rMonthPositions;
474} 474}
475 475
476const QPtrList<Recurrence::rMonthPos> &Recurrence::yearMonthPositions() const 476const QPtrList<Recurrence::rMonthPos> &Recurrence::yearMonthPositions() const
477{ 477{
478 return rMonthPositions; 478 return rMonthPositions;
479} 479}
480 480
481const QPtrList<int> &Recurrence::monthDays() const 481const QPtrList<int> &Recurrence::monthDays() const
482{ 482{
483 return rMonthDays; 483 return rMonthDays;
484} 484}
485 485
486void Recurrence::setMinutely(int _rFreq, int _rDuration) 486void Recurrence::setMinutely(int _rFreq, int _rDuration)
487{ 487{
488 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 488 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
489 return; 489 return;
490 setDailySub(rMinutely, _rFreq, _rDuration); 490 setDailySub(rMinutely, _rFreq, _rDuration);
491} 491}
492 492
493void Recurrence::setMinutely(int _rFreq, const QDateTime &_rEndDateTime) 493void Recurrence::setMinutely(int _rFreq, const QDateTime &_rEndDateTime)
494{ 494{
495 if (mRecurReadOnly) return; 495 if (mRecurReadOnly) return;
496 rEndDateTime = _rEndDateTime; 496 rEndDateTime = _rEndDateTime;
497 setDailySub(rMinutely, _rFreq, 0); 497 setDailySub(rMinutely, _rFreq, 0);
498} 498}
499 499
500void Recurrence::setHourly(int _rFreq, int _rDuration) 500void Recurrence::setHourly(int _rFreq, int _rDuration)
501{ 501{
502 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 502 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
503 return; 503 return;
504 setDailySub(rHourly, _rFreq, _rDuration); 504 setDailySub(rHourly, _rFreq, _rDuration);
505} 505}
506 506
507void Recurrence::setHourly(int _rFreq, const QDateTime &_rEndDateTime) 507void Recurrence::setHourly(int _rFreq, const QDateTime &_rEndDateTime)
508{ 508{
509 if (mRecurReadOnly) return; 509 if (mRecurReadOnly) return;
510 rEndDateTime = _rEndDateTime; 510 rEndDateTime = _rEndDateTime;
511 setDailySub(rHourly, _rFreq, 0); 511 setDailySub(rHourly, _rFreq, 0);
512} 512}
513 513
514void Recurrence::setDaily(int _rFreq, int _rDuration) 514void Recurrence::setDaily(int _rFreq, int _rDuration)
515{ 515{
516 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 516 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
517 return; 517 return;
518 setDailySub(rDaily, _rFreq, _rDuration); 518 setDailySub(rDaily, _rFreq, _rDuration);
519} 519}
520 520
521void Recurrence::setDaily(int _rFreq, const QDate &_rEndDate) 521void Recurrence::setDaily(int _rFreq, const QDate &_rEndDate)
522{ 522{
523 if (mRecurReadOnly) return; 523 if (mRecurReadOnly) return;
524 rEndDateTime.setDate(_rEndDate); 524 rEndDateTime.setDate(_rEndDate);
525 rEndDateTime.setTime(mRecurStart.time()); 525 rEndDateTime.setTime(mRecurStart.time());
526 setDailySub(rDaily, _rFreq, 0); 526 setDailySub(rDaily, _rFreq, 0);
527} 527}
528 528
529void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays, 529void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
530 int _rDuration, int _rWeekStart) 530 int _rDuration, int _rWeekStart)
531{ 531{
532 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 532 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
533 return; 533 return;
534 recurs = rWeekly; 534 recurs = rWeekly;
535 535
536 rFreq = _rFreq; 536 rFreq = _rFreq;
537 rDays = _rDays; 537 rDays = _rDays;
538 rWeekStart = _rWeekStart; 538 rWeekStart = _rWeekStart;
539 rDuration = _rDuration; 539 rDuration = _rDuration;
540 if (mCompatVersion < 310 && _rDuration > 0) { 540 if (mCompatVersion < 310 && _rDuration > 0) {
541 // Backwards compatibility for KDE < 3.1. 541 // Backwards compatibility for KDE < 3.1.
542 // rDuration was set to the number of time periods to recur, 542 // rDuration was set to the number of time periods to recur,
543 // with week start always on a Monday. 543 // with week start always on a Monday.
544 // Convert this to the number of occurrences. 544 // Convert this to the number of occurrences.
545 mCompatDuration = _rDuration; 545 mCompatDuration = _rDuration;
546 int weeks = ((mCompatDuration-1+mRecurExDatesCount)*7) + (7 - mRecurStart.date().dayOfWeek()); 546 int weeks = ((mCompatDuration-1+mRecurExDatesCount)*7) + (7 - mRecurStart.date().dayOfWeek());
547 QDate end(mRecurStart.date().addDays(weeks * rFreq)); 547 QDate end(mRecurStart.date().addDays(weeks * rFreq));
548 rDuration = INT_MAX; // ensure that weeklyCalc() does its job correctly 548 rDuration = INT_MAX; // ensure that weeklyCalc() does its job correctly
549 rDuration = weeklyCalc(COUNT_TO_DATE, end); 549 rDuration = weeklyCalc(COUNT_TO_DATE, end);
550 } else { 550 } else {
551 mCompatDuration = 0; 551 mCompatDuration = 0;
552 } 552 }
553 rMonthPositions.clear(); 553 rMonthPositions.clear();
554 rMonthDays.clear(); 554 rMonthDays.clear();
555 if (mParent) mParent->updated(); 555 if (mParent) mParent->updated();
556} 556}
557 557
558void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays, 558void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
559 const QDate &_rEndDate, int _rWeekStart) 559 const QDate &_rEndDate, int _rWeekStart)
560{ 560{
561 if (mRecurReadOnly) return; 561 if (mRecurReadOnly) return;
562 recurs = rWeekly; 562 recurs = rWeekly;
563 563
564 rFreq = _rFreq; 564 rFreq = _rFreq;
565 rDays = _rDays; 565 rDays = _rDays;
566 rWeekStart = _rWeekStart; 566 rWeekStart = _rWeekStart;
567 rEndDateTime.setDate(_rEndDate); 567 rEndDateTime.setDate(_rEndDate);
568 rEndDateTime.setTime(mRecurStart.time()); 568 rEndDateTime.setTime(mRecurStart.time());
569 rDuration = 0; // set to 0 because there is an end date 569 rDuration = 0; // set to 0 because there is an end date
570 mCompatDuration = 0; 570 mCompatDuration = 0;
571 rMonthPositions.clear(); 571 rMonthPositions.clear();
572 rMonthDays.clear(); 572 rMonthDays.clear();
573 rYearNums.clear(); 573 rYearNums.clear();
574 if (mParent) mParent->updated(); 574 if (mParent) mParent->updated();
575} 575}
576 576
577void Recurrence::setMonthly(short type, int _rFreq, int _rDuration) 577void Recurrence::setMonthly(short type, int _rFreq, int _rDuration)
578{ 578{
579 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 579 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
580 return; 580 return;
581 recurs = type; 581 recurs = type;
582 582
583 rFreq = _rFreq; 583 rFreq = _rFreq;
584 rDuration = _rDuration; 584 rDuration = _rDuration;
585 if (mCompatVersion < 310) 585 if (mCompatVersion < 310)
586 mCompatDuration = (_rDuration > 0) ? _rDuration : 0; 586 mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
587 rYearNums.clear(); 587 rYearNums.clear();
588 if (mParent) mParent->updated(); 588 if (mParent) mParent->updated();
589} 589}
590 590
591void Recurrence::setMonthly(short type, int _rFreq, 591void Recurrence::setMonthly(short type, int _rFreq,
592 const QDate &_rEndDate) 592 const QDate &_rEndDate)
593{ 593{
594 if (mRecurReadOnly) return; 594 if (mRecurReadOnly) return;
595 recurs = type; 595 recurs = type;
596 596
597 rFreq = _rFreq; 597 rFreq = _rFreq;
598 rEndDateTime.setDate(_rEndDate); 598 rEndDateTime.setDate(_rEndDate);
599 rEndDateTime.setTime(mRecurStart.time()); 599 rEndDateTime.setTime(mRecurStart.time());
600 rDuration = 0; // set to 0 because there is an end date 600 rDuration = 0; // set to 0 because there is an end date
601 mCompatDuration = 0; 601 mCompatDuration = 0;
602 rYearNums.clear(); 602 rYearNums.clear();
603 if (mParent) mParent->updated(); 603 if (mParent) mParent->updated();
604} 604}
605 605
606void Recurrence::addMonthlyPos(short _rPos, const QBitArray &_rDays) 606void Recurrence::addMonthlyPos(short _rPos, const QBitArray &_rDays)
607{ 607{
608 if (recurs == rMonthlyPos) 608 if (recurs == rMonthlyPos)
609 addMonthlyPos_(_rPos, _rDays); 609 addMonthlyPos_(_rPos, _rDays);
610} 610}
611 611
612void Recurrence::addMonthlyPos_(short _rPos, const QBitArray &_rDays) 612void Recurrence::addMonthlyPos_(short _rPos, const QBitArray &_rDays)
613{ 613{
614 if (mRecurReadOnly 614 if (mRecurReadOnly
615 || _rPos == 0 || _rPos > 5 || _rPos < -5) // invalid week number 615 || _rPos == 0 || _rPos > 5 || _rPos < -5) // invalid week number
616 return; 616 return;
617 617
618 for (rMonthPos* it = rMonthPositions.first(); it; it = rMonthPositions.next()) { 618 for (rMonthPos* it = rMonthPositions.first(); it; it = rMonthPositions.next()) {
619 int itPos = it->negative ? -it->rPos : it->rPos; 619 int itPos = it->negative ? -it->rPos : it->rPos;
620 if (_rPos == itPos) { 620 if (_rPos == itPos) {
621 // This week is already in the list. 621 // This week is already in the list.
622 // Combine the specified days with those in the list. 622 // Combine the specified days with those in the list.
623 it->rDays |= _rDays; 623 it->rDays |= _rDays;
624 if (mParent) mParent->updated(); 624 if (mParent) mParent->updated();
625 return; 625 return;
626 } 626 }
627 } 627 }
628 // Add the new position to the list 628 // Add the new position to the list
629 rMonthPos *tmpPos = new rMonthPos; 629 rMonthPos *tmpPos = new rMonthPos;
630 if (_rPos > 0) { 630 if (_rPos > 0) {
631 tmpPos->rPos = _rPos; 631 tmpPos->rPos = _rPos;
632 tmpPos->negative = false; 632 tmpPos->negative = false;
633 } else { 633 } else {
634 tmpPos->rPos = -_rPos; // take abs() 634 tmpPos->rPos = -_rPos; // take abs()
635 tmpPos->negative = true; 635 tmpPos->negative = true;
636 } 636 }
637 tmpPos->rDays = _rDays; 637 tmpPos->rDays = _rDays;
638 tmpPos->rDays.detach(); 638 tmpPos->rDays.detach();
639 rMonthPositions.append(tmpPos); 639 rMonthPositions.append(tmpPos);
640 640
641 if (mCompatVersion < 310 && mCompatDuration > 0) { 641 if (mCompatVersion < 310 && mCompatDuration > 0) {
642 // Backwards compatibility for KDE < 3.1. 642 // Backwards compatibility for KDE < 3.1.
643 // rDuration was set to the number of time periods to recur. 643 // rDuration was set to the number of time periods to recur.
644 // Convert this to the number of occurrences. 644 // Convert this to the number of occurrences.
645 int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq; 645 int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq;
646 int month = mRecurStart.date().month() - 1 + monthsAhead; 646 int month = mRecurStart.date().month() - 1 + monthsAhead;
647 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31); 647 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
648 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly 648 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
649 rDuration = recurCalc(COUNT_TO_DATE, end); 649 rDuration = recurCalc(COUNT_TO_DATE, end);
650 } 650 }
651 651
652 if (mParent) mParent->updated(); 652 if (mParent) mParent->updated();
653} 653}
654 654
655void Recurrence::addMonthlyDay(short _rDay) 655void Recurrence::addMonthlyDay(short _rDay)
656{ 656{
657 if (mRecurReadOnly || recurs != rMonthlyDay 657 if (mRecurReadOnly || (recurs != rMonthlyDay && recurs != rYearlyMonth)
658 || _rDay == 0 || _rDay > 31 || _rDay < -31) // invalid day number 658 || _rDay == 0 || _rDay > 31 || _rDay < -31) // invalid day number
659 return; 659 return;
660 for (int* it = rMonthDays.first(); it; it = rMonthDays.next()) { 660 for (int* it = rMonthDays.first(); it; it = rMonthDays.next()) {
661 if (_rDay == *it) 661 if (_rDay == *it)
662 return; // this day is already in the list - avoid duplication 662 return; // this day is already in the list - avoid duplication
663 } 663 }
664 int *tmpDay = new int; 664 int *tmpDay = new int;
665 *tmpDay = _rDay; 665 *tmpDay = _rDay;
666 rMonthDays.append(tmpDay); 666 rMonthDays.append(tmpDay);
667 667
668 if (mCompatVersion < 310 && mCompatDuration > 0) { 668 if (mCompatVersion < 310 && mCompatDuration > 0) {
669 // Backwards compatibility for KDE < 3.1. 669 // Backwards compatibility for KDE < 3.1.
670 // rDuration was set to the number of time periods to recur. 670 // rDuration was set to the number of time periods to recur.
671 // Convert this to the number of occurrences. 671 // Convert this to the number of occurrences.
672 int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq; 672 int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq;
673 int month = mRecurStart.date().month() - 1 + monthsAhead; 673 int month = mRecurStart.date().month() - 1 + monthsAhead;
674 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31); 674 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
675 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly 675 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
676 rDuration = recurCalc(COUNT_TO_DATE, end); 676 rDuration = recurCalc(COUNT_TO_DATE, end);
677 } 677 }
678 678
679 if (mParent) mParent->updated(); 679 if (mParent) mParent->updated();
680} 680}
681 681
682void Recurrence::setYearly(int type, int _rFreq, int _rDuration) 682void Recurrence::setYearly(int type, int _rFreq, int _rDuration)
683{ 683{
684 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 684 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
685 return; 685 return;
686 if (mCompatVersion < 310) 686 if (mCompatVersion < 310)
687 mCompatDuration = (_rDuration > 0) ? _rDuration : 0; 687 mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
688 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, _rDuration); 688 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, _rDuration);
689} 689}
690 690
691void Recurrence::setYearly(int type, int _rFreq, const QDate &_rEndDate) 691void Recurrence::setYearly(int type, int _rFreq, const QDate &_rEndDate)
692{ 692{
693 if (mRecurReadOnly) return; 693 if (mRecurReadOnly) return;
694 rEndDateTime.setDate(_rEndDate); 694 rEndDateTime.setDate(_rEndDate);
695 rEndDateTime.setTime(mRecurStart.time()); 695 rEndDateTime.setTime(mRecurStart.time());
696 mCompatDuration = 0; 696 mCompatDuration = 0;
697 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, 0); 697 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, 0);
698} 698}
699 699
700void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, int _rDuration) 700void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, int _rDuration)
701{ 701{
702 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 702 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
703 return; 703 return;
704 if (mCompatVersion < 310) 704 if (mCompatVersion < 310)
705 mCompatDuration = (_rDuration > 0) ? _rDuration : 0; 705 mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
706 setYearly_(rYearlyMonth, type, _rFreq, _rDuration); 706 setYearly_(rYearlyMonth, type, _rFreq, _rDuration);
707} 707}
708 708
709void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, const QDate &_rEndDate) 709void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, const QDate &_rEndDate)
710{ 710{
711 if (mRecurReadOnly) return; 711 if (mRecurReadOnly) return;
712 rEndDateTime.setDate(_rEndDate); 712 rEndDateTime.setDate(_rEndDate);
713 rEndDateTime.setTime(mRecurStart.time()); 713 rEndDateTime.setTime(mRecurStart.time());
714 mCompatDuration = 0; 714 mCompatDuration = 0;
715 setYearly_(rYearlyMonth, type, _rFreq, 0); 715 setYearly_(rYearlyMonth, type, _rFreq, 0);
716} 716}
717 717
718void Recurrence::addYearlyMonthPos(short _rPos, const QBitArray &_rDays) 718void Recurrence::addYearlyMonthPos(short _rPos, const QBitArray &_rDays)
719{ 719{
720 if (recurs == rYearlyPos) 720 if (recurs == rYearlyPos)
721 addMonthlyPos_(_rPos, _rDays); 721 addMonthlyPos_(_rPos, _rDays);
722} 722}
723 723
724const QPtrList<int> &Recurrence::yearNums() const 724const QPtrList<int> &Recurrence::yearNums() const
725{ 725{
726 return rYearNums; 726 return rYearNums;
727} 727}
728 728void Recurrence::addYearlyMonth(short _rPos )
729{
730 if (mRecurReadOnly || recurs != rYearlyMonth) // invalid day/month number
731 return;
732 rMonthPos *tmpPos = new rMonthPos;
733 if ( _rPos > 0) {
734 tmpPos->rPos = _rPos;
735 tmpPos->negative = false;
736 } else {
737 tmpPos->rPos = -_rPos; // take abs()
738 tmpPos->negative = true;
739 }
740 rMonthPositions.append(tmpPos);
741}
729void Recurrence::addYearlyNum(short _rNum) 742void Recurrence::addYearlyNum(short _rNum)
730{ 743{
731 if (mRecurReadOnly 744 if (mRecurReadOnly
732 || (recurs != rYearlyMonth && recurs != rYearlyDay && recurs != rYearlyPos) 745 || (recurs != rYearlyMonth && recurs != rYearlyDay && recurs != rYearlyPos)
733 || _rNum <= 0) // invalid day/month number 746 || _rNum <= 0) // invalid day/month number
734 return; 747 return;
735 748
736 if (mCompatVersion < 310 && mCompatRecurs == rYearlyDay) { 749 if (mCompatVersion < 310 && mCompatRecurs == rYearlyDay) {
737 // Backwards compatibility for KDE < 3.1. 750 // Backwards compatibility for KDE < 3.1.
738 // Dates were stored as day numbers, with a fiddle to take account of leap years. 751 // Dates were stored as day numbers, with a fiddle to take account of leap years.
739 // Convert the day number to a month. 752 // Convert the day number to a month.
740 if (_rNum <= 0 || _rNum > 366 || (_rNum == 366 && mRecurStart.date().daysInYear() < 366)) 753 if (_rNum <= 0 || _rNum > 366 || (_rNum == 366 && mRecurStart.date().daysInYear() < 366))
741 return; // invalid day number 754 return; // invalid day number
742 _rNum = QDate(mRecurStart.date().year(), 1, 1).addDays(_rNum - 1).month(); 755 _rNum = QDate(mRecurStart.date().year(), 1, 1).addDays(_rNum - 1).month();
743 } else 756 } else
744 if ((recurs == rYearlyMonth || recurs == rYearlyPos) && _rNum > 12 757 if ((recurs == rYearlyMonth || recurs == rYearlyPos) && _rNum > 12
745 || recurs == rYearlyDay && _rNum > 366) 758 || recurs == rYearlyDay && _rNum > 366)
746 return; // invalid day number 759 return; // invalid day number
747 760
748 uint i = 0; 761 uint i = 0;
749 for (int* it = rYearNums.first(); it && _rNum >= *it; it = rYearNums.next()) { 762 for (int* it = rYearNums.first(); it && _rNum >= *it; it = rYearNums.next()) {
750 if (_rNum == *it) 763 if (_rNum == *it)
751 return; // this day/month is already in the list - avoid duplication 764 return; // this day/month is already in the list - avoid duplication
752 ++i; 765 ++i;
753 } 766 }
754 767
755 int *tmpNum = new int; 768 int *tmpNum = new int;
756 *tmpNum = _rNum; 769 *tmpNum = _rNum;
757 rYearNums.insert(i, tmpNum); // insert the day/month in a sorted position 770 rYearNums.insert(i, tmpNum); // insert the day/month in a sorted position
758 771
759 if (mCompatVersion < 310 && mCompatDuration > 0) { 772 if (mCompatVersion < 310 && mCompatDuration > 0) {
760 // Backwards compatibility for KDE < 3.1. 773 // Backwards compatibility for KDE < 3.1.
761 // rDuration was set to the number of time periods to recur. 774 // rDuration was set to the number of time periods to recur.
762 // Convert this to the number of occurrences. 775 // Convert this to the number of occurrences.
763 QDate end(mRecurStart.date().year() + (mCompatDuration-1+mRecurExDatesCount)*rFreq, 12, 31); 776 QDate end(mRecurStart.date().year() + (mCompatDuration-1+mRecurExDatesCount)*rFreq, 12, 31);
764 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly 777 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
765 rDuration = recurCalc(COUNT_TO_DATE, end); 778 rDuration = recurCalc(COUNT_TO_DATE, end);
766 } 779 }
767 780
768 if (mParent) mParent->updated(); 781 if (mParent) mParent->updated();
769} 782}
770 783
771 784
772QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime, bool *last) const 785QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime, bool *last) const
773{ 786{
774 if (last) 787 if (last)
775 *last = false; 788 *last = false;
776 int freq; 789 int freq;
777 switch (recurs) 790 switch (recurs)
778 { 791 {
779 case rMinutely: 792 case rMinutely:
780 freq = rFreq * 60; 793 freq = rFreq * 60;
781 break; 794 break;
782 case rHourly: 795 case rHourly:
783 freq = rFreq * 3600; 796 freq = rFreq * 3600;
784 break; 797 break;
785 case rDaily: 798 case rDaily:
786 case rWeekly: 799 case rWeekly:
787 case rMonthlyPos: 800 case rMonthlyPos:
788 case rMonthlyDay: 801 case rMonthlyDay:
789 case rYearlyMonth: 802 case rYearlyMonth:
790 case rYearlyDay: 803 case rYearlyDay:
791 case rYearlyPos: { 804 case rYearlyPos: {
792 QDate preDate = preDateTime.date(); 805 QDate preDate = preDateTime.date();
793 if (!mFloats && mRecurStart.time() > preDateTime.time()) 806 if (!mFloats && mRecurStart.time() > preDateTime.time())
794 preDate = preDate.addDays(-1); 807 preDate = preDate.addDays(-1);
795 return QDateTime(getNextDateNoTime(preDate, last), mRecurStart.time()); 808 return QDateTime(getNextDateNoTime(preDate, last), mRecurStart.time());
796 } 809 }
797 default: 810 default:
798 return QDateTime(); 811 return QDateTime();
799 } 812 }
800 813
801 // It's a sub-daily recurrence 814 // It's a sub-daily recurrence
802 if (preDateTime < mRecurStart) 815 if (preDateTime < mRecurStart)
803 return mRecurStart; 816 return mRecurStart;
804 int count = mRecurStart.secsTo(preDateTime) / freq + 2; 817 int count = mRecurStart.secsTo(preDateTime) / freq + 2;
805 if (rDuration > 0) { 818 if (rDuration > 0) {
806 if (count > rDuration) 819 if (count > rDuration)
807 return QDateTime(); 820 return QDateTime();
808 if (last && count == rDuration) 821 if (last && count == rDuration)
809 *last = true; 822 *last = true;
810 } 823 }
811 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq); 824 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
812 if (rDuration == 0) { 825 if (rDuration == 0) {
813 if (endtime > rEndDateTime) 826 if (endtime > rEndDateTime)
814 return QDateTime(); 827 return QDateTime();
815 if (last && endtime == rEndDateTime) 828 if (last && endtime == rEndDateTime)
816 *last = true; 829 *last = true;
817 } 830 }
818 return endtime; 831 return endtime;
819} 832}
820 833
821QDate Recurrence::getNextDate(const QDate &preDate, bool *last) const 834QDate Recurrence::getNextDate(const QDate &preDate, bool *last) const
822{ 835{
823 if (last) 836 if (last)
824 *last = false; 837 *last = false;
825 switch (recurs) 838 switch (recurs)
826 { 839 {
827 case rMinutely: 840 case rMinutely:
828 case rHourly: 841 case rHourly:
829 return getNextDateTime(QDateTime(preDate, QTime(23,59,59)), last).date(); 842 return getNextDateTime(QDateTime(preDate, QTime(23,59,59)), last).date();
830 case rDaily: 843 case rDaily:
831 case rWeekly: 844 case rWeekly:
832 case rMonthlyPos: 845 case rMonthlyPos:
833 case rMonthlyDay: 846 case rMonthlyDay:
834 case rYearlyMonth: 847 case rYearlyMonth:
835 case rYearlyDay: 848 case rYearlyDay:
836 case rYearlyPos: 849 case rYearlyPos:
837 return getNextDateNoTime(preDate, last); 850 return getNextDateNoTime(preDate, last);
838 default: 851 default:
839 return QDate(); 852 return QDate();
840 } 853 }
841} 854}
842 855
843 856
844QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime, bool *last) const 857QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime, bool *last) const
845{ 858{
846 if (last) 859 if (last)
847 *last = false; 860 *last = false;
848 int freq; 861 int freq;
849 switch (recurs) 862 switch (recurs)
850 { 863 {
851 case rMinutely: 864 case rMinutely:
852 freq = rFreq * 60; 865 freq = rFreq * 60;
853 break; 866 break;
854 case rHourly: 867 case rHourly:
855 freq = rFreq * 3600; 868 freq = rFreq * 3600;
856 break; 869 break;
857 case rDaily: 870 case rDaily:
858 case rWeekly: 871 case rWeekly:
859 case rMonthlyPos: 872 case rMonthlyPos:
860 case rMonthlyDay: 873 case rMonthlyDay:
861 case rYearlyMonth: 874 case rYearlyMonth:
862 case rYearlyDay: 875 case rYearlyDay:
863 case rYearlyPos: { 876 case rYearlyPos: {
864 QDate afterDate = afterDateTime.date(); 877 QDate afterDate = afterDateTime.date();
865 if (!mFloats && mRecurStart.time() < afterDateTime.time()) 878 if (!mFloats && mRecurStart.time() < afterDateTime.time())
866 afterDate = afterDate.addDays(1); 879 afterDate = afterDate.addDays(1);
867 return QDateTime(getPreviousDateNoTime(afterDate, last), mRecurStart.time()); 880 return QDateTime(getPreviousDateNoTime(afterDate, last), mRecurStart.time());
868 } 881 }
869 default: 882 default:
870 return QDateTime(); 883 return QDateTime();
871 } 884 }
872 885
873 // It's a sub-daily recurrence 886 // It's a sub-daily recurrence
874 if (afterDateTime <= mRecurStart) 887 if (afterDateTime <= mRecurStart)
875 return QDateTime(); 888 return QDateTime();
876 int count = (mRecurStart.secsTo(afterDateTime) - 1) / freq + 1; 889 int count = (mRecurStart.secsTo(afterDateTime) - 1) / freq + 1;
877 if (rDuration > 0) { 890 if (rDuration > 0) {
878 if (count > rDuration) 891 if (count > rDuration)
879 count = rDuration; 892 count = rDuration;
880 if (last && count == rDuration) 893 if (last && count == rDuration)
881 *last = true; 894 *last = true;
882 } 895 }
883 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq); 896 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
884 if (rDuration == 0) { 897 if (rDuration == 0) {
885 if (endtime > rEndDateTime) 898 if (endtime > rEndDateTime)
886 endtime = rEndDateTime; 899 endtime = rEndDateTime;
887 if (last && endtime == rEndDateTime) 900 if (last && endtime == rEndDateTime)
888 *last = true; 901 *last = true;
889 } 902 }
890 return endtime; 903 return endtime;
891} 904}
892 905
893QDate Recurrence::getPreviousDate(const QDate &afterDate, bool *last) const 906QDate Recurrence::getPreviousDate(const QDate &afterDate, bool *last) const
894{ 907{
895 if (last) 908 if (last)
896 *last = false; 909 *last = false;
897 switch (recurs) 910 switch (recurs)
898 { 911 {
899 case rMinutely: 912 case rMinutely:
900 case rHourly: 913 case rHourly:
901 return getPreviousDateTime(QDateTime(afterDate, QTime(0,0,0)), last).date(); 914 return getPreviousDateTime(QDateTime(afterDate, QTime(0,0,0)), last).date();
902 case rDaily: 915 case rDaily:
903 case rWeekly: 916 case rWeekly:
904 case rMonthlyPos: 917 case rMonthlyPos:
905 case rMonthlyDay: 918 case rMonthlyDay:
906 case rYearlyMonth: 919 case rYearlyMonth:
907 case rYearlyDay: 920 case rYearlyDay:
908 case rYearlyPos: 921 case rYearlyPos:
909 return getPreviousDateNoTime(afterDate, last); 922 return getPreviousDateNoTime(afterDate, last);
910 default: 923 default:
911 return QDate(); 924 return QDate();
912 } 925 }
913} 926}
914 927
915 928
916/***************************** PROTECTED FUNCTIONS ***************************/ 929/***************************** PROTECTED FUNCTIONS ***************************/
917 930
918bool Recurrence::recursSecondly(const QDate &qd, int secondFreq) const 931bool Recurrence::recursSecondly(const QDate &qd, int secondFreq) const
919{ 932{
920 if ((qd >= mRecurStart.date()) && 933 if ((qd >= mRecurStart.date()) &&
921 ((rDuration > 0) && (qd <= endDate()) || 934 ((rDuration > 0) && (qd <= endDate()) ||
922 ((rDuration == 0) && (qd <= rEndDateTime.date())) || 935 ((rDuration == 0) && (qd <= rEndDateTime.date())) ||
923 (rDuration == -1))) { 936 (rDuration == -1))) {
924 // The date queried falls within the range of the event. 937 // The date queried falls within the range of the event.
925 if (secondFreq < 24*3600) 938 if (secondFreq < 24*3600)
926 return true; // the event recurs at least once each day 939 return true; // the event recurs at least once each day
927 int after = mRecurStart.secsTo(QDateTime(qd)); 940 int after = mRecurStart.secsTo(QDateTime(qd));
928 if (after / secondFreq != (after + 24*3600) / secondFreq) 941 if (after / secondFreq != (after + 24*3600) / secondFreq)
929 return true; 942 return true;
930 } 943 }
931 return false; 944 return false;
932} 945}
933 946
934bool Recurrence::recursMinutelyAt(const QDateTime &dt, int minuteFreq) const 947bool Recurrence::recursMinutelyAt(const QDateTime &dt, int minuteFreq) const
935{ 948{
936 if ((dt >= mRecurStart) && 949 if ((dt >= mRecurStart) &&
937 ((rDuration > 0) && (dt <= endDateTime()) || 950 ((rDuration > 0) && (dt <= endDateTime()) ||
938 ((rDuration == 0) && (dt <= rEndDateTime)) || 951 ((rDuration == 0) && (dt <= rEndDateTime)) ||
939 (rDuration == -1))) { 952 (rDuration == -1))) {
940 // The time queried falls within the range of the event. 953 // The time queried falls within the range of the event.
941 if (((mRecurStart.secsTo(dt) / 60) % minuteFreq) == 0) 954 if (((mRecurStart.secsTo(dt) / 60) % minuteFreq) == 0)
942 return true; 955 return true;
943 } 956 }
944 return false; 957 return false;
945} 958}
946 959
947bool Recurrence::recursDaily(const QDate &qd) const 960bool Recurrence::recursDaily(const QDate &qd) const
948{ 961{
949 QDate dStart = mRecurStart.date(); 962 QDate dStart = mRecurStart.date();
950 if ((dStart.daysTo(qd) % rFreq) == 0) { 963 if ((dStart.daysTo(qd) % rFreq) == 0) {
951 // The date is a day which recurs 964 // The date is a day which recurs
952 if (qd >= dStart 965 if (qd >= dStart
953 && ((rDuration > 0 && qd <= endDate()) || 966 && ((rDuration > 0 && qd <= endDate()) ||
954 (rDuration == 0 && qd <= rEndDateTime.date()) || 967 (rDuration == 0 && qd <= rEndDateTime.date()) ||
955 rDuration == -1)) { 968 rDuration == -1)) {
956 // The date queried falls within the range of the event. 969 // The date queried falls within the range of the event.
957 return true; 970 return true;
958 } 971 }
959 } 972 }
960 return false; 973 return false;
961} 974}
962 975
963bool Recurrence::recursWeekly(const QDate &qd) const 976bool Recurrence::recursWeekly(const QDate &qd) const
964{ 977{
965 QDate dStart = mRecurStart.date(); 978 QDate dStart = mRecurStart.date();
966 if ((dStart.daysTo(qd)/7) % rFreq == 0) { 979 if ((dStart.daysTo(qd)/7) % rFreq == 0) {
967 // The date is in a week which recurs 980 // The date is in a week which recurs
968 if (qd >= dStart 981 if (qd >= dStart
969 && ((rDuration > 0 && qd <= endDate()) || 982 && ((rDuration > 0 && qd <= endDate()) ||
970 (rDuration == 0 && qd <= rEndDateTime.date()) || 983 (rDuration == 0 && qd <= rEndDateTime.date()) ||
971 rDuration == -1)) { 984 rDuration == -1)) {
972 // The date queried falls within the range of the event. 985 // The date queried falls within the range of the event.
973 // check if the bits set match today. 986 // check if the bits set match today.
974 int i = qd.dayOfWeek()-1; 987 int i = qd.dayOfWeek()-1;
975 if (rDays.testBit((uint) i)) 988 if (rDays.testBit((uint) i))
976 return true; 989 return true;
977 } 990 }
978 } 991 }
979 return false; 992 return false;
980} 993}
981 994
982bool Recurrence::recursMonthly(const QDate &qd) const 995bool Recurrence::recursMonthly(const QDate &qd) const
983{ 996{
984 QDate dStart = mRecurStart.date(); 997 QDate dStart = mRecurStart.date();
985 int year = qd.year(); 998 int year = qd.year();
986 int month = qd.month(); 999 int month = qd.month();
987 int day = qd.day(); 1000 int day = qd.day();
988 // calculate how many months ahead this date is from the original 1001 // calculate how many months ahead this date is from the original
989 // event's date 1002 // event's date
990 int monthsAhead = (year - dStart.year()) * 12 + (month - dStart.month()); 1003 int monthsAhead = (year - dStart.year()) * 12 + (month - dStart.month());
991 if ((monthsAhead % rFreq) == 0) { 1004 if ((monthsAhead % rFreq) == 0) {
992 // The date is in a month which recurs 1005 // The date is in a month which recurs
993 if (qd >= dStart 1006 if (qd >= dStart
994 && ((rDuration > 0 && qd <= endDate()) || 1007 && ((rDuration > 0 && qd <= endDate()) ||
995 (rDuration == 0 && qd <= rEndDateTime.date()) || 1008 (rDuration == 0 && qd <= rEndDateTime.date()) ||
996 rDuration == -1)) { 1009 rDuration == -1)) {
997 // The date queried falls within the range of the event. 1010 // The date queried falls within the range of the event.
998 QValueList<int> days; 1011 QValueList<int> days;
999 int daysInMonth = qd.daysInMonth(); 1012 int daysInMonth = qd.daysInMonth();
1000 if (recurs == rMonthlyDay) 1013 if (recurs == rMonthlyDay)
1001 getMonthlyDayDays(days, daysInMonth); 1014 getMonthlyDayDays(days, daysInMonth);
1002 else if (recurs == rMonthlyPos) 1015 else if (recurs == rMonthlyPos)
1003 getMonthlyPosDays(days, daysInMonth, QDate(year, month, 1).dayOfWeek()); 1016 getMonthlyPosDays(days, daysInMonth, QDate(year, month, 1).dayOfWeek());
1004 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) { 1017 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) {
1005 if (*it == day) 1018 if (*it == day)
1006 return true; 1019 return true;
1007 } 1020 }
1008 // no dates matched 1021 // no dates matched
1009 } 1022 }
1010 } 1023 }
1011 return false; 1024 return false;
1012} 1025}
1013 1026
1014bool Recurrence::recursYearlyByMonth(const QDate &qd) const 1027bool Recurrence::recursYearlyByMonth(const QDate &qd) const
1015{ 1028{
1016 QDate dStart = mRecurStart.date(); 1029 QDate dStart = mRecurStart.date();
1017 int startDay = dStart.day(); 1030 int startDay = dStart.day();
1018 int qday = qd.day(); 1031 int qday = qd.day();
1019 int qmonth = qd.month(); 1032 int qmonth = qd.month();
1020 int qyear = qd.year(); 1033 int qyear = qd.year();
1021 bool match = (qday == startDay); 1034 bool match = (qday == startDay);
1022 if (!match && startDay == 29 && dStart.month() == 2) { 1035 if (!match && startDay == 29 && dStart.month() == 2) {
1023 // It's a recurrence on February 29th 1036 // It's a recurrence on February 29th
1024 switch (mFeb29YearlyType) { 1037 switch (mFeb29YearlyType) {
1025 case rFeb28: 1038 case rFeb28:
1026 if (qday == 28 && qmonth == 2 && !QDate::leapYear(qyear)) 1039 if (qday == 28 && qmonth == 2 && !QDate::leapYear(qyear))
1027 match = true; 1040 match = true;
1028 break; 1041 break;
1029 case rMar1: 1042 case rMar1:
1030 if (qday == 1 && qmonth == 3 && !QDate::leapYear(qyear)) { 1043 if (qday == 1 && qmonth == 3 && !QDate::leapYear(qyear)) {
1031 qmonth = 2; 1044 qmonth = 2;
1032 match = true; 1045 match = true;
1033 } 1046 }
1034 break; 1047 break;
1035 case rFeb29: 1048 case rFeb29:
1036 break; 1049 break;
1037 } 1050 }
1038 } 1051 }
1039 1052
1040 if (match) { 1053 if (match) {
1041 // The day of the month matches. Calculate how many years ahead 1054 // The day of the month matches. Calculate how many years ahead
1042 // this date is from the original event's date. 1055 // this date is from the original event's date.
1043 int yearsAhead = (qyear - dStart.year()); 1056 int yearsAhead = (qyear - dStart.year());
1044 if (yearsAhead % rFreq == 0) { 1057 if (yearsAhead % rFreq == 0) {
1045 // The date is in a year which recurs 1058 // The date is in a year which recurs
1046 if (qd >= dStart 1059 if (qd >= dStart
1047 && ((rDuration > 0 && qd <= endDate()) || 1060 && ((rDuration > 0 && qd <= endDate()) ||
1048 (rDuration == 0 && qd <= rEndDateTime.date()) || 1061 (rDuration == 0 && qd <= rEndDateTime.date()) ||
1049 rDuration == -1)) { 1062 rDuration == -1)) {
1050 // The date queried falls within the range of the event. 1063 // The date queried falls within the range of the event.
1051 int i = qmonth; 1064 int i = qmonth;
1052 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { 1065 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
1053 if (i == *qlin.current()) 1066 if (i == *qlin.current())
1054 return true; 1067 return true;
1055 } 1068 }
1056 } 1069 }
1057 } 1070 }
1058 } 1071 }
1059 return false; 1072 return false;
1060} 1073}
1061 1074
1062bool Recurrence::recursYearlyByPos(const QDate &qd) const 1075bool Recurrence::recursYearlyByPos(const QDate &qd) const
1063{ 1076{
1064 QDate dStart = mRecurStart.date(); 1077 QDate dStart = mRecurStart.date();
1065 int year = qd.year(); 1078 int year = qd.year();
1066 int month = qd.month(); 1079 int month = qd.month();
1067 int day = qd.day(); 1080 int day = qd.day();
1068 // calculate how many years ahead this date is from the original 1081 // calculate how many years ahead this date is from the original
1069 // event's date 1082 // event's date
1070 int yearsAhead = (year - dStart.year()); 1083 int yearsAhead = (year - dStart.year());
1071 if (yearsAhead % rFreq == 0) { 1084 if (yearsAhead % rFreq == 0) {
1072 // The date is in a year which recurs 1085 // The date is in a year which recurs
1073 if (qd >= dStart 1086 if (qd >= dStart
1074 && ((rDuration > 0 && qd <= endDate()) || 1087 && ((rDuration > 0 && qd <= endDate()) ||
1075 (rDuration == 0 && qd <= rEndDateTime.date()) || 1088 (rDuration == 0 && qd <= rEndDateTime.date()) ||
1076 rDuration == -1)) { 1089 rDuration == -1)) {
1077 // The date queried falls within the range of the event. 1090 // The date queried falls within the range of the event.
1078 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { 1091 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
1079 if (month == *qlin.current()) { 1092 if (month == *qlin.current()) {
1080 // The month recurs 1093 // The month recurs
1081 QValueList<int> days; 1094 QValueList<int> days;
1082 getMonthlyPosDays(days, qd.daysInMonth(), QDate(year, month, 1).dayOfWeek()); 1095 getMonthlyPosDays(days, qd.daysInMonth(), QDate(year, month, 1).dayOfWeek());
1083 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) { 1096 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) {
1084 if (*it == day) 1097 if (*it == day)
1085 return true; 1098 return true;
1086 } 1099 }
1087 } 1100 }
1088 } 1101 }
1089 } 1102 }
1090 } 1103 }
1091 return false; 1104 return false;
1092} 1105}
1093 1106
1094bool Recurrence::recursYearlyByDay(const QDate &qd) const 1107bool Recurrence::recursYearlyByDay(const QDate &qd) const
1095{ 1108{
1096 QDate dStart = mRecurStart.date(); 1109 QDate dStart = mRecurStart.date();
1097 // calculate how many years ahead this date is from the original 1110 // calculate how many years ahead this date is from the original
1098 // event's date 1111 // event's date
1099 int yearsAhead = (qd.year() - dStart.year()); 1112 int yearsAhead = (qd.year() - dStart.year());
1100 if (yearsAhead % rFreq == 0) { 1113 if (yearsAhead % rFreq == 0) {
1101 // The date is in a year which recurs 1114 // The date is in a year which recurs
1102 if (qd >= dStart 1115 if (qd >= dStart
1103 && ((rDuration > 0 && qd <= endDate()) || 1116 && ((rDuration > 0 && qd <= endDate()) ||
1104 (rDuration == 0 && qd <= rEndDateTime.date()) || 1117 (rDuration == 0 && qd <= rEndDateTime.date()) ||
1105 rDuration == -1)) { 1118 rDuration == -1)) {
1106 // The date queried falls within the range of the event. 1119 // The date queried falls within the range of the event.
1107 int i = qd.dayOfYear(); 1120 int i = qd.dayOfYear();
1108 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { 1121 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
1109 if (i == *qlin.current()) 1122 if (i == *qlin.current())
1110 return true; 1123 return true;
1111 } 1124 }
1112 } 1125 }
1113 } 1126 }
1114 return false; 1127 return false;
1115} 1128}
1116 1129
1117/* Get the date of the next recurrence, after the specified date. 1130/* Get the date of the next recurrence, after the specified date.
1118 * If 'last' is non-null, '*last' is set to true if the next recurrence is the 1131 * If 'last' is non-null, '*last' is set to true if the next recurrence is the
1119 * last recurrence, else false. 1132 * last recurrence, else false.
1120 * Reply = date of next recurrence, or invalid date if none. 1133 * Reply = date of next recurrence, or invalid date if none.
1121 */ 1134 */
1122QDate Recurrence::getNextDateNoTime(const QDate &preDate, bool *last) const 1135QDate Recurrence::getNextDateNoTime(const QDate &preDate, bool *last) const
1123{ 1136{
1124 if (last) 1137 if (last)
1125 *last = false; 1138 *last = false;
1126 QDate dStart = mRecurStart.date(); 1139 QDate dStart = mRecurStart.date();
1127 if (preDate < dStart) 1140 if (preDate < dStart)
1128 return dStart; 1141 return dStart;
1129 QDate earliestDate = preDate.addDays(1); 1142 QDate earliestDate = preDate.addDays(1);
1130 QDate nextDate; 1143 QDate nextDate;
1131 1144
1132 switch (recurs) { 1145 switch (recurs) {
1133 case rDaily: 1146 case rDaily:
1134 nextDate = dStart.addDays((dStart.daysTo(preDate)/rFreq + 1) * rFreq); 1147 nextDate = dStart.addDays((dStart.daysTo(preDate)/rFreq + 1) * rFreq);
1135 break; 1148 break;
1136 1149
1137 case rWeekly: { 1150 case rWeekly: {
1138 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart 1151 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart
1139 int earliestDayOfWeek = earliestDate.dayOfWeek(); 1152 int earliestDayOfWeek = earliestDate.dayOfWeek();
1140 int weeksAhead = start.daysTo(earliestDate) / 7; 1153 int weeksAhead = start.daysTo(earliestDate) / 7;
1141 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week 1154 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week
1142 weeksAhead -= notThisWeek; // latest week which recurred 1155 weeksAhead -= notThisWeek; // latest week which recurred
1143 int weekday = 0; 1156 int weekday = 0;
1144 // First check for any remaining day this week, if this week is a recurring week 1157 // First check for any remaining day this week, if this week is a recurring week
1145 if (!notThisWeek) 1158 if (!notThisWeek)
1146 weekday = getFirstDayInWeek(earliestDayOfWeek); 1159 weekday = getFirstDayInWeek(earliestDayOfWeek);
1147 // Check for a day in the next scheduled week 1160 // Check for a day in the next scheduled week
1148 if (!weekday && earliestDayOfWeek > 1) 1161 if (!weekday && earliestDayOfWeek > 1)
1149 weekday = getFirstDayInWeek(rWeekStart) + rFreq*7; 1162 weekday = getFirstDayInWeek(rWeekStart) + rFreq*7;
1150 if (weekday) 1163 if (weekday)
1151 nextDate = start.addDays(weeksAhead*7 + weekday - 1); 1164 nextDate = start.addDays(weeksAhead*7 + weekday - 1);
1152 break; 1165 break;
1153 } 1166 }
1154 case rMonthlyDay: 1167 case rMonthlyDay:
1155 case rMonthlyPos: { 1168 case rMonthlyPos: {
1156 int startYear = dStart.year(); 1169 int startYear = dStart.year();
1157 int startMonth = dStart.month(); // 1..12 1170 int startMonth = dStart.month(); // 1..12
1158 int earliestYear = earliestDate.year(); 1171 int earliestYear = earliestDate.year();
1159 int monthsAhead = (earliestYear - startYear)*12 + earliestDate.month() - startMonth; 1172 int monthsAhead = (earliestYear - startYear)*12 + earliestDate.month() - startMonth;
1160 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month 1173 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month
1161 monthsAhead -= notThisMonth; // latest month which recurred 1174 monthsAhead -= notThisMonth; // latest month which recurred
1162 // Check for the first later day in the current month 1175 // Check for the first later day in the current month
1163 if (!notThisMonth) 1176 if (!notThisMonth)
1164 nextDate = getFirstDateInMonth(earliestDate); 1177 nextDate = getFirstDateInMonth(earliestDate);
1165 if (!nextDate.isValid() && earliestDate.day() > 1) { 1178 if (!nextDate.isValid() && earliestDate.day() > 1) {
1166 // Check for a day in the next scheduled month 1179 // Check for a day in the next scheduled month
1167 int months = startMonth - 1 + monthsAhead + rFreq; 1180 int months = startMonth - 1 + monthsAhead + rFreq;
1168 nextDate = getFirstDateInMonth(QDate(startYear + months/12, months%12 + 1, 1)); 1181 nextDate = getFirstDateInMonth(QDate(startYear + months/12, months%12 + 1, 1));
1169 } 1182 }
1170 break; 1183 break;
1171 } 1184 }
1172 case rYearlyMonth: 1185 case rYearlyMonth:
1173 case rYearlyPos: 1186 case rYearlyPos:
1174 case rYearlyDay: { 1187 case rYearlyDay: {
1175 int startYear = dStart.year(); 1188 int startYear = dStart.year();
1176 int yearsAhead = earliestDate.year() - startYear; 1189 int yearsAhead = earliestDate.year() - startYear;
1177 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year 1190 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year
1178 yearsAhead -= notThisYear; // latest year which recurred 1191 yearsAhead -= notThisYear; // latest year which recurred
1179 // Check for the first later date in the current year 1192 // Check for the first later date in the current year
1180 if (!notThisYear) 1193 if (!notThisYear)
1181 nextDate = getFirstDateInYear(earliestDate); 1194 nextDate = getFirstDateInYear(earliestDate);
1182 // Check for a date in the next scheduled year 1195 // Check for a date in the next scheduled year
1183 if (!nextDate.isValid() && earliestDate.dayOfYear() > 1) 1196 if (!nextDate.isValid() && earliestDate.dayOfYear() > 1)
1184 nextDate = getFirstDateInYear(QDate(startYear + yearsAhead + rFreq, 1, 1)); 1197 nextDate = getFirstDateInYear(QDate(startYear + yearsAhead + rFreq, 1, 1));
1185 break; 1198 break;
1186 } 1199 }
1187 case rNone: 1200 case rNone:
1188 default: 1201 default:
1189 return QDate(); 1202 return QDate();
1190 } 1203 }
1191 1204
1192 if (rDuration >= 0 && nextDate.isValid()) { 1205 if (rDuration >= 0 && nextDate.isValid()) {
1193 // Check that the date found is within the range of the recurrence 1206 // Check that the date found is within the range of the recurrence
1194 QDate end = endDate(); 1207 QDate end = endDate();
1195 if (nextDate > end) 1208 if (nextDate > end)
1196 return QDate(); 1209 return QDate();
1197 if (last && nextDate == end) 1210 if (last && nextDate == end)
1198 *last = true; 1211 *last = true;
1199 } 1212 }
1200 return nextDate; 1213 return nextDate;
1201} 1214}
1202 1215
1203/* Get the date of the last previous recurrence, before the specified date. 1216/* Get the date of the last previous recurrence, before the specified date.
1204 * Reply = date of previous recurrence, or invalid date if none. 1217 * Reply = date of previous recurrence, or invalid date if none.
1205 */ 1218 */
1206QDate Recurrence::getPreviousDateNoTime(const QDate &afterDate, bool *last) const 1219QDate Recurrence::getPreviousDateNoTime(const QDate &afterDate, bool *last) const
1207{ 1220{
1208 if (last) 1221 if (last)
1209 *last = false; 1222 *last = false;
1210 QDate dStart = mRecurStart.date(); 1223 QDate dStart = mRecurStart.date();
1211 QDate latestDate = afterDate.addDays(-1); 1224 QDate latestDate = afterDate.addDays(-1);
1212 if (latestDate < dStart) 1225 if (latestDate < dStart)
1213 return QDate(); 1226 return QDate();
1214 QDate prevDate; 1227 QDate prevDate;
1215 1228
1216 switch (recurs) { 1229 switch (recurs) {
1217 case rDaily: 1230 case rDaily:
1218 prevDate = dStart.addDays((dStart.daysTo(latestDate) / rFreq) * rFreq); 1231 prevDate = dStart.addDays((dStart.daysTo(latestDate) / rFreq) * rFreq);
1219 break; 1232 break;
1220 1233
1221 case rWeekly: { 1234 case rWeekly: {
1222 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart 1235 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart
1223 int latestDayOfWeek = latestDate.dayOfWeek(); 1236 int latestDayOfWeek = latestDate.dayOfWeek();
1224 int weeksAhead = start.daysTo(latestDate) / 7; 1237 int weeksAhead = start.daysTo(latestDate) / 7;
1225 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week 1238 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week
1226 weeksAhead -= notThisWeek; // latest week which recurred 1239 weeksAhead -= notThisWeek; // latest week which recurred
1227 int weekday = 0; 1240 int weekday = 0;
1228 // First check for any previous day this week, if this week is a recurring week 1241 // First check for any previous day this week, if this week is a recurring week
1229 if (!notThisWeek) 1242 if (!notThisWeek)
1230 weekday = getLastDayInWeek(latestDayOfWeek); 1243 weekday = getLastDayInWeek(latestDayOfWeek);
1231 // Check for a day in the previous scheduled week 1244 // Check for a day in the previous scheduled week
1232 if (!weekday) { 1245 if (!weekday) {
1233 int weekEnd = (rWeekStart + 5)%7 + 1; 1246 int weekEnd = (rWeekStart + 5)%7 + 1;
1234 if (latestDayOfWeek < weekEnd) { 1247 if (latestDayOfWeek < weekEnd) {
1235 if (!notThisWeek) 1248 if (!notThisWeek)
1236 weeksAhead -= rFreq; 1249 weeksAhead -= rFreq;
1237 weekday = getLastDayInWeek(weekEnd); 1250 weekday = getLastDayInWeek(weekEnd);
1238 } 1251 }
1239 } 1252 }
1240 if (weekday) 1253 if (weekday)
1241 prevDate = start.addDays(weeksAhead*7 + weekday - 1); 1254 prevDate = start.addDays(weeksAhead*7 + weekday - 1);
1242 break; 1255 break;
1243 } 1256 }
1244 case rMonthlyDay: 1257 case rMonthlyDay:
1245 case rMonthlyPos: { 1258 case rMonthlyPos: {
1246 int startYear = dStart.year(); 1259 int startYear = dStart.year();
1247 int startMonth = dStart.month(); // 1..12 1260 int startMonth = dStart.month(); // 1..12
1248 int latestYear = latestDate.year(); 1261 int latestYear = latestDate.year();
1249 int monthsAhead = (latestYear - startYear)*12 + latestDate.month() - startMonth; 1262 int monthsAhead = (latestYear - startYear)*12 + latestDate.month() - startMonth;
1250 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month 1263 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month
1251 monthsAhead -= notThisMonth; // latest month which recurred 1264 monthsAhead -= notThisMonth; // latest month which recurred
1252 // Check for the last earlier day in the current month 1265 // Check for the last earlier day in the current month
1253 if (!notThisMonth) 1266 if (!notThisMonth)
1254 prevDate = getLastDateInMonth(latestDate); 1267 prevDate = getLastDateInMonth(latestDate);
1255 if (!prevDate.isValid() && latestDate.day() < latestDate.daysInMonth()) { 1268 if (!prevDate.isValid() && latestDate.day() < latestDate.daysInMonth()) {
1256 // Check for a day in the previous scheduled month 1269 // Check for a day in the previous scheduled month
1257 if (!notThisMonth) 1270 if (!notThisMonth)
1258 monthsAhead -= rFreq; 1271 monthsAhead -= rFreq;
1259 int months = startMonth + monthsAhead; // get the month after the one that recurs 1272 int months = startMonth + monthsAhead; // get the month after the one that recurs
1260 prevDate = getLastDateInMonth(QDate(startYear + months/12, months%12 + 1, 1).addDays(-1)); 1273 prevDate = getLastDateInMonth(QDate(startYear + months/12, months%12 + 1, 1).addDays(-1));
1261 } 1274 }
1262 break; 1275 break;
1263 } 1276 }
1264 case rYearlyMonth: 1277 case rYearlyMonth:
1265 case rYearlyPos: 1278 case rYearlyPos:
1266 case rYearlyDay: { 1279 case rYearlyDay: {
1267 int startYear = dStart.year(); 1280 int startYear = dStart.year();
1268 int yearsAhead = latestDate.year() - startYear; 1281 int yearsAhead = latestDate.year() - startYear;
1269 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year 1282 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year
1270 yearsAhead -= notThisYear; // latest year which recurred 1283 yearsAhead -= notThisYear; // latest year which recurred
1271 // Check for the first later date in the current year 1284 // Check for the first later date in the current year
1272 if (!notThisYear) 1285 if (!notThisYear)
1273 prevDate = getLastDateInYear(latestDate); 1286 prevDate = getLastDateInYear(latestDate);
1274 if (!prevDate.isValid() && latestDate.dayOfYear() < latestDate.daysInYear()) { 1287 if (!prevDate.isValid() && latestDate.dayOfYear() < latestDate.daysInYear()) {
1275 // Check for a date in the next scheduled year 1288 // Check for a date in the next scheduled year
1276 if (!notThisYear) 1289 if (!notThisYear)
1277 yearsAhead -= rFreq; 1290 yearsAhead -= rFreq;
1278 prevDate = getLastDateInYear(QDate(startYear + yearsAhead, 12, 31)); 1291 prevDate = getLastDateInYear(QDate(startYear + yearsAhead, 12, 31));
1279 } 1292 }
1280 break; 1293 break;
1281 } 1294 }
1282 case rNone: 1295 case rNone:
1283 default: 1296 default:
1284 return QDate(); 1297 return QDate();
1285 } 1298 }
1286 1299
1287 if (prevDate.isValid()) { 1300 if (prevDate.isValid()) {
1288 // Check that the date found is within the range of the recurrence 1301 // Check that the date found is within the range of the recurrence
1289 if (prevDate < dStart) 1302 if (prevDate < dStart)
1290 return QDate(); 1303 return QDate();
1291 if (rDuration >= 0) { 1304 if (rDuration >= 0) {
1292 QDate end = endDate(); 1305 QDate end = endDate();
1293 if (prevDate >= end) { 1306 if (prevDate >= end) {
1294 if (last) 1307 if (last)
1295 *last = true; 1308 *last = true;
1296 return end; 1309 return end;
1297 } 1310 }
1298 } 1311 }
1299 } 1312 }
1300 return prevDate; 1313 return prevDate;
1301} 1314}
1302 1315
1303void Recurrence::setDailySub(short type, int freq, int duration) 1316void Recurrence::setDailySub(short type, int freq, int duration)
1304{ 1317{
1305 recurs = type; 1318 recurs = type;
1306 rFreq = freq; 1319 rFreq = freq;
1307 rDuration = duration; 1320 rDuration = duration;
1308 rMonthPositions.clear(); 1321 rMonthPositions.clear();
1309 rMonthDays.clear(); 1322 rMonthDays.clear();
1310 rYearNums.clear(); 1323 rYearNums.clear();
1311 if (type != rDaily) 1324 if (type != rDaily)
1312 mFloats = false; // sub-daily types can't be floating 1325 mFloats = false; // sub-daily types can't be floating
1313 1326
1314 if (mParent) mParent->updated(); 1327 if (mParent) mParent->updated();
1315} 1328}
1316 1329
1317void Recurrence::setYearly_(short type, Feb29Type feb29type, int freq, int duration) 1330void Recurrence::setYearly_(short type, Feb29Type feb29type, int freq, int duration)
1318{ 1331{
1319 recurs = type; 1332 recurs = type;
1320 if (mCompatVersion < 310 && type == rYearlyDay) { 1333 if (mCompatVersion < 310 && type == rYearlyDay) {
1321 mCompatRecurs = rYearlyDay; 1334 mCompatRecurs = rYearlyDay;
1322 recurs = rYearlyMonth; // convert old yearly-by-day to yearly-by-month 1335 recurs = rYearlyMonth; // convert old yearly-by-day to yearly-by-month
1323 feb29type = rMar1; // retain the same day number in the year 1336 feb29type = rMar1; // retain the same day number in the year
1324 } 1337 }
1325 1338
1326 mFeb29YearlyType = feb29type; 1339 mFeb29YearlyType = feb29type;
1327 rFreq = freq; 1340 rFreq = freq;
1328 rDuration = duration; 1341 rDuration = duration;
1329 if (type != rYearlyPos) 1342 if (type != rYearlyPos)
1330 rMonthPositions.clear(); 1343 rMonthPositions.clear();
1331 rMonthDays.clear(); 1344 rMonthDays.clear();
1332 if (mParent) mParent->updated(); 1345 if (mParent) mParent->updated();
1333} 1346}
1334 1347
1335int Recurrence::recurCalc(PeriodFunc func, QDateTime &endtime) const 1348int Recurrence::recurCalc(PeriodFunc func, QDateTime &endtime) const
1336{ 1349{
1337 QDate enddate = endtime.date(); 1350 QDate enddate = endtime.date();
1338 switch (func) { 1351 switch (func) {
1339 case END_DATE_AND_COUNT: 1352 case END_DATE_AND_COUNT:
1340 if (rDuration < 0) { 1353 if (rDuration < 0) {
1341 endtime = QDateTime(); 1354 endtime = QDateTime();
1342 return 0; // infinite recurrence 1355 return 0; // infinite recurrence
1343 } 1356 }
1344 if (rDuration == 0) { 1357 if (rDuration == 0) {
1345 endtime = rEndDateTime; 1358 endtime = rEndDateTime;
1346 func = COUNT_TO_DATE; 1359 func = COUNT_TO_DATE;
1347 } 1360 }
1348 break; 1361 break;
1349 case COUNT_TO_DATE: 1362 case COUNT_TO_DATE:
1350 // Count recurrences up to and including the specified date/time. 1363 // Count recurrences up to and including the specified date/time.
1351 if (endtime < mRecurStart) 1364 if (endtime < mRecurStart)
1352 return 0; 1365 return 0;
1353 if (rDuration == 0 && endtime > rEndDateTime) 1366 if (rDuration == 0 && endtime > rEndDateTime)
1354 enddate = rEndDateTime.date(); 1367 enddate = rEndDateTime.date();
1355 else if (!mFloats && mRecurStart.time() > endtime.time()) 1368 else if (!mFloats && mRecurStart.time() > endtime.time())
1356 enddate = enddate.addDays(-1); 1369 enddate = enddate.addDays(-1);
1357 break; 1370 break;
1358 case NEXT_AFTER_DATE: 1371 case NEXT_AFTER_DATE:
1359 // Find next recurrence AFTER endtime 1372 // Find next recurrence AFTER endtime
1360 if (endtime < mRecurStart) { 1373 if (endtime < mRecurStart) {
1361 endtime = mRecurStart; 1374 endtime = mRecurStart;
1362 return 1; 1375 return 1;
1363 } 1376 }
1364 if (rDuration == 0 && endtime >= rEndDateTime) { 1377 if (rDuration == 0 && endtime >= rEndDateTime) {
1365 endtime = QDateTime(); 1378 endtime = QDateTime();
1366 return 0; 1379 return 0;
1367 } 1380 }
1368 if (!mFloats && mRecurStart.time() > endtime.time()) 1381 if (!mFloats && mRecurStart.time() > endtime.time())
1369 enddate = enddate.addDays(-1); 1382 enddate = enddate.addDays(-1);
1370 break; 1383 break;
1371 default: 1384 default:
1372 endtime = QDateTime(); 1385 endtime = QDateTime();
1373 return 0; 1386 return 0;
1374 } 1387 }
1375 1388
1376 int count = 0; // default = error 1389 int count = 0; // default = error
1377 bool timed = false; 1390 bool timed = false;
1378 switch (recurs) { 1391 switch (recurs) {
1379 case rMinutely: 1392 case rMinutely:
1380 timed = true; 1393 timed = true;
1381 count = secondlyCalc(func, endtime, rFreq*60); 1394 count = secondlyCalc(func, endtime, rFreq*60);
1382 break; 1395 break;
1383 case rHourly: 1396 case rHourly:
1384 timed = true; 1397 timed = true;
1385 count = secondlyCalc(func, endtime, rFreq*3600); 1398 count = secondlyCalc(func, endtime, rFreq*3600);
1386 break; 1399 break;
1387 case rDaily: 1400 case rDaily:
1388 count = dailyCalc(func, enddate); 1401 count = dailyCalc(func, enddate);
1389 break; 1402 break;
1390 case rWeekly: 1403 case rWeekly:
1391 count = weeklyCalc(func, enddate); 1404 count = weeklyCalc(func, enddate);
1392 break; 1405 break;
1393 case rMonthlyPos: 1406 case rMonthlyPos:
1394 case rMonthlyDay: 1407 case rMonthlyDay:
1395 count = monthlyCalc(func, enddate); 1408 count = monthlyCalc(func, enddate);
1396 break; 1409 break;
1397 case rYearlyMonth: 1410 case rYearlyMonth:
1398 count = yearlyMonthCalc(func, enddate); 1411 count = yearlyMonthCalc(func, enddate);
1399 break; 1412 break;
1400 case rYearlyPos: 1413 case rYearlyPos:
1401 count = yearlyPosCalc(func, enddate); 1414 count = yearlyPosCalc(func, enddate);
1402 break; 1415 break;
1403 case rYearlyDay: 1416 case rYearlyDay:
1404 count = yearlyDayCalc(func, enddate); 1417 count = yearlyDayCalc(func, enddate);
1405 break; 1418 break;
1406 default: 1419 default:
1407 break; 1420 break;
1408 } 1421 }
1409 1422
1410 switch (func) { 1423 switch (func) {
1411 case END_DATE_AND_COUNT: 1424 case END_DATE_AND_COUNT:
1412 case NEXT_AFTER_DATE: 1425 case NEXT_AFTER_DATE:
1413 if (count == 0) 1426 if (count == 0)
1414 endtime = QDateTime(); 1427 endtime = QDateTime();
1415 else if (!timed) { 1428 else if (!timed) {
1416 endtime.setDate(enddate); 1429 endtime.setDate(enddate);
1417 endtime.setTime(mRecurStart.time()); 1430 endtime.setTime(mRecurStart.time());
1418 } 1431 }
1419 break; 1432 break;
1420 case COUNT_TO_DATE: 1433 case COUNT_TO_DATE:
1421 break; 1434 break;
1422 } 1435 }
1423 return count; 1436 return count;
1424} 1437}
1425 1438
1426int Recurrence::recurCalc(PeriodFunc func, QDate &enddate) const 1439int Recurrence::recurCalc(PeriodFunc func, QDate &enddate) const
1427{ 1440{
1428 QDateTime endtime(enddate, QTime(23,59,59)); 1441 QDateTime endtime(enddate, QTime(23,59,59));
1429 switch (func) { 1442 switch (func) {
1430 case END_DATE_AND_COUNT: 1443 case END_DATE_AND_COUNT:
1431 if (rDuration < 0) { 1444 if (rDuration < 0) {
1432 enddate = QDate(); 1445 enddate = QDate();
1433 return 0; // infinite recurrence 1446 return 0; // infinite recurrence
1434 } 1447 }
1435 if (rDuration == 0) { 1448 if (rDuration == 0) {
1436 enddate = rEndDateTime.date(); 1449 enddate = rEndDateTime.date();
1437 func = COUNT_TO_DATE; 1450 func = COUNT_TO_DATE;
1438 } 1451 }
1439 break; 1452 break;
1440 case COUNT_TO_DATE: 1453 case COUNT_TO_DATE:
1441 // Count recurrences up to and including the specified date. 1454 // Count recurrences up to and including the specified date.
1442 if (enddate < mRecurStart.date()) 1455 if (enddate < mRecurStart.date())
1443 return 0; 1456 return 0;
1444 if (rDuration == 0 && enddate > rEndDateTime.date()) { 1457 if (rDuration == 0 && enddate > rEndDateTime.date()) {
1445 enddate = rEndDateTime.date(); 1458 enddate = rEndDateTime.date();
1446 endtime.setDate(enddate); 1459 endtime.setDate(enddate);
1447 } 1460 }
1448 break; 1461 break;
1449 case NEXT_AFTER_DATE: 1462 case NEXT_AFTER_DATE:
1450 if (enddate < mRecurStart.date()) { 1463 if (enddate < mRecurStart.date()) {
1451 enddate = mRecurStart.date(); 1464 enddate = mRecurStart.date();
1452 return 1; 1465 return 1;
1453 } 1466 }
1454 if (rDuration == 0 && enddate >= rEndDateTime.date()) { 1467 if (rDuration == 0 && enddate >= rEndDateTime.date()) {
1455 enddate = QDate(); 1468 enddate = QDate();
1456 return 0; 1469 return 0;
1457 } 1470 }
1458 break; 1471 break;
1459 default: 1472 default:
1460 enddate = QDate(); 1473 enddate = QDate();
1461 return 0; 1474 return 0;
1462 } 1475 }
1463 1476
1464 int count = 0; // default = error 1477 int count = 0; // default = error
1465 bool timed = false; 1478 bool timed = false;
1466 switch (recurs) { 1479 switch (recurs) {
1467 case rMinutely: 1480 case rMinutely:
1468 timed = true; 1481 timed = true;
1469 count = secondlyCalc(func, endtime, rFreq*60); 1482 count = secondlyCalc(func, endtime, rFreq*60);
1470 break; 1483 break;
1471 case rHourly: 1484 case rHourly:
1472 timed = true; 1485 timed = true;
1473 count = secondlyCalc(func, endtime, rFreq*3600); 1486 count = secondlyCalc(func, endtime, rFreq*3600);
1474 break; 1487 break;
1475 case rDaily: 1488 case rDaily:
1476 count = dailyCalc(func, enddate); 1489 count = dailyCalc(func, enddate);
1477 break; 1490 break;
1478 case rWeekly: 1491 case rWeekly:
1479 count = weeklyCalc(func, enddate); 1492 count = weeklyCalc(func, enddate);
1480 break; 1493 break;
1481 case rMonthlyPos: 1494 case rMonthlyPos:
1482 case rMonthlyDay: 1495 case rMonthlyDay:
1483 count = monthlyCalc(func, enddate); 1496 count = monthlyCalc(func, enddate);
1484 break; 1497 break;
1485 case rYearlyMonth: 1498 case rYearlyMonth:
1486 count = yearlyMonthCalc(func, enddate); 1499 count = yearlyMonthCalc(func, enddate);
1487 break; 1500 break;
1488 case rYearlyPos: 1501 case rYearlyPos:
1489 count = yearlyPosCalc(func, enddate); 1502 count = yearlyPosCalc(func, enddate);
1490 break; 1503 break;
1491 case rYearlyDay: 1504 case rYearlyDay:
1492 count = yearlyDayCalc(func, enddate); 1505 count = yearlyDayCalc(func, enddate);
1493 break; 1506 break;
1494 default: 1507 default:
1495 break; 1508 break;
1496 } 1509 }
1497 1510
1498 switch (func) { 1511 switch (func) {
1499 case END_DATE_AND_COUNT: 1512 case END_DATE_AND_COUNT:
1500 case NEXT_AFTER_DATE: 1513 case NEXT_AFTER_DATE:
1501 if (count == 0) 1514 if (count == 0)
1502 endtime = QDate(); 1515 endtime = QDate();
1503 else if (timed) 1516 else if (timed)
1504 enddate = endtime.date(); 1517 enddate = endtime.date();
1505 break; 1518 break;
1506 case COUNT_TO_DATE: 1519 case COUNT_TO_DATE:
1507 break; 1520 break;
1508 } 1521 }
1509 return count; 1522 return count;
1510} 1523}
1511 1524
1512/* Find count and, depending on 'func', the end date/time of a secondly recurrence. 1525/* Find count and, depending on 'func', the end date/time of a secondly recurrence.
1513 * Reply = total number of occurrences up to 'endtime', or 0 if error. 1526 * Reply = total number of occurrences up to 'endtime', or 0 if error.
1514 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'endtime' is updated to the 1527 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'endtime' is updated to the
1515 * recurrence end date/time. 1528 * recurrence end date/time.
1516 */ 1529 */
1517int Recurrence::secondlyCalc(PeriodFunc func, QDateTime &endtime, int freq) const 1530int Recurrence::secondlyCalc(PeriodFunc func, QDateTime &endtime, int freq) const
1518{ 1531{
1519 switch (func) { 1532 switch (func) {
1520 case END_DATE_AND_COUNT: 1533 case END_DATE_AND_COUNT:
1521 endtime = mRecurStart.addSecs((rDuration + mRecurExDatesCount - 1) * freq); 1534 endtime = mRecurStart.addSecs((rDuration + mRecurExDatesCount - 1) * freq);
1522 return rDuration + mRecurExDatesCount; 1535 return rDuration + mRecurExDatesCount;
1523 case COUNT_TO_DATE: { 1536 case COUNT_TO_DATE: {
1524 int n = mRecurStart.secsTo(endtime)/freq + 1; 1537 int n = mRecurStart.secsTo(endtime)/freq + 1;
1525 if (rDuration > 0 && n > rDuration + mRecurExDatesCount) 1538 if (rDuration > 0 && n > rDuration + mRecurExDatesCount)
1526 return rDuration + mRecurExDatesCount; 1539 return rDuration + mRecurExDatesCount;
1527 return n; 1540 return n;
1528 } 1541 }
1529 case NEXT_AFTER_DATE: { 1542 case NEXT_AFTER_DATE: {
1530 int count = mRecurStart.secsTo(endtime) / freq + 2; 1543 int count = mRecurStart.secsTo(endtime) / freq + 2;
1531 if (rDuration > 0 && count > rDuration) 1544 if (rDuration > 0 && count > rDuration)
1532 return 0; 1545 return 0;
1533 endtime = mRecurStart.addSecs((count - 1)*freq); 1546 endtime = mRecurStart.addSecs((count - 1)*freq);
1534 return count; 1547 return count;
1535 } 1548 }
1536 } 1549 }
1537 return 0; 1550 return 0;
1538} 1551}
1539 1552
1540/* Find count and, depending on 'func', the end date of a daily recurrence. 1553/* Find count and, depending on 'func', the end date of a daily recurrence.
1541 * Reply = total number of occurrences up to 'enddate', or 0 if error. 1554 * Reply = total number of occurrences up to 'enddate', or 0 if error.
1542 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 1555 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
1543 * recurrence end date. 1556 * recurrence end date.
1544 */ 1557 */
1545int Recurrence::dailyCalc(PeriodFunc func, QDate &enddate) const 1558int Recurrence::dailyCalc(PeriodFunc func, QDate &enddate) const
1546{ 1559{
1547 QDate dStart = mRecurStart.date(); 1560 QDate dStart = mRecurStart.date();
1548 switch (func) { 1561 switch (func) {
1549 case END_DATE_AND_COUNT: 1562 case END_DATE_AND_COUNT:
1550 enddate = dStart.addDays((rDuration + mRecurExDatesCount - 1) * rFreq); 1563 enddate = dStart.addDays((rDuration + mRecurExDatesCount - 1) * rFreq);
1551 return rDuration + mRecurExDatesCount; 1564 return rDuration + mRecurExDatesCount;
1552 case COUNT_TO_DATE: { 1565 case COUNT_TO_DATE: {
1553 int n = dStart.daysTo(enddate)/rFreq + 1; 1566 int n = dStart.daysTo(enddate)/rFreq + 1;
1554 if (rDuration > 0 && n > rDuration + mRecurExDatesCount) 1567 if (rDuration > 0 && n > rDuration + mRecurExDatesCount)
1555 return rDuration + mRecurExDatesCount; 1568 return rDuration + mRecurExDatesCount;
1556 return n; 1569 return n;
1557 } 1570 }
1558 case NEXT_AFTER_DATE: { 1571 case NEXT_AFTER_DATE: {
1559 int count = dStart.daysTo(enddate) / rFreq + 2; 1572 int count = dStart.daysTo(enddate) / rFreq + 2;
1560 if (rDuration > 0 && count > rDuration) 1573 if (rDuration > 0 && count > rDuration)
1561 return 0; 1574 return 0;
1562 enddate = dStart.addDays((count - 1)*rFreq); 1575 enddate = dStart.addDays((count - 1)*rFreq);
1563 return count; 1576 return count;
1564 } 1577 }
1565 } 1578 }
1566 return 0; 1579 return 0;
1567} 1580}
1568 1581
1569/* Find count and, depending on 'func', the end date of a weekly recurrence. 1582/* Find count and, depending on 'func', the end date of a weekly recurrence.
1570 * Reply = total number of occurrences up to 'enddate', or 0 if error. 1583 * Reply = total number of occurrences up to 'enddate', or 0 if error.
1571 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 1584 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
1572 * recurrence end date. 1585 * recurrence end date.
1573 */ 1586 */
1574int Recurrence::weeklyCalc(PeriodFunc func, QDate &enddate) const 1587int Recurrence::weeklyCalc(PeriodFunc func, QDate &enddate) const
1575{ 1588{
1576 int daysPerWeek = 0; 1589 int daysPerWeek = 0;
1577 for (int i = 0; i < 7; ++i) { 1590 for (int i = 0; i < 7; ++i) {
1578 if (rDays.testBit((uint)i)) 1591 if (rDays.testBit((uint)i))
1579 ++daysPerWeek; 1592 ++daysPerWeek;
1580 } 1593 }
1581 if (!daysPerWeek) 1594 if (!daysPerWeek)
1582 return 0; // there are no days to recur on 1595 return 0; // there are no days to recur on
1583 1596
1584 switch (func) { 1597 switch (func) {
1585 case END_DATE_AND_COUNT: 1598 case END_DATE_AND_COUNT:
1586 return weeklyCalcEndDate(enddate, daysPerWeek); 1599 return weeklyCalcEndDate(enddate, daysPerWeek);
1587 case COUNT_TO_DATE: 1600 case COUNT_TO_DATE:
1588 return weeklyCalcToDate(enddate, daysPerWeek); 1601 return weeklyCalcToDate(enddate, daysPerWeek);
1589 case NEXT_AFTER_DATE: 1602 case NEXT_AFTER_DATE:
1590 return weeklyCalcNextAfter(enddate, daysPerWeek); 1603 return weeklyCalcNextAfter(enddate, daysPerWeek);
1591 } 1604 }
1592 return 0; 1605 return 0;
1593} 1606}
1594 1607
1595int Recurrence::weeklyCalcEndDate(QDate &enddate, int daysPerWeek) const 1608int Recurrence::weeklyCalcEndDate(QDate &enddate, int daysPerWeek) const
1596{ 1609{
1597 int startDayOfWeek = mRecurStart.date().dayOfWeek(); // 1..7 1610 int startDayOfWeek = mRecurStart.date().dayOfWeek(); // 1..7
1598 int countGone = 0; 1611 int countGone = 0;
1599 int daysGone = 0; 1612 int daysGone = 0;
1600 uint countTogo = rDuration + mRecurExDatesCount; 1613 uint countTogo = rDuration + mRecurExDatesCount;
1601 if (startDayOfWeek != rWeekStart) { 1614 if (startDayOfWeek != rWeekStart) {
1602 // Check what remains of the start week 1615 // Check what remains of the start week
1603 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { 1616 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
1604 ++daysGone; 1617 ++daysGone;
1605 if (rDays.testBit((uint)i)) { 1618 if (rDays.testBit((uint)i)) {
1606 ++countGone; 1619 ++countGone;
1607 if (--countTogo == 0) 1620 if (--countTogo == 0)
1608 break; 1621 break;
1609 } 1622 }
1610 } 1623 }
1611 daysGone += 7 * (rFreq - 1); 1624 daysGone += 7 * (rFreq - 1);
1612 } 1625 }
1613 if (countTogo) { 1626 if (countTogo) {
1614 // Skip the remaining whole weeks 1627 // Skip the remaining whole weeks
1615 // Leave at least 1 recurrence remaining, in order to get its date 1628 // Leave at least 1 recurrence remaining, in order to get its date
1616 int wholeWeeks = (countTogo - 1) / daysPerWeek; 1629 int wholeWeeks = (countTogo - 1) / daysPerWeek;
1617 daysGone += wholeWeeks * 7 * rFreq; 1630 daysGone += wholeWeeks * 7 * rFreq;
1618 countGone += wholeWeeks * daysPerWeek; 1631 countGone += wholeWeeks * daysPerWeek;
1619 countTogo -= wholeWeeks * daysPerWeek; 1632 countTogo -= wholeWeeks * daysPerWeek;
1620 // Check the last week in the recurrence 1633 // Check the last week in the recurrence
1621 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { 1634 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
1622 ++daysGone; 1635 ++daysGone;
1623 if (rDays.testBit((uint)i)) { 1636 if (rDays.testBit((uint)i)) {
1624 ++countGone; 1637 ++countGone;
1625 if (--countTogo == 0) 1638 if (--countTogo == 0)
1626 break; 1639 break;
1627 } 1640 }
1628 } 1641 }
1629 } 1642 }
1630 enddate = mRecurStart.date().addDays(daysGone); 1643 enddate = mRecurStart.date().addDays(daysGone);
1631 return countGone; 1644 return countGone;
1632} 1645}
1633 1646
1634int Recurrence::weeklyCalcToDate(const QDate &enddate, int daysPerWeek) const 1647int Recurrence::weeklyCalcToDate(const QDate &enddate, int daysPerWeek) const
1635{ 1648{
1636 QDate dStart = mRecurStart.date(); 1649 QDate dStart = mRecurStart.date();
1637 int startDayOfWeek = dStart.dayOfWeek(); // 1..7 1650 int startDayOfWeek = dStart.dayOfWeek(); // 1..7
1638 int countGone = 0; 1651 int countGone = 0;
1639 int daysGone = 0; 1652 int daysGone = 0;
1640 int totalDays = dStart.daysTo(enddate) + 1; 1653 int totalDays = dStart.daysTo(enddate) + 1;
1641 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 1654 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
1642 1655
1643 if (startDayOfWeek != rWeekStart) { 1656 if (startDayOfWeek != rWeekStart) {
1644 // Check what remains of the start week 1657 // Check what remains of the start week
1645 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { 1658 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
1646 if (rDays.testBit((uint)i)) { 1659 if (rDays.testBit((uint)i)) {
1647 if (++countGone >= countMax) 1660 if (++countGone >= countMax)
1648 return countMax; 1661 return countMax;
1649 } 1662 }
1650 if (++daysGone == totalDays) 1663 if (++daysGone == totalDays)
1651 return countGone; 1664 return countGone;
1652 } 1665 }
1653 daysGone += 7 * (rFreq - 1); 1666 daysGone += 7 * (rFreq - 1);
1654 if (daysGone >= totalDays) 1667 if (daysGone >= totalDays)
1655 return countGone; 1668 return countGone;
1656 } 1669 }
1657 // Skip the remaining whole weeks 1670 // Skip the remaining whole weeks
1658 int wholeWeeks = (totalDays - daysGone) / 7; 1671 int wholeWeeks = (totalDays - daysGone) / 7;
1659 countGone += (wholeWeeks / rFreq) * daysPerWeek; 1672 countGone += (wholeWeeks / rFreq) * daysPerWeek;
1660 if (countGone >= countMax) 1673 if (countGone >= countMax)
1661 return countMax; 1674 return countMax;
1662 daysGone += wholeWeeks * 7; 1675 daysGone += wholeWeeks * 7;
1663 if (daysGone >= totalDays // have we reached the end date? 1676 if (daysGone >= totalDays // have we reached the end date?
1664 || wholeWeeks % rFreq) // is end week a recurrence week? 1677 || wholeWeeks % rFreq) // is end week a recurrence week?
1665 return countGone; 1678 return countGone;
1666 1679
1667 // Check the last week in the recurrence 1680 // Check the last week in the recurrence
1668 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { 1681 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
1669 if (rDays.testBit((uint)i)) { 1682 if (rDays.testBit((uint)i)) {
1670 if (++countGone >= countMax) 1683 if (++countGone >= countMax)
1671 return countMax; 1684 return countMax;
1672 } 1685 }
1673 if (++daysGone == totalDays) 1686 if (++daysGone == totalDays)
1674 return countGone; 1687 return countGone;
1675 } 1688 }
1676 return countGone; 1689 return countGone;
1677} 1690}
1678 1691
1679int Recurrence::weeklyCalcNextAfter(QDate &enddate, int daysPerWeek) const 1692int Recurrence::weeklyCalcNextAfter(QDate &enddate, int daysPerWeek) const
1680{ 1693{
1681 QDate dStart = mRecurStart.date(); 1694 QDate dStart = mRecurStart.date();
1682 int startDayOfWeek = dStart.dayOfWeek(); // 1..7 1695 int startDayOfWeek = dStart.dayOfWeek(); // 1..7
1683 int totalDays = dStart.daysTo(enddate) + 1; 1696 int totalDays = dStart.daysTo(enddate) + 1;
1684 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 1697 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
1685 int countGone = 0; 1698 int countGone = 0;
1686 int daysGone = 0; 1699 int daysGone = 0;
1687 int recurWeeks; 1700 int recurWeeks;
1688 1701
1689 if (startDayOfWeek != rWeekStart) { 1702 if (startDayOfWeek != rWeekStart) {
1690 // Check what remains of the start week 1703 // Check what remains of the start week
1691 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { 1704 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
1692 ++daysGone; 1705 ++daysGone;
1693 if (rDays.testBit((uint)i)) { 1706 if (rDays.testBit((uint)i)) {
1694 ++countGone; 1707 ++countGone;
1695 if (daysGone > totalDays) 1708 if (daysGone > totalDays)
1696 goto ex; 1709 goto ex;
1697 if (--countTogo == 0) 1710 if (--countTogo == 0)
1698 return 0; 1711 return 0;
1699 } 1712 }
1700 } 1713 }
1701 daysGone += 7 * (rFreq - 1); 1714 daysGone += 7 * (rFreq - 1);
1702 } 1715 }
1703 1716
1704 // Skip the remaining whole weeks 1717 // Skip the remaining whole weeks
1705 recurWeeks = (totalDays - daysGone) / (7 * rFreq); 1718 recurWeeks = (totalDays - daysGone) / (7 * rFreq);
1706 if (recurWeeks) { 1719 if (recurWeeks) {
1707 int n = recurWeeks * daysPerWeek; 1720 int n = recurWeeks * daysPerWeek;
1708 if (static_cast<uint>(n) > countTogo) 1721 if (static_cast<uint>(n) > countTogo)
1709 return 0; // reached end of recurrence 1722 return 0; // reached end of recurrence
1710 countGone += n; 1723 countGone += n;
1711 countTogo -= n; 1724 countTogo -= n;
1712 daysGone += recurWeeks * 7 * rFreq; 1725 daysGone += recurWeeks * 7 * rFreq;
1713 } 1726 }
1714 1727
1715 // Check the last week or two in the recurrence 1728 // Check the last week or two in the recurrence
1716 for ( ; ; ) { 1729 for ( ; ; ) {
1717 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { 1730 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
1718 ++daysGone; 1731 ++daysGone;
1719 if (rDays.testBit((uint)i)) { 1732 if (rDays.testBit((uint)i)) {
1720 ++countGone; 1733 ++countGone;
1721 if (daysGone > totalDays) 1734 if (daysGone > totalDays)
1722 goto ex; 1735 goto ex;
1723 if (--countTogo == 0) 1736 if (--countTogo == 0)
1724 return 0; 1737 return 0;
1725 } 1738 }
1726 } 1739 }
1727 daysGone += 7 * (rFreq - 1); 1740 daysGone += 7 * (rFreq - 1);
1728 } 1741 }
1729ex: 1742ex:
1730 enddate = dStart.addDays(daysGone); 1743 enddate = dStart.addDays(daysGone);
1731 return countGone; 1744 return countGone;
1732} 1745}
1733 1746
1734/* Find count and, depending on 'func', the end date of a monthly recurrence. 1747/* Find count and, depending on 'func', the end date of a monthly recurrence.
1735 * Reply = total number of occurrences up to 'enddate', or 0 if error. 1748 * Reply = total number of occurrences up to 'enddate', or 0 if error.
1736 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 1749 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
1737 * recurrence end date. 1750 * recurrence end date.
1738 */ 1751 */
1739struct Recurrence::MonthlyData { 1752struct Recurrence::MonthlyData {
1740 const Recurrence *recurrence; 1753 const Recurrence *recurrence;
1741 int year; // current year 1754 int year; // current year
1742 int month; // current month 0..11 1755 int month; // current month 0..11
1743 int day; // current day of month 1..31 1756 int day; // current day of month 1..31
1744 bool varies; // true if recurring days vary between different months 1757 bool varies; // true if recurring days vary between different months
1745 private: 1758 private:
1746 QValueList<int> days28, days29, days30, days31; // recurring days in months of each length 1759 QValueList<int> days28, days29, days30, days31; // recurring days in months of each length
1747 QValueList<int> *recurDays[4]; 1760 QValueList<int> *recurDays[4];
1748 public: 1761 public:
1749 MonthlyData(const Recurrence* r, const QDate &date) 1762 MonthlyData(const Recurrence* r, const QDate &date)
1750 : recurrence(r), year(date.year()), month(date.month()-1), day(date.day()) 1763 : recurrence(r), year(date.year()), month(date.month()-1), day(date.day())
1751 { recurDays[0] = &days28; 1764 { recurDays[0] = &days28;
1752 recurDays[1] = &days29; 1765 recurDays[1] = &days29;
1753 recurDays[2] = &days30; 1766 recurDays[2] = &days30;
1754 recurDays[3] = &days31; 1767 recurDays[3] = &days31;
1755 varies = (recurrence->recurs == rMonthlyPos) 1768 varies = (recurrence->recurs == rMonthlyPos)
1756 ? true : recurrence->getMonthlyDayDays(days31, 31); 1769 ? true : recurrence->getMonthlyDayDays(days31, 31);
1757 } 1770 }
1758 const QValueList<int>* dayList() const { 1771 const QValueList<int>* dayList() const {
1759 if (!varies) 1772 if (!varies)
1760 return &days31; 1773 return &days31;
1761 QDate startOfMonth(year, month + 1, 1); 1774 QDate startOfMonth(year, month + 1, 1);
1762 int daysInMonth = startOfMonth.daysInMonth(); 1775 int daysInMonth = startOfMonth.daysInMonth();
1763 QValueList<int>* days = recurDays[daysInMonth - 28]; 1776 QValueList<int>* days = recurDays[daysInMonth - 28];
1764 if (recurrence->recurs == rMonthlyPos) 1777 if (recurrence->recurs == rMonthlyPos)
1765 recurrence->getMonthlyPosDays(*days, daysInMonth, startOfMonth.dayOfWeek()); 1778 recurrence->getMonthlyPosDays(*days, daysInMonth, startOfMonth.dayOfWeek());
1766 else if (days->isEmpty()) 1779 else if (days->isEmpty())
1767 recurrence->getMonthlyDayDays(*days, daysInMonth); 1780 recurrence->getMonthlyDayDays(*days, daysInMonth);
1768 return days; 1781 return days;
1769 } 1782 }
1770 int yearMonth() const { return year*12 + month; } 1783 int yearMonth() const { return year*12 + month; }
1771 void addMonths(int diff) { month += diff; year += month / 12; month %= 12; } 1784 void addMonths(int diff) { month += diff; year += month / 12; month %= 12; }
1772 QDate date() const { return QDate(year, month + 1, day); } 1785 QDate date() const { return QDate(year, month + 1, day); }
1773}; 1786};
1774 1787
1775int Recurrence::monthlyCalc(PeriodFunc func, QDate &enddate) const 1788int Recurrence::monthlyCalc(PeriodFunc func, QDate &enddate) const
1776{ 1789{
1777 if (recurs == rMonthlyPos && rMonthPositions.isEmpty() 1790 if (recurs == rMonthlyPos && rMonthPositions.isEmpty()
1778 || recurs == rMonthlyDay && rMonthDays.isEmpty()) 1791 || recurs == rMonthlyDay && rMonthDays.isEmpty())
1779 return 0; 1792 return 0;
1780 1793
1781 MonthlyData data(this, mRecurStart.date()); 1794 MonthlyData data(this, mRecurStart.date());
1782 switch (func) { 1795 switch (func) {
1783 case END_DATE_AND_COUNT: 1796 case END_DATE_AND_COUNT:
1784 return monthlyCalcEndDate(enddate, data); 1797 return monthlyCalcEndDate(enddate, data);
1785 case COUNT_TO_DATE: 1798 case COUNT_TO_DATE:
1786 return monthlyCalcToDate(enddate, data); 1799 return monthlyCalcToDate(enddate, data);
1787 case NEXT_AFTER_DATE: 1800 case NEXT_AFTER_DATE:
1788 return monthlyCalcNextAfter(enddate, data); 1801 return monthlyCalcNextAfter(enddate, data);
1789 } 1802 }
1790 return 0; 1803 return 0;
1791} 1804}
1792 1805
1793int Recurrence::monthlyCalcEndDate(QDate &enddate, MonthlyData &data) const 1806int Recurrence::monthlyCalcEndDate(QDate &enddate, MonthlyData &data) const
1794{ 1807{
1795 uint countTogo = rDuration + mRecurExDatesCount; 1808 uint countTogo = rDuration + mRecurExDatesCount;
1796 int countGone = 0; 1809 int countGone = 0;
1797 QValueList<int>::ConstIterator it; 1810 QValueList<int>::ConstIterator it;
1798 const QValueList<int>* days = data.dayList(); 1811 const QValueList<int>* days = data.dayList();
1799 1812
1800 if (data.day > 1) { 1813 if (data.day > 1) {
1801 // Check what remains of the start month 1814 // Check what remains of the start month
1802 for (it = days->begin(); it != days->end(); ++it) { 1815 for (it = days->begin(); it != days->end(); ++it) {
1803 if (*it >= data.day) { 1816 if (*it >= data.day) {
1804 ++countGone; 1817 ++countGone;
1805 if (--countTogo == 0) { 1818 if (--countTogo == 0) {
1806 data.day = *it; 1819 data.day = *it;
1807 break; 1820 break;
1808 } 1821 }
1809 } 1822 }
1810 } 1823 }
1811 if (countTogo) { 1824 if (countTogo) {
1812 data.day = 1; 1825 data.day = 1;
1813 data.addMonths(rFreq); 1826 data.addMonths(rFreq);
1814 } 1827 }
1815 } 1828 }
1816 if (countTogo) { 1829 if (countTogo) {
1817 if (data.varies) { 1830 if (data.varies) {
1818 // The number of recurrence days varies from month to month, 1831 // The number of recurrence days varies from month to month,
1819 // so we need to check month by month. 1832 // so we need to check month by month.
1820 for ( ; ; ) { 1833 for ( ; ; ) {
1821 days = data.dayList(); 1834 days = data.dayList();
1822 uint n = days->count(); // number of recurrence days in this month 1835 uint n = days->count(); // number of recurrence days in this month
1823 if (n >= countTogo) 1836 if (n >= countTogo)
1824 break; 1837 break;
1825 countTogo -= n; 1838 countTogo -= n;
1826 countGone += n; 1839 countGone += n;
1827 data.addMonths(rFreq); 1840 data.addMonths(rFreq);
1828 } 1841 }
1829 } else { 1842 } else {
1830 // The number of recurrences is the same every month, 1843 // The number of recurrences is the same every month,
1831 // so skip the month-by-month check. 1844 // so skip the month-by-month check.
1832 // Skip the remaining whole months, but leave at least 1845 // Skip the remaining whole months, but leave at least
1833 // 1 recurrence remaining, in order to get its date. 1846 // 1 recurrence remaining, in order to get its date.
1834 int daysPerMonth = days->count(); 1847 int daysPerMonth = days->count();
1835 int wholeMonths = (countTogo - 1) / daysPerMonth; 1848 int wholeMonths = (countTogo - 1) / daysPerMonth;
1836 data.addMonths(wholeMonths * rFreq); 1849 data.addMonths(wholeMonths * rFreq);
1837 countGone += wholeMonths * daysPerMonth; 1850 countGone += wholeMonths * daysPerMonth;
1838 countTogo -= wholeMonths * daysPerMonth; 1851 countTogo -= wholeMonths * daysPerMonth;
1839 } 1852 }
1840 if (countTogo) { 1853 if (countTogo) {
1841 // Check the last month in the recurrence 1854 // Check the last month in the recurrence
1842 for (it = days->begin(); it != days->end(); ++it) { 1855 for (it = days->begin(); it != days->end(); ++it) {
1843 ++countGone; 1856 ++countGone;
1844 if (--countTogo == 0) { 1857 if (--countTogo == 0) {
1845 data.day = *it; 1858 data.day = *it;
1846 break; 1859 break;
1847 } 1860 }
1848 } 1861 }
1849 } 1862 }
1850 } 1863 }
1851 enddate = data.date(); 1864 enddate = data.date();
1852 return countGone; 1865 return countGone;
1853} 1866}
1854 1867
1855int Recurrence::monthlyCalcToDate(const QDate &enddate, MonthlyData &data) const 1868int Recurrence::monthlyCalcToDate(const QDate &enddate, MonthlyData &data) const
1856{ 1869{
1857 int countGone = 0; 1870 int countGone = 0;
1858 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 1871 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
1859 int endYear = enddate.year(); 1872 int endYear = enddate.year();
1860 int endMonth = enddate.month() - 1; // zero-based 1873 int endMonth = enddate.month() - 1; // zero-based
1861 int endDay = enddate.day(); 1874 int endDay = enddate.day();
1862 int endYearMonth = endYear*12 + endMonth; 1875 int endYearMonth = endYear*12 + endMonth;
1863 QValueList<int>::ConstIterator it; 1876 QValueList<int>::ConstIterator it;
1864 const QValueList<int>* days = data.dayList(); 1877 const QValueList<int>* days = data.dayList();
1865 1878
1866 if (data.day > 1) { 1879 if (data.day > 1) {
1867 // Check what remains of the start month 1880 // Check what remains of the start month
1868 for (it = days->begin(); it != days->end(); ++it) { 1881 for (it = days->begin(); it != days->end(); ++it) {
1869 if (*it >= data.day) { 1882 if (*it >= data.day) {
1870 if (data.yearMonth() == endYearMonth && *it > endDay) 1883 if (data.yearMonth() == endYearMonth && *it > endDay)
1871 return countGone; 1884 return countGone;
1872 if (++countGone >= countMax) 1885 if (++countGone >= countMax)
1873 return countMax; 1886 return countMax;
1874 } 1887 }
1875 } 1888 }
1876 data.day = 1; 1889 data.day = 1;
1877 data.addMonths(rFreq); 1890 data.addMonths(rFreq);
1878 } 1891 }
1879 1892
1880 if (data.varies) { 1893 if (data.varies) {
1881 // The number of recurrence days varies from month to month, 1894 // The number of recurrence days varies from month to month,
1882 // so we need to check month by month. 1895 // so we need to check month by month.
1883 while (data.yearMonth() < endYearMonth) { 1896 while (data.yearMonth() < endYearMonth) {
1884 countGone += data.dayList()->count(); 1897 countGone += data.dayList()->count();
1885 if (countGone >= countMax) 1898 if (countGone >= countMax)
1886 return countMax; 1899 return countMax;
1887 data.addMonths(rFreq); 1900 data.addMonths(rFreq);
1888 } 1901 }
1889 days = data.dayList(); 1902 days = data.dayList();
1890 } else { 1903 } else {
1891 // The number of recurrences is the same every month, 1904 // The number of recurrences is the same every month,
1892 // so skip the month-by-month check. 1905 // so skip the month-by-month check.
1893 // Skip the remaining whole months. 1906 // Skip the remaining whole months.
1894 int daysPerMonth = days->count(); 1907 int daysPerMonth = days->count();
1895 int wholeMonths = endYearMonth - data.yearMonth(); 1908 int wholeMonths = endYearMonth - data.yearMonth();
1896 countGone += (wholeMonths / rFreq) * daysPerMonth; 1909 countGone += (wholeMonths / rFreq) * daysPerMonth;
1897 if (countGone >= countMax) 1910 if (countGone >= countMax)
1898 return countMax; 1911 return countMax;
1899 if (wholeMonths % rFreq) 1912 if (wholeMonths % rFreq)
1900 return countGone; // end year isn't a recurrence year 1913 return countGone; // end year isn't a recurrence year
1901 data.year = endYear; 1914 data.year = endYear;
1902 data.month = endMonth; 1915 data.month = endMonth;
1903 } 1916 }
1904 1917
1905 // Check the last month in the recurrence 1918 // Check the last month in the recurrence
1906 for (it = days->begin(); it != days->end(); ++it) { 1919 for (it = days->begin(); it != days->end(); ++it) {
1907 if (*it > endDay) 1920 if (*it > endDay)
1908 return countGone; 1921 return countGone;
1909 if (++countGone >= countMax) 1922 if (++countGone >= countMax)
1910 return countMax; 1923 return countMax;
1911 } 1924 }
1912 return countGone; 1925 return countGone;
1913} 1926}
1914 1927
1915int Recurrence::monthlyCalcNextAfter(QDate &enddate, MonthlyData &data) const 1928int Recurrence::monthlyCalcNextAfter(QDate &enddate, MonthlyData &data) const
1916{ 1929{
1917 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 1930 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
1918 int countGone = 0; 1931 int countGone = 0;
1919 int endYear = enddate.year(); 1932 int endYear = enddate.year();
1920 int endDay = enddate.day(); 1933 int endDay = enddate.day();
1921 int endYearMonth = endYear*12 + enddate.month() - 1; 1934 int endYearMonth = endYear*12 + enddate.month() - 1;
1922 QValueList<int>::ConstIterator it; 1935 QValueList<int>::ConstIterator it;
1923 const QValueList<int>* days = data.dayList(); 1936 const QValueList<int>* days = data.dayList();
1924 1937
1925 if (data.day > 1) { 1938 if (data.day > 1) {
1926 // Check what remains of the start month 1939 // Check what remains of the start month
1927 for (it = days->begin(); it != days->end(); ++it) { 1940 for (it = days->begin(); it != days->end(); ++it) {
1928 if (*it >= data.day) { 1941 if (*it >= data.day) {
1929 ++countGone; 1942 ++countGone;
1930 if (data.yearMonth() == endYearMonth && *it > endDay) { 1943 if (data.yearMonth() == endYearMonth && *it > endDay) {
1931 data.day = *it; 1944 data.day = *it;
1932 goto ex; 1945 goto ex;
1933 } 1946 }
1934 if (--countTogo == 0) 1947 if (--countTogo == 0)
1935 return 0; 1948 return 0;
1936 } 1949 }
1937 } 1950 }
1938 data.day = 1; 1951 data.day = 1;
1939 data.addMonths(rFreq); 1952 data.addMonths(rFreq);
1940 } 1953 }
1941 1954
1942 if (data.varies) { 1955 if (data.varies) {
1943 // The number of recurrence days varies from month to month, 1956 // The number of recurrence days varies from month to month,
1944 // so we need to check month by month. 1957 // so we need to check month by month.
1945 while (data.yearMonth() <= endYearMonth) { 1958 while (data.yearMonth() <= endYearMonth) {
1946 days = data.dayList(); 1959 days = data.dayList();
1947 uint n = days->count(); // number of recurrence days in this month 1960 uint n = days->count(); // number of recurrence days in this month
1948 if (data.yearMonth() == endYearMonth && days->last() > endDay) 1961 if (data.yearMonth() == endYearMonth && days->last() > endDay)
1949 break; 1962 break;
1950 if (n >= countTogo) 1963 if (n >= countTogo)
1951 return 0; 1964 return 0;
1952 countGone += n; 1965 countGone += n;
1953 countTogo -= n; 1966 countTogo -= n;
1954 data.addMonths(rFreq); 1967 data.addMonths(rFreq);
1955 } 1968 }
1956 days = data.dayList(); 1969 days = data.dayList();
1957 } else { 1970 } else {
1958 // The number of recurrences is the same every month, 1971 // The number of recurrences is the same every month,
1959 // so skip the month-by-month check. 1972 // so skip the month-by-month check.
1960 // Skip the remaining whole months to at least end year/month. 1973 // Skip the remaining whole months to at least end year/month.
1961 int daysPerMonth = days->count(); 1974 int daysPerMonth = days->count();
1962 int elapsed = endYearMonth - data.yearMonth(); 1975 int elapsed = endYearMonth - data.yearMonth();
1963 int recurMonths = (elapsed + rFreq - 1) / rFreq; 1976 int recurMonths = (elapsed + rFreq - 1) / rFreq;
1964 if (elapsed % rFreq == 0 && days->last() <= endDay) 1977 if (elapsed % rFreq == 0 && days->last() <= endDay)
1965 ++recurMonths; // required month is after endYearMonth 1978 ++recurMonths; // required month is after endYearMonth
1966 if (recurMonths) { 1979 if (recurMonths) {
1967 int n = recurMonths * daysPerMonth; 1980 int n = recurMonths * daysPerMonth;
1968 if (static_cast<uint>(n) > countTogo) 1981 if (static_cast<uint>(n) > countTogo)
1969 return 0; // reached end of recurrence 1982 return 0; // reached end of recurrence
1970 countTogo -= n; 1983 countTogo -= n;
1971 countGone += n; 1984 countGone += n;
1972 data.addMonths(recurMonths * rFreq); 1985 data.addMonths(recurMonths * rFreq);
1973 } 1986 }
1974 } 1987 }
1975 1988
1976 // Check the last month in the recurrence 1989 // Check the last month in the recurrence
1977 for (it = days->begin(); it != days->end(); ++it) { 1990 for (it = days->begin(); it != days->end(); ++it) {
1978 ++countGone; 1991 ++countGone;
1979 if (data.yearMonth() > endYearMonth || *it > endDay) { 1992 if (data.yearMonth() > endYearMonth || *it > endDay) {
1980 data.day = *it; 1993 data.day = *it;
1981 break; 1994 break;
1982 } 1995 }
1983 if (--countTogo == 0) 1996 if (--countTogo == 0)
1984 return 0; 1997 return 0;
1985 } 1998 }
1986ex: 1999ex:
1987 enddate = data.date(); 2000 enddate = data.date();
1988 return countGone; 2001 return countGone;
1989} 2002}
1990 2003
1991 2004
1992/* Find count and, depending on 'func', the end date of an annual recurrence by date. 2005/* Find count and, depending on 'func', the end date of an annual recurrence by date.
1993 * Reply = total number of occurrences up to 'enddate', or 0 if error. 2006 * Reply = total number of occurrences up to 'enddate', or 0 if error.
1994 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 2007 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
1995 * recurrence end date. 2008 * recurrence end date.
1996 */ 2009 */
1997struct Recurrence::YearlyMonthData { 2010struct Recurrence::YearlyMonthData {
1998 const Recurrence *recurrence; 2011 const Recurrence *recurrence;
1999 int year; // current year 2012 int year; // current year
2000 int month; // current month 1..12 2013 int month; // current month 1..12
2001 int day; // current day of month 1..31 2014 int day; // current day of month 1..31
2002 bool leapyear; // true if February 29th recurs and current year is a leap year 2015 bool leapyear; // true if February 29th recurs and current year is a leap year
2003 bool feb29; // true if February 29th recurs 2016 bool feb29; // true if February 29th recurs
2004 private: 2017 private:
2005 QValueList<int> months; // recurring months in non-leap years 1..12 2018 QValueList<int> months; // recurring months in non-leap years 1..12
2006 QValueList<int> leapMonths; // recurring months in leap years 1..12 2019 QValueList<int> leapMonths; // recurring months in leap years 1..12
2007 public: 2020 public:
2008 YearlyMonthData(const Recurrence* r, const QDate &date) 2021 YearlyMonthData(const Recurrence* r, const QDate &date)
2009 : recurrence(r), year(date.year()), month(date.month()), day(date.day()) 2022 : recurrence(r), year(date.year()), month(date.month()), day(date.day())
2010 { feb29 = recurrence->getYearlyMonthMonths(day, months, leapMonths); 2023 { feb29 = recurrence->getYearlyMonthMonths(day, months, leapMonths);
2011 leapyear = feb29 && QDate::leapYear(year); 2024 leapyear = feb29 && QDate::leapYear(year);
2012 } 2025 }
2013 const QValueList<int>* monthList() const 2026 const QValueList<int>* monthList() const
2014 { return leapyear ? &leapMonths : &months; } 2027 { return leapyear ? &leapMonths : &months; }
2015 const QValueList<int>* leapMonthList() const { return &leapMonths; } 2028 const QValueList<int>* leapMonthList() const { return &leapMonths; }
2016 QDate date() const { return QDate(year, month, day); } 2029 QDate date() const { return QDate(year, month, day); }
2017}; 2030};
2018 2031
2019int Recurrence::yearlyMonthCalc(PeriodFunc func, QDate &enddate) const 2032int Recurrence::yearlyMonthCalc(PeriodFunc func, QDate &enddate) const
2020{ 2033{
2021 if (rYearNums.isEmpty()) 2034 if (rYearNums.isEmpty())
2022 return 0; 2035 return 0;
2023 YearlyMonthData data(this, mRecurStart.date()); 2036 YearlyMonthData data(this, mRecurStart.date());
2024 switch (func) { 2037 switch (func) {
2025 case END_DATE_AND_COUNT: 2038 case END_DATE_AND_COUNT:
2026 return yearlyMonthCalcEndDate(enddate, data); 2039 return yearlyMonthCalcEndDate(enddate, data);
2027 case COUNT_TO_DATE: 2040 case COUNT_TO_DATE:
2028 return yearlyMonthCalcToDate(enddate, data); 2041 return yearlyMonthCalcToDate(enddate, data);
2029 case NEXT_AFTER_DATE: 2042 case NEXT_AFTER_DATE:
2030 return yearlyMonthCalcNextAfter(enddate, data); 2043 return yearlyMonthCalcNextAfter(enddate, data);
2031 } 2044 }
2032 return 0; 2045 return 0;
2033} 2046}
2034 2047
2035// Find total count and end date of an annual recurrence by date. 2048// Find total count and end date of an annual recurrence by date.
2036// Reply = total number of occurrences. 2049// Reply = total number of occurrences.
2037int Recurrence::yearlyMonthCalcEndDate(QDate &enddate, YearlyMonthData &data) const 2050int Recurrence::yearlyMonthCalcEndDate(QDate &enddate, YearlyMonthData &data) const
2038{ 2051{
2039 uint countTogo = rDuration + mRecurExDatesCount; 2052 uint countTogo = rDuration + mRecurExDatesCount;
2040 int countGone = 0; 2053 int countGone = 0;
2041 QValueList<int>::ConstIterator it; 2054 QValueList<int>::ConstIterator it;
2042 const QValueList<int>* mons = data.monthList(); // get recurring months for this year 2055 const QValueList<int>* mons = data.monthList(); // get recurring months for this year
2043 2056
2044 if (data.month > 1) { 2057 if (data.month > 1) {
2045 // Check what remains of the start year 2058 // Check what remains of the start year
2046 for (it = mons->begin(); it != mons->end(); ++it) { 2059 for (it = mons->begin(); it != mons->end(); ++it) {
2047 if (*it >= data.month) { 2060 if (*it >= data.month) {
2048 ++countGone; 2061 ++countGone;
2049 if (--countTogo == 0) { 2062 if (--countTogo == 0) {
2050 data.month = *it; 2063 data.month = *it;
2051 if (data.month == 2 && data.feb29 && !data.leapyear) { 2064 if (data.month == 2 && data.feb29 && !data.leapyear) {
2052 // The recurrence should end on February 29th, but it's a non-leap year 2065 // The recurrence should end on February 29th, but it's a non-leap year
2053 switch (mFeb29YearlyType) { 2066 switch (mFeb29YearlyType) {
2054 case rFeb28: 2067 case rFeb28:
2055 data.day = 28; 2068 data.day = 28;
2056 break; 2069 break;
2057 case rMar1: 2070 case rMar1:
2058 data.month = 3; 2071 data.month = 3;
2059 data.day = 1; 2072 data.day = 1;
2060 break; 2073 break;
2061 case rFeb29: 2074 case rFeb29:
2062 break; 2075 break;
2063 } 2076 }
2064 } 2077 }
2065 break; 2078 break;
2066 } 2079 }
2067 } 2080 }
2068 } 2081 }
2069 if (countTogo) { 2082 if (countTogo) {
2070 data.month = 1; 2083 data.month = 1;
2071 data.year += rFreq; 2084 data.year += rFreq;
2072 } 2085 }
2073 } 2086 }
2074 if (countTogo) { 2087 if (countTogo) {
2075 if (data.feb29 && mFeb29YearlyType == rFeb29) { 2088 if (data.feb29 && mFeb29YearlyType == rFeb29) {
2076 // The number of recurrences is different on leap years, 2089 // The number of recurrences is different on leap years,
2077 // so check year-by-year. 2090 // so check year-by-year.
2078 for ( ; ; ) { 2091 for ( ; ; ) {
2079 mons = data.monthList(); 2092 mons = data.monthList();
2080 uint n = mons->count(); 2093 uint n = mons->count();
2081 if (n >= countTogo) 2094 if (n >= countTogo)
2082 break; 2095 break;
2083 countTogo -= n; 2096 countTogo -= n;
2084 countGone += n; 2097 countGone += n;
2085 data.year += rFreq; 2098 data.year += rFreq;
2086 } 2099 }
2087 } else { 2100 } else {
2088 // The number of recurrences is the same every year, 2101 // The number of recurrences is the same every year,
2089 // so skip the year-by-year check. 2102 // so skip the year-by-year check.
2090 // Skip the remaining whole years, but leave at least 2103 // Skip the remaining whole years, but leave at least
2091 // 1 recurrence remaining, in order to get its date. 2104 // 1 recurrence remaining, in order to get its date.
2092 int monthsPerYear = mons->count(); 2105 int monthsPerYear = mons->count();
2093 int wholeYears = (countTogo - 1) / monthsPerYear; 2106 int wholeYears = (countTogo - 1) / monthsPerYear;
2094 data.year += wholeYears * rFreq; 2107 data.year += wholeYears * rFreq;
2095 countGone += wholeYears * monthsPerYear; 2108 countGone += wholeYears * monthsPerYear;
2096 countTogo -= wholeYears * monthsPerYear; 2109 countTogo -= wholeYears * monthsPerYear;
2097 } 2110 }
2098 if (countTogo) { 2111 if (countTogo) {
2099 // Check the last year in the recurrence 2112 // Check the last year in the recurrence
2100 for (it = mons->begin(); it != mons->end(); ++it) { 2113 for (it = mons->begin(); it != mons->end(); ++it) {
2101 ++countGone; 2114 ++countGone;
2102 if (--countTogo == 0) { 2115 if (--countTogo == 0) {
2103 data.month = *it; 2116 data.month = *it;
2104 if (data.month == 2 && data.feb29 && !QDate::leapYear(data.year)) { 2117 if (data.month == 2 && data.feb29 && !QDate::leapYear(data.year)) {
2105 // The recurrence should end on February 29th, but it's a non-leap year 2118 // The recurrence should end on February 29th, but it's a non-leap year
2106 switch (mFeb29YearlyType) { 2119 switch (mFeb29YearlyType) {
2107 case rFeb28: 2120 case rFeb28:
2108 data.day = 28; 2121 data.day = 28;
2109 break; 2122 break;
2110 case rMar1: 2123 case rMar1:
2111 data.month = 3; 2124 data.month = 3;
2112 data.day = 1; 2125 data.day = 1;
2113 break; 2126 break;
2114 case rFeb29: 2127 case rFeb29:
2115 break; 2128 break;
2116 } 2129 }
2117 } 2130 }
2118 break; 2131 break;
2119 } 2132 }
2120 } 2133 }
2121 } 2134 }
2122 } 2135 }
2123 enddate = data.date(); 2136 enddate = data.date();
2124 return countGone; 2137 return countGone;
2125} 2138}
2126 2139
2127// Find count of an annual recurrence by date. 2140// Find count of an annual recurrence by date.
2128// Reply = total number of occurrences up to 'enddate'. 2141// Reply = total number of occurrences up to 'enddate'.
2129int Recurrence::yearlyMonthCalcToDate(const QDate &enddate, YearlyMonthData &data) const 2142int Recurrence::yearlyMonthCalcToDate(const QDate &enddate, YearlyMonthData &data) const
2130{ 2143{
2131 int countGone = 0; 2144 int countGone = 0;
2132 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 2145 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
2133 int endYear = enddate.year(); 2146 int endYear = enddate.year();
2134 int endMonth = enddate.month(); 2147 int endMonth = enddate.month();
2135 int endDay = enddate.day(); 2148 int endDay = enddate.day();
2136 if (endDay < data.day) { 2149 if (endDay < data.day) {
2137 /* The end day of the month is earlier than the recurrence day of the month. 2150 /* The end day of the month is earlier than the recurrence day of the month.
2138 * If Feb 29th recurs and: 2151 * If Feb 29th recurs and:
2139 * 1) it recurs on Feb 28th in non-leap years, don't adjust the end month 2152 * 1) it recurs on Feb 28th in non-leap years, don't adjust the end month
2140 * if enddate is Feb 28th on a non-leap year. 2153 * if enddate is Feb 28th on a non-leap year.
2141 * 2) it recurs on Mar 1st in non-leap years, allow the end month to be 2154 * 2) it recurs on Mar 1st in non-leap years, allow the end month to be
2142 * adjusted to February, to simplify calculations. 2155 * adjusted to February, to simplify calculations.
2143 */ 2156 */
2144 if (data.feb29 && !QDate::leapYear(endYear) 2157 if (data.feb29 && !QDate::leapYear(endYear)
2145 && mFeb29YearlyType == rFeb28 && endDay == 28 && endMonth == 2) { 2158 && mFeb29YearlyType == rFeb28 && endDay == 28 && endMonth == 2) {
2146 } 2159 }
2147 else if (--endMonth == 0) { 2160 else if (--endMonth == 0) {
2148 endMonth = 12; 2161 endMonth = 12;
2149 --endYear; 2162 --endYear;
2150 } 2163 }
2151 } 2164 }
2152 QValueList<int>::ConstIterator it; 2165 QValueList<int>::ConstIterator it;
2153 const QValueList<int>* mons = data.monthList(); 2166 const QValueList<int>* mons = data.monthList();
2154 2167
2155 if (data.month > 1) { 2168 if (data.month > 1) {
2156 // Check what remains of the start year 2169 // Check what remains of the start year
2157 for (it = mons->begin(); it != mons->end(); ++it) { 2170 for (it = mons->begin(); it != mons->end(); ++it) {
2158 if (*it >= data.month) { 2171 if (*it >= data.month) {
2159 if (data.year == endYear && *it > endMonth) 2172 if (data.year == endYear && *it > endMonth)
2160 return countGone; 2173 return countGone;
2161 if (++countGone >= countMax) 2174 if (++countGone >= countMax)
2162 return countMax; 2175 return countMax;
2163 } 2176 }
2164 } 2177 }
2165 data.month = 1; 2178 data.month = 1;
2166 data.year += rFreq; 2179 data.year += rFreq;
2167 } 2180 }
2168 if (data.feb29 && mFeb29YearlyType == rFeb29) { 2181 if (data.feb29 && mFeb29YearlyType == rFeb29) {
2169 // The number of recurrences is different on leap years, 2182 // The number of recurrences is different on leap years,
2170 // so check year-by-year. 2183 // so check year-by-year.
2171 while (data.year < endYear) { 2184 while (data.year < endYear) {
2172 countGone += data.monthList()->count(); 2185 countGone += data.monthList()->count();
2173 if (countGone >= countMax) 2186 if (countGone >= countMax)
2174 return countMax; 2187 return countMax;
2175 data.year += rFreq; 2188 data.year += rFreq;
2176 } 2189 }
2177 mons = data.monthList(); 2190 mons = data.monthList();
2178 } else { 2191 } else {
2179 // The number of recurrences is the same every year, 2192 // The number of recurrences is the same every year,
2180 // so skip the year-by-year check. 2193 // so skip the year-by-year check.
2181 // Skip the remaining whole years. 2194 // Skip the remaining whole years.
2182 int monthsPerYear = mons->count(); 2195 int monthsPerYear = mons->count();
2183 int wholeYears = endYear - data.year; 2196 int wholeYears = endYear - data.year;
2184 countGone += (wholeYears / rFreq) * monthsPerYear; 2197 countGone += (wholeYears / rFreq) * monthsPerYear;
2185 if (countGone >= countMax) 2198 if (countGone >= countMax)
2186 return countMax; 2199 return countMax;
2187 if (wholeYears % rFreq) 2200 if (wholeYears % rFreq)
2188 return countGone; // end year isn't a recurrence year 2201 return countGone; // end year isn't a recurrence year
2189 data.year = endYear; 2202 data.year = endYear;
2190 } 2203 }
2191 2204
2192 // Check the last year in the recurrence 2205 // Check the last year in the recurrence
2193 for (it = mons->begin(); it != mons->end(); ++it) { 2206 for (it = mons->begin(); it != mons->end(); ++it) {
2194 if (*it > endMonth) 2207 if (*it > endMonth)
2195 return countGone; 2208 return countGone;
2196 if (++countGone >= countMax) 2209 if (++countGone >= countMax)
2197 return countMax; 2210 return countMax;
2198 } 2211 }
2199 return countGone; 2212 return countGone;
2200} 2213}
2201 2214
2202// Find count and date of first recurrence after 'enddate' of an annual recurrence by date. 2215// Find count and date of first recurrence after 'enddate' of an annual recurrence by date.
2203// Reply = total number of occurrences up to 'enddate'. 2216// Reply = total number of occurrences up to 'enddate'.
2204int Recurrence::yearlyMonthCalcNextAfter(QDate &enddate, YearlyMonthData &data) const 2217int Recurrence::yearlyMonthCalcNextAfter(QDate &enddate, YearlyMonthData &data) const
2205{ 2218{
2206 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 2219 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
2207 int countGone = 0; 2220 int countGone = 0;
2208 int endYear = enddate.year(); 2221 int endYear = enddate.year();
2209 int endMonth = enddate.month(); 2222 int endMonth = enddate.month();
2210 int endDay = enddate.day(); 2223 int endDay = enddate.day();
2211 bool mar1TooEarly = false; 2224 bool mar1TooEarly = false;
2212 bool feb28ok = false; 2225 bool feb28ok = false;
2213 if (endDay < data.day) { 2226 if (endDay < data.day) {
2214 if (data.feb29 && mFeb29YearlyType == rMar1 && endMonth == 3) 2227 if (data.feb29 && mFeb29YearlyType == rMar1 && endMonth == 3)
2215 mar1TooEarly = true; 2228 mar1TooEarly = true;
2216 if (data.feb29 && mFeb29YearlyType == rFeb28 && endMonth == 2 && endDay == 28) 2229 if (data.feb29 && mFeb29YearlyType == rFeb28 && endMonth == 2 && endDay == 28)
2217 feb28ok = true; 2230 feb28ok = true;
2218 else if (--endMonth == 0) { 2231 else if (--endMonth == 0) {
2219 endMonth = 12; 2232 endMonth = 12;
2220 --endYear; 2233 --endYear;
2221 } 2234 }
2222 } 2235 }
2223 QValueList<int>::ConstIterator it; 2236 QValueList<int>::ConstIterator it;
2224 const QValueList<int>* mons = data.monthList(); 2237 const QValueList<int>* mons = data.monthList();
2225 2238
2226 if (data.month > 1) { 2239 if (data.month > 1) {
2227 // Check what remains of the start year 2240 // Check what remains of the start year
2228 for (it = mons->begin(); it != mons->end(); ++it) { 2241 for (it = mons->begin(); it != mons->end(); ++it) {
2229 if (*it >= data.month) { 2242 if (*it >= data.month) {
2230 ++countGone; 2243 ++countGone;
2231 if (data.year == endYear 2244 if (data.year == endYear
2232 && ( *it > endMonth && (*it > 3 || !mar1TooEarly) 2245 && ( *it > endMonth && (*it > 3 || !mar1TooEarly)
2233 || *it == 2 && feb28ok && data.leapyear)) { 2246 || *it == 2 && feb28ok && data.leapyear)) {
2234 if (*it == 2 && data.feb29 && !data.leapyear) { 2247 if (*it == 2 && data.feb29 && !data.leapyear) {
2235 // The next recurrence should be on February 29th, but it's a non-leap year 2248 // The next recurrence should be on February 29th, but it's a non-leap year
2236 switch (mFeb29YearlyType) { 2249 switch (mFeb29YearlyType) {
2237 case rFeb28: 2250 case rFeb28:
2238 data.month = 2; 2251 data.month = 2;
2239 data.day = 28; 2252 data.day = 28;
2240 break; 2253 break;
2241 case rMar1: 2254 case rMar1:
2242 data.month = 3; 2255 data.month = 3;
2243 data.day = 1; 2256 data.day = 1;
2244 break; 2257 break;
2245 case rFeb29: // impossible in this context! 2258 case rFeb29: // impossible in this context!
2246 break; 2259 break;
2247 } 2260 }
2248 } 2261 }
2249 else 2262 else
2250 data.month = *it; 2263 data.month = *it;
2251 goto ex; 2264 goto ex;
2252 } 2265 }
2253 if (--countTogo == 0) 2266 if (--countTogo == 0)
2254 return 0; 2267 return 0;
2255 } 2268 }
2256 } 2269 }
2257 data.month = 1; 2270 data.month = 1;
2258 data.year += rFreq; 2271 data.year += rFreq;
2259 } 2272 }
2260 2273
2261 if (data.feb29 && mFeb29YearlyType == rFeb29) { 2274 if (data.feb29 && mFeb29YearlyType == rFeb29) {
2262 // The number of recurrences is different on leap years, 2275 // The number of recurrences is different on leap years,
2263 // so check year-by-year. 2276 // so check year-by-year.
2264 while (data.year <= endYear) { 2277 while (data.year <= endYear) {
2265 mons = data.monthList(); 2278 mons = data.monthList();
2266 if (data.year == endYear && mons->last() > endMonth) 2279 if (data.year == endYear && mons->last() > endMonth)
2267 break; 2280 break;
2268 uint n = mons->count(); 2281 uint n = mons->count();
2269 if (n >= countTogo) 2282 if (n >= countTogo)
2270 break; 2283 break;
2271 countTogo -= n; 2284 countTogo -= n;
2272 countGone += n; 2285 countGone += n;
2273 data.year += rFreq; 2286 data.year += rFreq;
2274 } 2287 }
2275 mons = data.monthList(); 2288 mons = data.monthList();
2276 } else { 2289 } else {
2277 // The number of recurrences is the same every year, 2290 // The number of recurrences is the same every year,
2278 // so skip the year-by-year check. 2291 // so skip the year-by-year check.
2279 // Skip the remaining whole years to at least endYear. 2292 // Skip the remaining whole years to at least endYear.
2280 int monthsPerYear = mons->count(); 2293 int monthsPerYear = mons->count();
2281 int recurYears = (endYear - data.year + rFreq - 1) / rFreq; 2294 int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
2282 if ((endYear - data.year)%rFreq == 0 2295 if ((endYear - data.year)%rFreq == 0
2283 && mons->last() <= endMonth) 2296 && mons->last() <= endMonth)
2284 ++recurYears; // required year is after endYear 2297 ++recurYears; // required year is after endYear
2285 if (recurYears) { 2298 if (recurYears) {
2286 int n = recurYears * monthsPerYear; 2299 int n = recurYears * monthsPerYear;
2287 if (static_cast<uint>(n) > countTogo) 2300 if (static_cast<uint>(n) > countTogo)
2288 return 0; // reached end of recurrence 2301 return 0; // reached end of recurrence
2289 countTogo -= n; 2302 countTogo -= n;
2290 countGone += n; 2303 countGone += n;
2291 data.year += recurYears * rFreq; 2304 data.year += recurYears * rFreq;
2292 } 2305 }
2293 } 2306 }
2294 2307
2295 // Check the last year in the recurrence 2308 // Check the last year in the recurrence
2296 for (it = mons->begin(); it != mons->end(); ++it) { 2309 for (it = mons->begin(); it != mons->end(); ++it) {
2297 ++countGone; 2310 ++countGone;
2298 if (data.year > endYear 2311 if (data.year > endYear
2299 || ( *it > endMonth && (*it > 3 || !mar1TooEarly) 2312 || ( *it > endMonth && (*it > 3 || !mar1TooEarly)
2300 || *it == 2 && feb28ok && QDate::leapYear(data.year))) { 2313 || *it == 2 && feb28ok && QDate::leapYear(data.year))) {
2301 if (*it == 2 && data.feb29 && !QDate::leapYear(data.year)) { 2314 if (*it == 2 && data.feb29 && !QDate::leapYear(data.year)) {
2302 // The next recurrence should be on February 29th, but it's a non-leap year 2315 // The next recurrence should be on February 29th, but it's a non-leap year
2303 switch (mFeb29YearlyType) { 2316 switch (mFeb29YearlyType) {
2304 case rFeb28: 2317 case rFeb28:
2305 data.month = 2; 2318 data.month = 2;
2306 data.day = 28; 2319 data.day = 28;
2307 break; 2320 break;
2308 case rMar1: 2321 case rMar1:
2309 data.month = 3; 2322 data.month = 3;
2310 data.day = 1; 2323 data.day = 1;
2311 break; 2324 break;
2312 case rFeb29: // impossible in this context! 2325 case rFeb29: // impossible in this context!
2313 break; 2326 break;
2314 } 2327 }
2315 } 2328 }
2316 else 2329 else
2317 data.month = *it; 2330 data.month = *it;
2318 break; 2331 break;
2319 } 2332 }
2320 if (--countTogo == 0) 2333 if (--countTogo == 0)
2321 return 0; 2334 return 0;
2322 } 2335 }
2323ex: 2336ex:
2324 enddate = data.date(); 2337 enddate = data.date();
2325 return countGone; 2338 return countGone;
2326} 2339}
2327 2340
2328 2341
2329/* Find count and, depending on 'func', the end date of an annual recurrence by date. 2342/* Find count and, depending on 'func', the end date of an annual recurrence by date.
2330 * Reply = total number of occurrences up to 'enddate', or 0 if error. 2343 * Reply = total number of occurrences up to 'enddate', or 0 if error.
2331 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 2344 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
2332 * recurrence end date. 2345 * recurrence end date.
2333 */ 2346 */
2334struct Recurrence::YearlyPosData { 2347struct Recurrence::YearlyPosData {
2335 const Recurrence *recurrence; 2348 const Recurrence *recurrence;
2336 int year; // current year 2349 int year; // current year
2337 int month; // current month 1..12 2350 int month; // current month 1..12
2338 int day; // current day of month 1..31 2351 int day; // current day of month 1..31
2339 int daysPerMonth; // number of days which recur each month, or -1 if variable 2352 int daysPerMonth; // number of days which recur each month, or -1 if variable
2340 int count; // number of days which recur each year, or -1 if variable 2353 int count; // number of days which recur each year, or -1 if variable
2341 bool varies; // true if number of days varies from year to year 2354 bool varies; // true if number of days varies from year to year
2342 private: 2355 private:
2343 mutable QValueList<int> days; 2356 mutable QValueList<int> days;
2344 public: 2357 public:
2345 YearlyPosData(const Recurrence* r, const QDate &date) 2358 YearlyPosData(const Recurrence* r, const QDate &date)
2346 : recurrence(r), year(date.year()), month(date.month()), day(date.day()), count(-1) 2359 : recurrence(r), year(date.year()), month(date.month()), day(date.day()), count(-1)
2347 { if ((daysPerMonth = r->countMonthlyPosDays()) > 0) 2360 { if ((daysPerMonth = r->countMonthlyPosDays()) > 0)
2348 count = daysPerMonth * r->rYearNums.count(); 2361 count = daysPerMonth * r->rYearNums.count();
2349 varies = (daysPerMonth < 0); 2362 varies = (daysPerMonth < 0);
2350 } 2363 }
2351 const QValueList<int>* dayList() const { 2364 const QValueList<int>* dayList() const {
2352 QDate startOfMonth(year, month, 1); 2365 QDate startOfMonth(year, month, 1);
2353 recurrence->getMonthlyPosDays(days, startOfMonth.daysInMonth(), startOfMonth.dayOfWeek()); 2366 recurrence->getMonthlyPosDays(days, startOfMonth.daysInMonth(), startOfMonth.dayOfWeek());
2354 return &days; 2367 return &days;
2355 } 2368 }
2356 int yearMonth() const { return year*12 + month - 1; } 2369 int yearMonth() const { return year*12 + month - 1; }
2357 void addMonths(int diff) { month += diff - 1; year += month / 12; month = month % 12 + 1; } 2370 void addMonths(int diff) { month += diff - 1; year += month / 12; month = month % 12 + 1; }
2358 QDate date() const { return QDate(year, month, day); } 2371 QDate date() const { return QDate(year, month, day); }
2359}; 2372};
2360 2373
2361int Recurrence::yearlyPosCalc(PeriodFunc func, QDate &enddate) const 2374int Recurrence::yearlyPosCalc(PeriodFunc func, QDate &enddate) const
2362{ 2375{
2363 if (rYearNums.isEmpty() || rMonthPositions.isEmpty()) 2376 if (rYearNums.isEmpty() || rMonthPositions.isEmpty())
2364 return 0; 2377 return 0;
2365 YearlyPosData data(this, mRecurStart.date()); 2378 YearlyPosData data(this, mRecurStart.date());
2366 switch (func) { 2379 switch (func) {
2367 case END_DATE_AND_COUNT: 2380 case END_DATE_AND_COUNT:
2368 return yearlyPosCalcEndDate(enddate, data); 2381 return yearlyPosCalcEndDate(enddate, data);
2369 case COUNT_TO_DATE: 2382 case COUNT_TO_DATE:
2370 return yearlyPosCalcToDate(enddate, data); 2383 return yearlyPosCalcToDate(enddate, data);
2371 case NEXT_AFTER_DATE: 2384 case NEXT_AFTER_DATE:
2372 return yearlyPosCalcNextAfter(enddate, data); 2385 return yearlyPosCalcNextAfter(enddate, data);
2373 } 2386 }
2374 return 0; 2387 return 0;
2375} 2388}
2376 2389
2377int Recurrence::yearlyPosCalcEndDate(QDate &enddate, YearlyPosData &data) const 2390int Recurrence::yearlyPosCalcEndDate(QDate &enddate, YearlyPosData &data) const
2378{ 2391{
2379 uint countTogo = rDuration + mRecurExDatesCount; 2392 uint countTogo = rDuration + mRecurExDatesCount;
2380 int countGone = 0; 2393 int countGone = 0;
2381 QValueList<int>::ConstIterator id; 2394 QValueList<int>::ConstIterator id;
2382 const QValueList<int>* days; 2395 const QValueList<int>* days;
2383 2396
2384 if (data.month > 1 || data.day > 1) { 2397 if (data.month > 1 || data.day > 1) {
2385 // Check what remains of the start year 2398 // Check what remains of the start year
2386 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2399 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2387 if (*im.current() >= data.month) { 2400 if (*im.current() >= data.month) {
2388 // Check what remains of the start month 2401 // Check what remains of the start month
2389 if (data.day > 1 || data.varies 2402 if (data.day > 1 || data.varies
2390 || static_cast<uint>(data.daysPerMonth) >= countTogo) { 2403 || static_cast<uint>(data.daysPerMonth) >= countTogo) {
2391 data.month = *im.current(); 2404 data.month = *im.current();
2392 days = data.dayList(); 2405 days = data.dayList();
2393 for (id = days->begin(); id != days->end(); ++id) { 2406 for (id = days->begin(); id != days->end(); ++id) {
2394 if (*id >= data.day) { 2407 if (*id >= data.day) {
2395 ++countGone; 2408 ++countGone;
2396 if (--countTogo == 0) { 2409 if (--countTogo == 0) {
2397 data.month = *im.current(); 2410 data.month = *im.current();
2398 data.day = *id; 2411 data.day = *id;
2399 goto ex; 2412 goto ex;
2400 } 2413 }
2401 } 2414 }
2402 } 2415 }
2403 data.day = 1; 2416 data.day = 1;
2404 } else { 2417 } else {
2405 // The number of days per month is constant, so skip 2418 // The number of days per month is constant, so skip
2406 // the whole month. 2419 // the whole month.
2407 countTogo -= data.daysPerMonth; 2420 countTogo -= data.daysPerMonth;
2408 countGone += data.daysPerMonth; 2421 countGone += data.daysPerMonth;
2409 } 2422 }
2410 } 2423 }
2411 } 2424 }
2412 data.month = 1; 2425 data.month = 1;
2413 data.year += rFreq; 2426 data.year += rFreq;
2414 } 2427 }
2415 2428
2416 if (data.varies) { 2429 if (data.varies) {
2417 // The number of recurrences varies from year to year. 2430 // The number of recurrences varies from year to year.
2418 for ( ; ; ) { 2431 for ( ; ; ) {
2419 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2432 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2420 data.month = *im.current(); 2433 data.month = *im.current();
2421 days = data.dayList(); 2434 days = data.dayList();
2422 int n = days->count(); 2435 int n = days->count();
2423 if (static_cast<uint>(n) >= countTogo) { 2436 if (static_cast<uint>(n) >= countTogo) {
2424 // Check the last month in the recurrence 2437 // Check the last month in the recurrence
2425 for (id = days->begin(); id != days->end(); ++id) { 2438 for (id = days->begin(); id != days->end(); ++id) {
2426 ++countGone; 2439 ++countGone;
2427 if (--countTogo == 0) { 2440 if (--countTogo == 0) {
2428 data.day = *id; 2441 data.day = *id;
2429 goto ex; 2442 goto ex;
2430 } 2443 }
2431 } 2444 }
2432 } 2445 }
2433 countTogo -= n; 2446 countTogo -= n;
2434 countGone += n; 2447 countGone += n;
2435 } 2448 }
2436 data.year += rFreq; 2449 data.year += rFreq;
2437 } 2450 }
2438 } else { 2451 } else {
2439 // The number of recurrences is the same every year, 2452 // The number of recurrences is the same every year,
2440 // so skip the year-by-year check. 2453 // so skip the year-by-year check.
2441 // Skip the remaining whole years, but leave at least 2454 // Skip the remaining whole years, but leave at least
2442 // 1 recurrence remaining, in order to get its date. 2455 // 1 recurrence remaining, in order to get its date.
2443 int wholeYears = (countTogo - 1) / data.count; 2456 int wholeYears = (countTogo - 1) / data.count;
2444 data.year += wholeYears * rFreq; 2457 data.year += wholeYears * rFreq;
2445 countGone += wholeYears * data.count; 2458 countGone += wholeYears * data.count;
2446 countTogo -= wholeYears * data.count; 2459 countTogo -= wholeYears * data.count;
2447 2460
2448 // Check the last year in the recurrence. 2461 // Check the last year in the recurrence.
2449 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2462 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2450 if (static_cast<uint>(data.daysPerMonth) >= countTogo) { 2463 if (static_cast<uint>(data.daysPerMonth) >= countTogo) {
2451 // Check the last month in the recurrence 2464 // Check the last month in the recurrence
2452 data.month = *im.current(); 2465 data.month = *im.current();
2453 days = data.dayList(); 2466 days = data.dayList();
2454 for (id = days->begin(); id != days->end(); ++id) { 2467 for (id = days->begin(); id != days->end(); ++id) {
2455 ++countGone; 2468 ++countGone;
2456 if (--countTogo == 0) { 2469 if (--countTogo == 0) {
2457 data.day = *id; 2470 data.day = *id;
2458 goto ex; 2471 goto ex;
2459 } 2472 }
2460 } 2473 }
2461 } 2474 }
2462 countTogo -= data.daysPerMonth; 2475 countTogo -= data.daysPerMonth;
2463 countGone += data.daysPerMonth; 2476 countGone += data.daysPerMonth;
2464 } 2477 }
2465 data.year += rFreq; 2478 data.year += rFreq;
2466 } 2479 }
2467ex: 2480ex:
2468 enddate = data.date(); 2481 enddate = data.date();
2469 return countGone; 2482 return countGone;
2470} 2483}
2471 2484
2472int Recurrence::yearlyPosCalcToDate(const QDate &enddate, YearlyPosData &data) const 2485int Recurrence::yearlyPosCalcToDate(const QDate &enddate, YearlyPosData &data) const
2473{ 2486{
2474 int countGone = 0; 2487 int countGone = 0;
2475 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 2488 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
2476 int endYear = enddate.year(); 2489 int endYear = enddate.year();
2477 int endMonth = enddate.month(); 2490 int endMonth = enddate.month();
2478 int endDay = enddate.day(); 2491 int endDay = enddate.day();
2479 if (endDay < data.day && --endMonth == 0) { 2492 if (endDay < data.day && --endMonth == 0) {
2480 endMonth = 12; 2493 endMonth = 12;
2481 --endYear; 2494 --endYear;
2482 } 2495 }
2483 int endYearMonth = endYear*12 + endMonth; 2496 int endYearMonth = endYear*12 + endMonth;
2484 QValueList<int>::ConstIterator id; 2497 QValueList<int>::ConstIterator id;
2485 const QValueList<int>* days; 2498 const QValueList<int>* days;
2486 2499
2487 if (data.month > 1 || data.day > 1) { 2500 if (data.month > 1 || data.day > 1) {
2488 // Check what remains of the start year 2501 // Check what remains of the start year
2489 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2502 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2490 if (*im.current() >= data.month) { 2503 if (*im.current() >= data.month) {
2491 data.month = *im.current(); 2504 data.month = *im.current();
2492 if (data.yearMonth() > endYearMonth) 2505 if (data.yearMonth() > endYearMonth)
2493 return countGone; 2506 return countGone;
2494 // Check what remains of the start month 2507 // Check what remains of the start month
2495 bool lastMonth = (data.yearMonth() == endYearMonth); 2508 bool lastMonth = (data.yearMonth() == endYearMonth);
2496 if (lastMonth || data.day > 1 || data.varies) { 2509 if (lastMonth || data.day > 1 || data.varies) {
2497 days = data.dayList(); 2510 days = data.dayList();
2498 if (lastMonth || data.day > 1) { 2511 if (lastMonth || data.day > 1) {
2499 for (id = days->begin(); id != days->end(); ++id) { 2512 for (id = days->begin(); id != days->end(); ++id) {
2500 if (*id >= data.day) { 2513 if (*id >= data.day) {
2501 if (lastMonth && *id > endDay) 2514 if (lastMonth && *id > endDay)
2502 return countGone; 2515 return countGone;
2503 if (++countGone >= countMax) 2516 if (++countGone >= countMax)
2504 return countMax; 2517 return countMax;
2505 } 2518 }
2506 } 2519 }
2507 } else { 2520 } else {
2508 countGone += days->count(); 2521 countGone += days->count();
2509 if (countGone >= countMax) 2522 if (countGone >= countMax)
2510 return countMax; 2523 return countMax;
2511 } 2524 }
2512 data.day = 1; 2525 data.day = 1;
2513 } else { 2526 } else {
2514 // The number of days per month is constant, so skip 2527 // The number of days per month is constant, so skip
2515 // the whole month. 2528 // the whole month.
2516 countGone += data.daysPerMonth; 2529 countGone += data.daysPerMonth;
2517 if (countGone >= countMax) 2530 if (countGone >= countMax)
2518 return countMax; 2531 return countMax;
2519 } 2532 }
2520 } 2533 }
2521 } 2534 }
2522 data.month = 1; 2535 data.month = 1;
2523 data.year += rFreq; 2536 data.year += rFreq;
2524 } 2537 }
2525 2538
2526 if (data.varies) { 2539 if (data.varies) {
2527 // The number of recurrences varies from year to year. 2540 // The number of recurrences varies from year to year.
2528 for ( ; ; ) { 2541 for ( ; ; ) {
2529 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2542 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2530 data.month = *im.current(); 2543 data.month = *im.current();
2531 days = data.dayList(); 2544 days = data.dayList();
2532 if (data.yearMonth() >= endYearMonth) { 2545 if (data.yearMonth() >= endYearMonth) {
2533 if (data.yearMonth() > endYearMonth) 2546 if (data.yearMonth() > endYearMonth)
2534 return countGone; 2547 return countGone;
2535 // Check the last month in the recurrence 2548 // Check the last month in the recurrence
2536 for (id = days->begin(); id != days->end(); ++id) { 2549 for (id = days->begin(); id != days->end(); ++id) {
2537 if (*id > endDay) 2550 if (*id > endDay)
2538 return countGone; 2551 return countGone;
2539 if (++countGone >= countMax) 2552 if (++countGone >= countMax)
2540 return countMax; 2553 return countMax;
2541 } 2554 }
2542 } else { 2555 } else {
2543 countGone += days->count(); 2556 countGone += days->count();
2544 if (countGone >= countMax) 2557 if (countGone >= countMax)
2545 return countMax; 2558 return countMax;
2546 } 2559 }
2547 } 2560 }
2548 data.year += rFreq; 2561 data.year += rFreq;
2549 } 2562 }
2550 } else { 2563 } else {
2551 // The number of recurrences is the same every year, 2564 // The number of recurrences is the same every year,
2552 // so skip the year-by-year check. 2565 // so skip the year-by-year check.
2553 // Skip the remaining whole years, but leave at least 2566 // Skip the remaining whole years, but leave at least
2554 // 1 recurrence remaining, in order to get its date. 2567 // 1 recurrence remaining, in order to get its date.
2555 int wholeYears = endYear - data.year; 2568 int wholeYears = endYear - data.year;
2556 countGone += (wholeYears / rFreq) * data.count; 2569 countGone += (wholeYears / rFreq) * data.count;
2557 if (countGone >= countMax) 2570 if (countGone >= countMax)
2558 return countMax; 2571 return countMax;
2559 if (wholeYears % rFreq) 2572 if (wholeYears % rFreq)
2560 return countGone; // end year isn't a recurrence year 2573 return countGone; // end year isn't a recurrence year
2561 data.year = endYear; 2574 data.year = endYear;
2562 2575
2563 // Check the last year in the recurrence. 2576 // Check the last year in the recurrence.
2564 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2577 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2565 data.month = *im.current(); 2578 data.month = *im.current();
2566 if (data.month >= endMonth) { 2579 if (data.month >= endMonth) {
2567 if (data.month > endMonth) 2580 if (data.month > endMonth)
2568 return countGone; 2581 return countGone;
2569 // Check the last month in the recurrence 2582 // Check the last month in the recurrence
2570 days = data.dayList(); 2583 days = data.dayList();
2571 for (id = days->begin(); id != days->end(); ++id) { 2584 for (id = days->begin(); id != days->end(); ++id) {
2572 if (*id > endDay) 2585 if (*id > endDay)
2573 return countGone; 2586 return countGone;
2574 if (++countGone >= countMax) 2587 if (++countGone >= countMax)
2575 return countMax; 2588 return countMax;
2576 } 2589 }
2577 } else { 2590 } else {
2578 countGone += data.daysPerMonth; 2591 countGone += data.daysPerMonth;
2579 if (countGone >= countMax) 2592 if (countGone >= countMax)
2580 return countMax; 2593 return countMax;
2581 } 2594 }
2582 } 2595 }
2583 } 2596 }
2584 return countGone; 2597 return countGone;
2585} 2598}
2586 2599
2587int Recurrence::yearlyPosCalcNextAfter(QDate &enddate, YearlyPosData &data) const 2600int Recurrence::yearlyPosCalcNextAfter(QDate &enddate, YearlyPosData &data) const
2588{ 2601{
2589 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 2602 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
2590 int countGone = 0; 2603 int countGone = 0;
2591 int endYear = enddate.year(); 2604 int endYear = enddate.year();
2592 int endMonth = enddate.month(); 2605 int endMonth = enddate.month();
2593 int endDay = enddate.day(); 2606 int endDay = enddate.day();
2594 if (endDay < data.day && --endMonth == 0) { 2607 if (endDay < data.day && --endMonth == 0) {
2595 endMonth = 12; 2608 endMonth = 12;
2596 --endYear; 2609 --endYear;
2597 } 2610 }
2598 int endYearMonth = endYear*12 + endMonth; 2611 int endYearMonth = endYear*12 + endMonth;
2599 QValueList<int>::ConstIterator id; 2612 QValueList<int>::ConstIterator id;
2600 const QValueList<int>* days; 2613 const QValueList<int>* days;
2601 2614
2602 if (data.varies) { 2615 if (data.varies) {
2603 // The number of recurrences varies from year to year. 2616 // The number of recurrences varies from year to year.
2604 for ( ; ; ) { 2617 for ( ; ; ) {
2605 // Check the next year 2618 // Check the next year
2606 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2619 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2607 if (*im.current() >= data.month) { 2620 if (*im.current() >= data.month) {
2608 // Check the next month 2621 // Check the next month
2609 data.month = *im.current(); 2622 data.month = *im.current();
2610 int ended = data.yearMonth() - endYearMonth; 2623 int ended = data.yearMonth() - endYearMonth;
2611 days = data.dayList(); 2624 days = data.dayList();
2612 if (ended >= 0 || data.day > 1) { 2625 if (ended >= 0 || data.day > 1) {
2613 // This is the start or end month, so check each day 2626 // This is the start or end month, so check each day
2614 for (id = days->begin(); id != days->end(); ++id) { 2627 for (id = days->begin(); id != days->end(); ++id) {
2615 if (*id >= data.day) { 2628 if (*id >= data.day) {
2616 ++countGone; 2629 ++countGone;
2617 if (ended > 0 || (ended == 0 && *id > endDay)) { 2630 if (ended > 0 || (ended == 0 && *id > endDay)) {
2618 data.day = *id; 2631 data.day = *id;
2619 goto ex; 2632 goto ex;
2620 } 2633 }
2621 if (--countTogo == 0) 2634 if (--countTogo == 0)
2622 return 0; 2635 return 0;
2623 } 2636 }
2624 } 2637 }
2625 } else { 2638 } else {
2626 // Skip the whole month 2639 // Skip the whole month
2627 uint n = days->count(); 2640 uint n = days->count();
2628 if (n >= countTogo) 2641 if (n >= countTogo)
2629 return 0; 2642 return 0;
2630 countGone += n; 2643 countGone += n;
2631 } 2644 }
2632 data.day = 1; // we've checked the start month now 2645 data.day = 1; // we've checked the start month now
2633 } 2646 }
2634 } 2647 }
2635 data.month = 1; // we've checked the start year now 2648 data.month = 1; // we've checked the start year now
2636 data.year += rFreq; 2649 data.year += rFreq;
2637 } 2650 }
2638 } else { 2651 } else {
2639 // The number of recurrences is the same every year. 2652 // The number of recurrences is the same every year.
2640 if (data.month > 1 || data.day > 1) { 2653 if (data.month > 1 || data.day > 1) {
2641 // Check what remains of the start year 2654 // Check what remains of the start year
2642 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2655 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2643 if (*im.current() >= data.month) { 2656 if (*im.current() >= data.month) {
2644 // Check what remains of the start month 2657 // Check what remains of the start month
2645 data.month = *im.current(); 2658 data.month = *im.current();
2646 int ended = data.yearMonth() - endYearMonth; 2659 int ended = data.yearMonth() - endYearMonth;
2647 if (ended >= 0 || data.day > 1) { 2660 if (ended >= 0 || data.day > 1) {
2648 // This is the start or end month, so check each day 2661 // This is the start or end month, so check each day
2649 days = data.dayList(); 2662 days = data.dayList();
2650 for (id = days->begin(); id != days->end(); ++id) { 2663 for (id = days->begin(); id != days->end(); ++id) {
2651 if (*id >= data.day) { 2664 if (*id >= data.day) {
2652 ++countGone; 2665 ++countGone;
2653 if (ended > 0 || (ended == 0 && *id > endDay)) { 2666 if (ended > 0 || (ended == 0 && *id > endDay)) {
2654 data.day = *id; 2667 data.day = *id;
2655 goto ex; 2668 goto ex;
2656 } 2669 }
2657 if (--countTogo == 0) 2670 if (--countTogo == 0)
2658 return 0; 2671 return 0;
2659 } 2672 }
2660 } 2673 }
2661 data.day = 1; // we've checked the start month now 2674 data.day = 1; // we've checked the start month now
2662 } else { 2675 } else {
2663 // Skip the whole month. 2676 // Skip the whole month.
2664 if (static_cast<uint>(data.daysPerMonth) >= countTogo) 2677 if (static_cast<uint>(data.daysPerMonth) >= countTogo)
2665 return 0; 2678 return 0;
2666 countGone += data.daysPerMonth; 2679 countGone += data.daysPerMonth;
2667 } 2680 }
2668 } 2681 }
2669 } 2682 }
2670 data.year += rFreq; 2683 data.year += rFreq;
2671 } 2684 }
2672 // Skip the remaining whole years to at least endYear. 2685 // Skip the remaining whole years to at least endYear.
2673 int recurYears = (endYear - data.year + rFreq - 1) / rFreq; 2686 int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
2674 if ((endYear - data.year)%rFreq == 0 2687 if ((endYear - data.year)%rFreq == 0
2675 && *rYearNums.getLast() <= endMonth) 2688 && *rYearNums.getLast() <= endMonth)
2676 ++recurYears; // required year is after endYear 2689 ++recurYears; // required year is after endYear
2677 if (recurYears) { 2690 if (recurYears) {
2678 int n = recurYears * data.count; 2691 int n = recurYears * data.count;
2679 if (static_cast<uint>(n) > countTogo) 2692 if (static_cast<uint>(n) > countTogo)
2680 return 0; // reached end of recurrence 2693 return 0; // reached end of recurrence
2681 countTogo -= n; 2694 countTogo -= n;
2682 countGone += n; 2695 countGone += n;
2683 data.year += recurYears * rFreq; 2696 data.year += recurYears * rFreq;
2684 } 2697 }
2685 2698
2686 // Check the last year in the recurrence 2699 // Check the last year in the recurrence
2687 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2700 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2688 data.month = *im.current(); 2701 data.month = *im.current();
2689 int ended = data.yearMonth() - endYearMonth; 2702 int ended = data.yearMonth() - endYearMonth;
2690 if (ended >= 0) { 2703 if (ended >= 0) {
2691 // This is the end month, so check each day 2704 // This is the end month, so check each day
2692 days = data.dayList(); 2705 days = data.dayList();
2693 for (id = days->begin(); id != days->end(); ++id) { 2706 for (id = days->begin(); id != days->end(); ++id) {
2694 ++countGone; 2707 ++countGone;
2695 if (ended > 0 || (ended == 0 && *id > endDay)) { 2708 if (ended > 0 || (ended == 0 && *id > endDay)) {
2696 data.day = *id; 2709 data.day = *id;
2697 goto ex; 2710 goto ex;
2698 } 2711 }
2699 if (--countTogo == 0) 2712 if (--countTogo == 0)
2700 return 0; 2713 return 0;
2701 } 2714 }
2702 } else { 2715 } else {
2703 // Skip the whole month. 2716 // Skip the whole month.
2704 if (static_cast<uint>(data.daysPerMonth) >= countTogo) 2717 if (static_cast<uint>(data.daysPerMonth) >= countTogo)
2705 return 0; 2718 return 0;
2706 countGone += data.daysPerMonth; 2719 countGone += data.daysPerMonth;
2707 } 2720 }
2708 } 2721 }
2709 } 2722 }
2710ex: 2723ex:
2711 enddate = data.date(); 2724 enddate = data.date();
2712 return countGone; 2725 return countGone;
2713} 2726}
2714 2727
2715 2728
2716/* Find count and, depending on 'func', the end date of an annual recurrence by day. 2729/* Find count and, depending on 'func', the end date of an annual recurrence by day.
2717 * Reply = total number of occurrences up to 'enddate', or 0 if error. 2730 * Reply = total number of occurrences up to 'enddate', or 0 if error.
2718 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 2731 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
2719 * recurrence end date. 2732 * recurrence end date.
2720 */ 2733 */
2721struct Recurrence::YearlyDayData { 2734struct Recurrence::YearlyDayData {
2722 int year; // current year 2735 int year; // current year
2723 int day; // current day of year 1..366 2736 int day; // current day of year 1..366
2724 bool varies; // true if day 366 recurs 2737 bool varies; // true if day 366 recurs
2725 private: 2738 private:
2726 int daycount; 2739 int daycount;
2727 public: 2740 public:
2728 YearlyDayData(const Recurrence* r, const QDate &date) 2741 YearlyDayData(const Recurrence* r, const QDate &date)
2729 : year(date.year()), day(date.dayOfYear()), varies(*r->rYearNums.getLast() == 366), 2742 : year(date.year()), day(date.dayOfYear()), varies(*r->rYearNums.getLast() == 366),
2730 daycount(r->rYearNums.count()) { } 2743 daycount(r->rYearNums.count()) { }
2731 bool leapYear() const { return QDate::leapYear(year); } 2744 bool leapYear() const { return QDate::leapYear(year); }
2732 int dayCount() const { return daycount - (varies && !QDate::leapYear(year) ? 1 : 0); } 2745 int dayCount() const { return daycount - (varies && !QDate::leapYear(year) ? 1 : 0); }
2733 bool isMaxDayCount() const { return !varies || QDate::leapYear(year); } 2746 bool isMaxDayCount() const { return !varies || QDate::leapYear(year); }
2734 QDate date() const { return QDate(year, 1, 1).addDays(day - 1); } 2747 QDate date() const { return QDate(year, 1, 1).addDays(day - 1); }
2735}; 2748};
2736 2749
2737int Recurrence::yearlyDayCalc(PeriodFunc func, QDate &enddate) const 2750int Recurrence::yearlyDayCalc(PeriodFunc func, QDate &enddate) const
2738{ 2751{
2739 if (rYearNums.isEmpty()) 2752 if (rYearNums.isEmpty())
2740 return 0; 2753 return 0;
2741 YearlyDayData data(this, mRecurStart.date()); 2754 YearlyDayData data(this, mRecurStart.date());
2742 switch (func) { 2755 switch (func) {
2743 case END_DATE_AND_COUNT: 2756 case END_DATE_AND_COUNT:
2744 return yearlyDayCalcEndDate(enddate, data); 2757 return yearlyDayCalcEndDate(enddate, data);
2745 case COUNT_TO_DATE: 2758 case COUNT_TO_DATE:
2746 return yearlyDayCalcToDate(enddate, data); 2759 return yearlyDayCalcToDate(enddate, data);
2747 case NEXT_AFTER_DATE: 2760 case NEXT_AFTER_DATE:
2748 return yearlyDayCalcNextAfter(enddate, data); 2761 return yearlyDayCalcNextAfter(enddate, data);
2749 } 2762 }
2750 return 0; 2763 return 0;
2751} 2764}
2752 2765
2753int Recurrence::yearlyDayCalcEndDate(QDate &enddate, YearlyDayData &data) const 2766int Recurrence::yearlyDayCalcEndDate(QDate &enddate, YearlyDayData &data) const
2754{ 2767{
2755 uint countTogo = rDuration + mRecurExDatesCount; 2768 uint countTogo = rDuration + mRecurExDatesCount;
2756 int countGone = 0; 2769 int countGone = 0;
2757 2770
2758 if (data.day > 1) { 2771 if (data.day > 1) {
2759 // Check what remains of the start year 2772 // Check what remains of the start year
2760 bool leapOK = data.isMaxDayCount(); 2773 bool leapOK = data.isMaxDayCount();
2761 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2774 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2762 int d = *it.current(); 2775 int d = *it.current();
2763 if (d >= data.day && (leapOK || d < 366)) { 2776 if (d >= data.day && (leapOK || d < 366)) {
2764 ++countGone; 2777 ++countGone;
2765 if (--countTogo == 0) { 2778 if (--countTogo == 0) {
2766 data.day = d; 2779 data.day = d;
2767 goto ex; 2780 goto ex;
2768 } 2781 }
2769 } 2782 }
2770 } 2783 }
2771 data.day = 1; 2784 data.day = 1;
2772 data.year += rFreq; 2785 data.year += rFreq;
2773 } 2786 }
2774 2787
2775 if (data.varies) { 2788 if (data.varies) {
2776 // The number of recurrences is different in leap years, 2789 // The number of recurrences is different in leap years,
2777 // so check year-by-year. 2790 // so check year-by-year.
2778 for ( ; ; ) { 2791 for ( ; ; ) {
2779 uint n = data.dayCount(); 2792 uint n = data.dayCount();
2780 if (n >= countTogo) 2793 if (n >= countTogo)
2781 break; 2794 break;
2782 countTogo -= n; 2795 countTogo -= n;
2783 countGone += n; 2796 countGone += n;
2784 data.year += rFreq; 2797 data.year += rFreq;
2785 } 2798 }
2786 } else { 2799 } else {
2787 // The number of recurrences is the same every year, 2800 // The number of recurrences is the same every year,
2788 // so skip the year-by-year check. 2801 // so skip the year-by-year check.
2789 // Skip the remaining whole years, but leave at least 2802 // Skip the remaining whole years, but leave at least
2790 // 1 recurrence remaining, in order to get its date. 2803 // 1 recurrence remaining, in order to get its date.
2791 int daysPerYear = rYearNums.count(); 2804 int daysPerYear = rYearNums.count();
2792 int wholeYears = (countTogo - 1) / daysPerYear; 2805 int wholeYears = (countTogo - 1) / daysPerYear;
2793 data.year += wholeYears * rFreq; 2806 data.year += wholeYears * rFreq;
2794 countGone += wholeYears * daysPerYear; 2807 countGone += wholeYears * daysPerYear;
2795 countTogo -= wholeYears * daysPerYear; 2808 countTogo -= wholeYears * daysPerYear;
2796 } 2809 }
2797 if (countTogo) { 2810 if (countTogo) {
2798 // Check the last year in the recurrence 2811 // Check the last year in the recurrence
2799 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2812 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2800 ++countGone; 2813 ++countGone;
2801 if (--countTogo == 0) { 2814 if (--countTogo == 0) {
2802 data.day = *it.current(); 2815 data.day = *it.current();
2803 break; 2816 break;
2804 } 2817 }
2805 } 2818 }
2806 } 2819 }
2807ex: 2820ex:
2808 enddate = data.date(); 2821 enddate = data.date();
2809 return countGone; 2822 return countGone;
2810} 2823}
2811 2824
2812int Recurrence::yearlyDayCalcToDate(const QDate &enddate, YearlyDayData &data) const 2825int Recurrence::yearlyDayCalcToDate(const QDate &enddate, YearlyDayData &data) const
2813{ 2826{
2814 int countGone = 0; 2827 int countGone = 0;
2815 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 2828 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
2816 int endYear = enddate.year(); 2829 int endYear = enddate.year();
2817 int endDay = enddate.dayOfYear(); 2830 int endDay = enddate.dayOfYear();
2818 2831
2819 if (data.day > 1) { 2832 if (data.day > 1) {
2820 // Check what remains of the start year 2833 // Check what remains of the start year
2821 bool leapOK = data.isMaxDayCount(); 2834 bool leapOK = data.isMaxDayCount();
2822 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2835 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2823 int d = *it.current(); 2836 int d = *it.current();
2824 if (d >= data.day && (leapOK || d < 366)) { 2837 if (d >= data.day && (leapOK || d < 366)) {
2825 if (data.year == endYear && d > endDay) 2838 if (data.year == endYear && d > endDay)
2826 return countGone; 2839 return countGone;
2827 if (++countGone >= countMax) 2840 if (++countGone >= countMax)
2828 return countMax; 2841 return countMax;
2829 } 2842 }
2830 } 2843 }
2831 data.day = 1; 2844 data.day = 1;
2832 data.year += rFreq; 2845 data.year += rFreq;
2833 } 2846 }
2834 2847
2835 if (data.varies) { 2848 if (data.varies) {
2836 // The number of recurrences is different in leap years, 2849 // The number of recurrences is different in leap years,
2837 // so check year-by-year. 2850 // so check year-by-year.
2838 while (data.year < endYear) { 2851 while (data.year < endYear) {
2839 uint n = data.dayCount(); 2852 uint n = data.dayCount();
2840 countGone += n; 2853 countGone += n;
2841 if (countGone >= countMax) 2854 if (countGone >= countMax)
2842 return countMax; 2855 return countMax;
2843 data.year += rFreq; 2856 data.year += rFreq;
2844 } 2857 }
2845 if (data.year > endYear) 2858 if (data.year > endYear)
2846 return countGone; 2859 return countGone;
2847 } else { 2860 } else {
2848 // The number of recurrences is the same every year. 2861 // The number of recurrences is the same every year.
2849 // Skip the remaining whole years. 2862 // Skip the remaining whole years.
2850 int wholeYears = endYear - data.year; 2863 int wholeYears = endYear - data.year;
2851 countGone += (wholeYears / rFreq) * rYearNums.count(); 2864 countGone += (wholeYears / rFreq) * rYearNums.count();
2852 if (countGone >= countMax) 2865 if (countGone >= countMax)
2853 return countMax; 2866 return countMax;
2854 if (wholeYears % rFreq) 2867 if (wholeYears % rFreq)
2855 return countGone; // end year isn't a recurrence year 2868 return countGone; // end year isn't a recurrence year
2856 data.year = endYear; 2869 data.year = endYear;
2857 } 2870 }
2858 2871
2859 if (data.year <= endYear) { 2872 if (data.year <= endYear) {
2860 // Check the last year in the recurrence 2873 // Check the last year in the recurrence
2861 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2874 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2862 if (*it.current() > endDay) 2875 if (*it.current() > endDay)
2863 return countGone; 2876 return countGone;
2864 if (++countGone >= countMax) 2877 if (++countGone >= countMax)
2865 return countMax; 2878 return countMax;
2866 } 2879 }
2867 } 2880 }
2868 return countGone; 2881 return countGone;
2869} 2882}
2870 2883
2871int Recurrence::yearlyDayCalcNextAfter(QDate &enddate, YearlyDayData &data) const 2884int Recurrence::yearlyDayCalcNextAfter(QDate &enddate, YearlyDayData &data) const
2872{ 2885{
2873 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 2886 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
2874 int countGone = 0; 2887 int countGone = 0;
2875 int endYear = enddate.year(); 2888 int endYear = enddate.year();
2876 int endDay = enddate.dayOfYear(); 2889 int endDay = enddate.dayOfYear();
2877 2890
2878 if (data.day > 1) { 2891 if (data.day > 1) {
2879 // Check what remains of the start year 2892 // Check what remains of the start year
2880 bool leapOK = data.isMaxDayCount(); 2893 bool leapOK = data.isMaxDayCount();
2881 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2894 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2882 int d = *it.current(); 2895 int d = *it.current();
2883 if (d >= data.day && (leapOK || d < 366)) { 2896 if (d >= data.day && (leapOK || d < 366)) {
2884 ++countGone; 2897 ++countGone;
2885 if (data.year == endYear && d > endDay) { 2898 if (data.year == endYear && d > endDay) {
2886 data.day = d; 2899 data.day = d;
2887 goto ex; 2900 goto ex;
2888 } 2901 }
2889 if (--countTogo == 0) 2902 if (--countTogo == 0)
2890 return 0; 2903 return 0;
2891 } 2904 }
2892 } 2905 }
2893 data.day = 1; 2906 data.day = 1;
2894 data.year += rFreq; 2907 data.year += rFreq;
2895 } 2908 }
2896 2909
2897 if (data.varies) { 2910 if (data.varies) {
2898 // The number of recurrences is different in leap years, 2911 // The number of recurrences is different in leap years,
2899 // so check year-by-year. 2912 // so check year-by-year.
2900 while (data.year <= endYear) { 2913 while (data.year <= endYear) {
2901 uint n = data.dayCount(); 2914 uint n = data.dayCount();
2902 if (data.year == endYear && *rYearNums.getLast() > endDay) 2915 if (data.year == endYear && *rYearNums.getLast() > endDay)
2903 break; 2916 break;
2904 if (n >= countTogo) 2917 if (n >= countTogo)
2905 break; 2918 break;
2906 countTogo -= n; 2919 countTogo -= n;
2907 countGone += n; 2920 countGone += n;
2908 data.year += rFreq; 2921 data.year += rFreq;
2909 } 2922 }
2910 } else { 2923 } else {
2911 // The number of recurrences is the same every year, 2924 // The number of recurrences is the same every year,
2912 // so skip the year-by-year check. 2925 // so skip the year-by-year check.
2913 // Skip the remaining whole years to at least endYear. 2926 // Skip the remaining whole years to at least endYear.
2914 int daysPerYear = rYearNums.count(); 2927 int daysPerYear = rYearNums.count();
2915 int recurYears = (endYear - data.year + rFreq - 1) / rFreq; 2928 int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
2916 if ((endYear - data.year)%rFreq == 0 2929 if ((endYear - data.year)%rFreq == 0
2917 && *rYearNums.getLast() <= endDay) 2930 && *rYearNums.getLast() <= endDay)
2918 ++recurYears; // required year is after endYear 2931 ++recurYears; // required year is after endYear
2919 if (recurYears) { 2932 if (recurYears) {
2920 int n = recurYears * daysPerYear; 2933 int n = recurYears * daysPerYear;
2921 if (static_cast<uint>(n) > countTogo) 2934 if (static_cast<uint>(n) > countTogo)
2922 return 0; // reached end of recurrence 2935 return 0; // reached end of recurrence
2923 countTogo -= n; 2936 countTogo -= n;
2924 countGone += n; 2937 countGone += n;
2925 data.year += recurYears * rFreq; 2938 data.year += recurYears * rFreq;
2926 } 2939 }
2927 } 2940 }
2928 2941
2929 // Check the last year in the recurrence 2942 // Check the last year in the recurrence
2930 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2943 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2931 ++countGone; 2944 ++countGone;
2932 int d = *it.current(); 2945 int d = *it.current();
2933 if (data.year > endYear || d > endDay) { 2946 if (data.year > endYear || d > endDay) {
2934 data.day = d; 2947 data.day = d;
2935 break; 2948 break;
2936 } 2949 }
2937 if (--countTogo == 0) 2950 if (--countTogo == 0)
2938 return 0; 2951 return 0;
2939 } 2952 }
2940ex: 2953ex:
2941 enddate = data.date(); 2954 enddate = data.date();
2942 return countGone; 2955 return countGone;
2943} 2956}
2944 2957
2945// Get the days in this month which recur, in numerical order. 2958// Get the days in this month which recur, in numerical order.
2946// Parameters: daysInMonth = number of days in this month 2959// Parameters: daysInMonth = number of days in this month
2947// startDayOfWeek = day of week for first day of month. 2960// startDayOfWeek = day of week for first day of month.
2948void Recurrence::getMonthlyPosDays(QValueList<int> &list, int daysInMonth, int startDayOfWeek) const 2961void Recurrence::getMonthlyPosDays(QValueList<int> &list, int daysInMonth, int startDayOfWeek) const
2949{ 2962{
2950 list.clear(); 2963 list.clear();
2951 int endDayOfWeek = (startDayOfWeek + daysInMonth - 2) % 7 + 1; 2964 int endDayOfWeek = (startDayOfWeek + daysInMonth - 2) % 7 + 1;
2952 // Go through the list, compiling a bit list of actual day numbers 2965 // Go through the list, compiling a bit list of actual day numbers
2953 Q_UINT32 days = 0; 2966 Q_UINT32 days = 0;
2954 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) { 2967 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) {
2955 int weeknum = pos.current()->rPos - 1; // get 0-based week number 2968 int weeknum = pos.current()->rPos - 1; // get 0-based week number
2956 QBitArray &rdays = pos.current()->rDays; 2969 QBitArray &rdays = pos.current()->rDays;
2957 if (pos.current()->negative) { 2970 if (pos.current()->negative) {
2958 // nth days before the end of the month 2971 // nth days before the end of the month
2959 for (uint i = 1; i <= 7; ++i) { 2972 for (uint i = 1; i <= 7; ++i) {
2960 if (rdays.testBit(i - 1)) { 2973 if (rdays.testBit(i - 1)) {
2961 int day = daysInMonth - weeknum*7 - (endDayOfWeek - i + 7) % 7; 2974 int day = daysInMonth - weeknum*7 - (endDayOfWeek - i + 7) % 7;
2962 if (day > 0) 2975 if (day > 0)
2963 days |= 1 << (day - 1); 2976 days |= 1 << (day - 1);
2964 } 2977 }
2965 } 2978 }
2966 } else { 2979 } else {
2967 // nth days after the start of the month 2980 // nth days after the start of the month
2968 for (uint i = 1; i <= 7; ++i) { 2981 for (uint i = 1; i <= 7; ++i) {
2969 if (rdays.testBit(i - 1)) { 2982 if (rdays.testBit(i - 1)) {
2970 int day = 1 + weeknum*7 + (i - startDayOfWeek + 7) % 7; 2983 int day = 1 + weeknum*7 + (i - startDayOfWeek + 7) % 7;
2971 if (day <= daysInMonth) 2984 if (day <= daysInMonth)
2972 days |= 1 << (day - 1); 2985 days |= 1 << (day - 1);
2973 } 2986 }
2974 } 2987 }
2975 } 2988 }
2976 } 2989 }
2977 // Compile the ordered list 2990 // Compile the ordered list
2978 Q_UINT32 mask = 1; 2991 Q_UINT32 mask = 1;
2979 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) { 2992 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) {
2980 if (days & mask) 2993 if (days & mask)
2981 list.append(i + 1); 2994 list.append(i + 1);
2982 } 2995 }
2983} 2996}
2984 2997
2985// Get the number of days in the month which recur. 2998// Get the number of days in the month which recur.
2986// Reply = -1 if the number varies from month to month. 2999// Reply = -1 if the number varies from month to month.
2987int Recurrence::countMonthlyPosDays() const 3000int Recurrence::countMonthlyPosDays() const
2988{ 3001{
2989 int count = 0; 3002 int count = 0;
2990 Q_UINT8 positive[5] = { 0, 0, 0, 0, 0 }; 3003 Q_UINT8 positive[5] = { 0, 0, 0, 0, 0 };
2991 Q_UINT8 negative[4] = { 0, 0, 0, 0 }; 3004 Q_UINT8 negative[4] = { 0, 0, 0, 0 };
2992 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) { 3005 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) {
2993 int weeknum = pos.current()->rPos; 3006 int weeknum = pos.current()->rPos;
2994 Q_UINT8* wk; 3007 Q_UINT8* wk;
2995 if (pos.current()->negative) { 3008 if (pos.current()->negative) {
2996 // nth days before the end of the month 3009 // nth days before the end of the month
2997 if (weeknum > 4) 3010 if (weeknum > 4)
2998 return -1; // days in 5th week are often missing 3011 return -1; // days in 5th week are often missing
2999 wk = &negative[4 - weeknum]; 3012 wk = &negative[4 - weeknum];
3000 } else { 3013 } else {
3001 // nth days after the start of the month 3014 // nth days after the start of the month
3002 if (weeknum > 4) 3015 if (weeknum > 4)
3003 return -1; // days in 5th week are often missing 3016 return -1; // days in 5th week are often missing
3004 wk = &positive[weeknum - 1]; 3017 wk = &positive[weeknum - 1];
3005 } 3018 }
3006 QBitArray &rdays = pos.current()->rDays; 3019 QBitArray &rdays = pos.current()->rDays;
3007 for (uint i = 0; i < 7; ++i) { 3020 for (uint i = 0; i < 7; ++i) {
3008 if (rdays.testBit(i)) { 3021 if (rdays.testBit(i)) {
3009 ++count; 3022 ++count;
3010 *wk |= (1 << i); 3023 *wk |= (1 << i);
3011 } 3024 }
3012 } 3025 }
3013 } 3026 }
3014 // Check for any possible days which could be duplicated by 3027 // Check for any possible days which could be duplicated by
3015 // a positive and a negative position. 3028 // a positive and a negative position.
3016 for (int i = 0; i < 4; ++i) { 3029 for (int i = 0; i < 4; ++i) {
3017 if (negative[i] & (positive[i] | positive[i+1])) 3030 if (negative[i] & (positive[i] | positive[i+1]))
3018 return -1; 3031 return -1;
3019 } 3032 }
3020 return count; 3033 return count;
3021} 3034}
3022 3035
3023// Get the days in this month which recur, in numerical order. 3036// Get the days in this month which recur, in numerical order.
3024// Reply = true if day numbers varies from month to month. 3037// Reply = true if day numbers varies from month to month.
3025bool Recurrence::getMonthlyDayDays(QValueList<int> &list, int daysInMonth) const 3038bool Recurrence::getMonthlyDayDays(QValueList<int> &list, int daysInMonth) const
3026{ 3039{
3027 list.clear(); 3040 list.clear();
3028 bool variable = false; 3041 bool variable = false;
3029 Q_UINT32 days = 0; 3042 Q_UINT32 days = 0;
3030 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { 3043 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
3031 int day = *it.current(); 3044 int day = *it.current();
3032 if (day > 0) { 3045 if (day > 0) {
3033 // date in the month 3046 // date in the month
3034 if (day <= daysInMonth) 3047 if (day <= daysInMonth)
3035 days |= 1 << (day - 1); 3048 days |= 1 << (day - 1);
3036 if (day > 28 && day <= 31) 3049 if (day > 28 && day <= 31)
3037 variable = true; // this date does not appear in some months 3050 variable = true; // this date does not appear in some months
3038 } else if (day < 0) { 3051 } else if (day < 0) {
3039 // days before the end of the month 3052 // days before the end of the month
3040 variable = true; // this date varies depending on the month length 3053 variable = true; // this date varies depending on the month length
3041 day = daysInMonth + day; // zero-based day of month 3054 day = daysInMonth + day; // zero-based day of month
3042 if (day >= 0) 3055 if (day >= 0)
3043 days |= 1 << day; 3056 days |= 1 << day;
3044 } 3057 }
3045 } 3058 }
3046 // Compile the ordered list 3059 // Compile the ordered list
3047 Q_UINT32 mask = 1; 3060 Q_UINT32 mask = 1;
3048 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) { 3061 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) {
3049 if (days & mask) 3062 if (days & mask)
3050 list.append(i + 1); 3063 list.append(i + 1);
3051 } 3064 }
3052 return variable; 3065 return variable;
3053} 3066}
3054 3067
3055// Get the months which recur, in numerical order, for both leap years and non-leap years. 3068// Get the months which recur, in numerical order, for both leap years and non-leap years.
3056// N.B. If February 29th recurs on March 1st in non-leap years, February (not March) is 3069// N.B. If February 29th recurs on March 1st in non-leap years, February (not March) is
3057// included in the non-leap year month list. 3070// included in the non-leap year month list.
3058// Reply = true if February 29th also recurs. 3071// Reply = true if February 29th also recurs.
3059bool Recurrence::getYearlyMonthMonths(int day, QValueList<int> &list, QValueList<int> &leaplist) const 3072bool Recurrence::getYearlyMonthMonths(int day, QValueList<int> &list, QValueList<int> &leaplist) const
3060{ 3073{
3061 list.clear(); 3074 list.clear();
3062 leaplist.clear(); 3075 leaplist.clear();
3063 bool feb29 = false; 3076 bool feb29 = false;
3064 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 3077 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
3065 int month = *it.current(); 3078 int month = *it.current();
3066 if (month == 2) { 3079 if (month == 2) {
3067 if (day <= 28) { 3080 if (day <= 28) {
3068 list.append(month); // date appears in February 3081 list.append(month); // date appears in February
3069 leaplist.append(month); 3082 leaplist.append(month);
3070 } 3083 }
3071 else if (day == 29) { 3084 else if (day == 29) {
3072 // February 29th 3085 // February 29th
3073 leaplist.append(month); 3086 leaplist.append(month);
3074 switch (mFeb29YearlyType) { 3087 switch (mFeb29YearlyType) {
3075 case rFeb28: 3088 case rFeb28:
3076 case rMar1: 3089 case rMar1:
3077 list.append(2); 3090 list.append(2);
3078 break; 3091 break;
3079 case rFeb29: 3092 case rFeb29:
3080 break; 3093 break;
3081 } 3094 }
3082 feb29 = true; 3095 feb29 = true;
3083 } 3096 }
3084 } 3097 }
3085 else if (day <= 30 || QDate(2000, month, 1).daysInMonth() == 31) { 3098 else if (day <= 30 || QDate(2000, month, 1).daysInMonth() == 31) {
3086 list.append(month); // date appears in every month 3099 list.append(month); // date appears in every month
3087 leaplist.append(month); 3100 leaplist.append(month);
3088 } 3101 }
3089 } 3102 }
3090 return feb29; 3103 return feb29;
3091} 3104}
3092 3105
3093/* From the recurrence day of the week list, get the earliest day in the 3106/* From the recurrence day of the week list, get the earliest day in the
3094 * specified week which is >= the startDay. 3107 * specified week which is >= the startDay.
3095 * Parameters: startDay = 1..7 (Monday..Sunday) 3108 * Parameters: startDay = 1..7 (Monday..Sunday)
3096 * useWeekStart = true to end search at day before next rWeekStart 3109 * useWeekStart = true to end search at day before next rWeekStart
3097 * = false to search for a full 7 days 3110 * = false to search for a full 7 days
3098 * Reply = day of the week (1..7), or 0 if none found. 3111 * Reply = day of the week (1..7), or 0 if none found.
3099 */ 3112 */
3100int Recurrence::getFirstDayInWeek(int startDay, bool useWeekStart) const 3113int Recurrence::getFirstDayInWeek(int startDay, bool useWeekStart) const
3101{ 3114{
3102 int last = ((useWeekStart ? rWeekStart : startDay) + 5)%7; 3115 int last = ((useWeekStart ? rWeekStart : startDay) + 5)%7;
3103 for (int i = startDay - 1; ; i = (i + 1)%7) { 3116 for (int i = startDay - 1; ; i = (i + 1)%7) {
3104 if (rDays.testBit(i)) 3117 if (rDays.testBit(i))
3105 return i + 1; 3118 return i + 1;
3106 if (i == last) 3119 if (i == last)
3107 return 0; 3120 return 0;
3108 } 3121 }
3109} 3122}
3110 3123
3111/* From the recurrence day of the week list, get the latest day in the 3124/* From the recurrence day of the week list, get the latest day in the
3112 * specified week which is <= the endDay. 3125 * specified week which is <= the endDay.
3113 * Parameters: endDay = 1..7 (Monday..Sunday) 3126 * Parameters: endDay = 1..7 (Monday..Sunday)
3114 * useWeekStart = true to end search at rWeekStart 3127 * useWeekStart = true to end search at rWeekStart
3115 * = false to search for a full 7 days 3128 * = false to search for a full 7 days
3116 * Reply = day of the week (1..7), or 0 if none found. 3129 * Reply = day of the week (1..7), or 0 if none found.
3117 */ 3130 */
3118int Recurrence::getLastDayInWeek(int endDay, bool useWeekStart) const 3131int Recurrence::getLastDayInWeek(int endDay, bool useWeekStart) const
3119{ 3132{
3120 int last = useWeekStart ? rWeekStart - 1 : endDay%7; 3133 int last = useWeekStart ? rWeekStart - 1 : endDay%7;
3121 for (int i = endDay - 1; ; i = (i + 6)%7) { 3134 for (int i = endDay - 1; ; i = (i + 6)%7) {
3122 if (rDays.testBit(i)) 3135 if (rDays.testBit(i))
3123 return i + 1; 3136 return i + 1;
3124 if (i == last) 3137 if (i == last)
3125 return 0; 3138 return 0;
3126 } 3139 }
3127} 3140}
3128 3141
3129/* From the recurrence monthly day number list or monthly day of week/week of 3142/* From the recurrence monthly day number list or monthly day of week/week of
3130 * month list, get the earliest day in the specified month which is >= the 3143 * month list, get the earliest day in the specified month which is >= the
3131 * earliestDate. 3144 * earliestDate.
3132 */ 3145 */
3133QDate Recurrence::getFirstDateInMonth(const QDate &earliestDate) const 3146QDate Recurrence::getFirstDateInMonth(const QDate &earliestDate) const
3134{ 3147{
3135 int earliestDay = earliestDate.day(); 3148 int earliestDay = earliestDate.day();
3136 int daysInMonth = earliestDate.daysInMonth(); 3149 int daysInMonth = earliestDate.daysInMonth();
3137 switch (recurs) { 3150 switch (recurs) {
3138 case rMonthlyDay: { 3151 case rMonthlyDay: {
3139 int minday = daysInMonth + 1; 3152 int minday = daysInMonth + 1;
3140 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { 3153 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
3141 int day = *it.current(); 3154 int day = *it.current();
3142 if (day < 0) 3155 if (day < 0)
3143 day = daysInMonth + day + 1; 3156 day = daysInMonth + day + 1;
3144 if (day >= earliestDay && day < minday) 3157 if (day >= earliestDay && day < minday)
3145 minday = day; 3158 minday = day;
3146 } 3159 }
3147 if (minday <= daysInMonth) 3160 if (minday <= daysInMonth)
3148 return earliestDate.addDays(minday - earliestDay); 3161 return earliestDate.addDays(minday - earliestDay);
3149 break; 3162 break;
3150 } 3163 }
3151 case rMonthlyPos: 3164 case rMonthlyPos:
3152 case rYearlyPos: { 3165 case rYearlyPos: {
3153 QDate monthBegin(earliestDate.addDays(1 - earliestDay)); 3166 QDate monthBegin(earliestDate.addDays(1 - earliestDay));
3154 QValueList<int> dayList; 3167 QValueList<int> dayList;
3155 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek()); 3168 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek());
3156 for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) { 3169 for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) {
3157 if (*id >= earliestDay) 3170 if (*id >= earliestDay)
3158 return monthBegin.addDays(*id - 1); 3171 return monthBegin.addDays(*id - 1);
3159 } 3172 }
3160 break; 3173 break;
3161 } 3174 }
3162 } 3175 }
3163 return QDate(); 3176 return QDate();
3164} 3177}
3165 3178
3166/* From the recurrence monthly day number list or monthly day of week/week of 3179/* From the recurrence monthly day number list or monthly day of week/week of
3167 * month list, get the latest day in the specified month which is <= the 3180 * month list, get the latest day in the specified month which is <= the
3168 * latestDate. 3181 * latestDate.
3169 */ 3182 */
3170QDate Recurrence::getLastDateInMonth(const QDate &latestDate) const 3183QDate Recurrence::getLastDateInMonth(const QDate &latestDate) const
3171{ 3184{
3172 int latestDay = latestDate.day(); 3185 int latestDay = latestDate.day();
3173 int daysInMonth = latestDate.daysInMonth(); 3186 int daysInMonth = latestDate.daysInMonth();
3174 switch (recurs) { 3187 switch (recurs) {
3175 case rMonthlyDay: { 3188 case rMonthlyDay: {
3176 int maxday = -1; 3189 int maxday = -1;
3177 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { 3190 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
3178 int day = *it.current(); 3191 int day = *it.current();
3179 if (day < 0) 3192 if (day < 0)
3180 day = daysInMonth + day + 1; 3193 day = daysInMonth + day + 1;
3181 if (day <= latestDay && day > maxday) 3194 if (day <= latestDay && day > maxday)
3182 maxday = day; 3195 maxday = day;
3183 } 3196 }
3184 if (maxday > 0) 3197 if (maxday > 0)
3185 return QDate(latestDate.year(), latestDate.month(), maxday); 3198 return QDate(latestDate.year(), latestDate.month(), maxday);
3186 break; 3199 break;
3187 } 3200 }
3188 case rMonthlyPos: 3201 case rMonthlyPos:
3189 case rYearlyPos: { 3202 case rYearlyPos: {
3190 QDate monthBegin(latestDate.addDays(1 - latestDay)); 3203 QDate monthBegin(latestDate.addDays(1 - latestDay));
3191 QValueList<int> dayList; 3204 QValueList<int> dayList;
3192 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek()); 3205 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek());
3193 for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) { 3206 for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) {
3194 if (*id <= latestDay) 3207 if (*id <= latestDay)
3195 return monthBegin.addDays(*id - 1); 3208 return monthBegin.addDays(*id - 1);
3196 } 3209 }
3197 break; 3210 break;
3198 } 3211 }
3199 } 3212 }
3200 return QDate(); 3213 return QDate();
3201} 3214}
3202 3215
3203/* From the recurrence yearly month list or yearly day list, get the earliest 3216/* From the recurrence yearly month list or yearly day list, get the earliest
3204 * month or day in the specified year which is >= the earliestDate. 3217 * month or day in the specified year which is >= the earliestDate.
3205 * Note that rYearNums is sorted in numerical order. 3218 * Note that rYearNums is sorted in numerical order.
3206 */ 3219 */
3207QDate Recurrence::getFirstDateInYear(const QDate &earliestDate) const 3220QDate Recurrence::getFirstDateInYear(const QDate &earliestDate) const
3208{ 3221{
3209 QPtrListIterator<int> it(rYearNums); 3222 QPtrListIterator<int> it(rYearNums);
3210 switch (recurs) { 3223 switch (recurs) {
3211 case rYearlyMonth: { 3224 case rYearlyMonth: {
3212 int day = recurStart().date().day(); 3225 int day = recurStart().date().day();
3213 int earliestYear = earliestDate.year(); 3226 int earliestYear = earliestDate.year();
3214 int earliestMonth = earliestDate.month(); 3227 int earliestMonth = earliestDate.month();
3215 int earliestDay = earliestDate.day(); 3228 int earliestDay = earliestDate.day();
3216 if (earliestDay > day) { 3229 if (earliestDay > day) {
3217 // The earliest date is later in the month than the recurrence date, 3230 // The earliest date is later in the month than the recurrence date,
3218 // so skip to the next month before starting to check 3231 // so skip to the next month before starting to check
3219 if (++earliestMonth > 12) 3232 if (++earliestMonth > 12)
3220 return QDate(); 3233 return QDate();
3221 } 3234 }
3222 for ( ; it.current(); ++it) { 3235 for ( ; it.current(); ++it) {
3223 int month = *it.current(); 3236 int month = *it.current();
3224 if (month >= earliestMonth) { 3237 if (month >= earliestMonth) {
3225 if (day <= 28 || QDate::isValid(earliestYear, month, day)) 3238 if (day <= 28 || QDate::isValid(earliestYear, month, day))
3226 return QDate(earliestYear, month, day); 3239 return QDate(earliestYear, month, day);
3227 if (day == 29 && month == 2) { 3240 if (day == 29 && month == 2) {
3228 // It's a recurrence on February 29th, in a non-leap year 3241 // It's a recurrence on February 29th, in a non-leap year
3229 switch (mFeb29YearlyType) { 3242 switch (mFeb29YearlyType) {
3230 case rMar1: 3243 case rMar1:
3231 return QDate(earliestYear, 3, 1); 3244 return QDate(earliestYear, 3, 1);
3232 case rFeb28: 3245 case rFeb28:
3233 if (earliestDay <= 28) 3246 if (earliestDay <= 28)
3234 return QDate(earliestYear, 2, 28); 3247 return QDate(earliestYear, 2, 28);
3235 break; 3248 break;
3236 case rFeb29: 3249 case rFeb29:
3237 break; 3250 break;
3238 } 3251 }
3239 } 3252 }
3240 } 3253 }
3241 } 3254 }
3242 break; 3255 break;
3243 } 3256 }
3244 case rYearlyPos: { 3257 case rYearlyPos: {
3245 QValueList<int> dayList; 3258 QValueList<int> dayList;
3246 int earliestYear = earliestDate.year(); 3259 int earliestYear = earliestDate.year();
3247 int earliestMonth = earliestDate.month(); 3260 int earliestMonth = earliestDate.month();
3248 int earliestDay = earliestDate.day(); 3261 int earliestDay = earliestDate.day();
3249 for ( ; it.current(); ++it) { 3262 for ( ; it.current(); ++it) {
3250 int month = *it.current(); 3263 int month = *it.current();
3251 if (month >= earliestMonth) { 3264 if (month >= earliestMonth) {
3252 QDate monthBegin(earliestYear, month, 1); 3265 QDate monthBegin(earliestYear, month, 1);
3253 getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek()); 3266 getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek());
3254 for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) { 3267 for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) {
3255 if (*id >= earliestDay) 3268 if (*id >= earliestDay)
3256 return monthBegin.addDays(*id - 1); 3269 return monthBegin.addDays(*id - 1);
3257 } 3270 }
3258 earliestDay = 1; 3271 earliestDay = 1;
3259 } 3272 }
3260 } 3273 }
3261 break; 3274 break;
3262 } 3275 }
3263 case rYearlyDay: { 3276 case rYearlyDay: {
3264 int earliestDay = earliestDate.dayOfYear(); 3277 int earliestDay = earliestDate.dayOfYear();
3265 for ( ; it.current(); ++it) { 3278 for ( ; it.current(); ++it) {
3266 int day = *it.current(); 3279 int day = *it.current();
3267 if (day >= earliestDay && (day <= 365 || day <= earliestDate.daysInYear())) 3280 if (day >= earliestDay && (day <= 365 || day <= earliestDate.daysInYear()))
3268 return earliestDate.addDays(day - earliestDay); 3281 return earliestDate.addDays(day - earliestDay);
3269 } 3282 }
3270 break; 3283 break;
3271 } 3284 }
3272 } 3285 }
3273 return QDate(); 3286 return QDate();
3274} 3287}
3275 3288
3276/* From the recurrence yearly month list or yearly day list, get the latest 3289/* From the recurrence yearly month list or yearly day list, get the latest
3277 * month or day in the specified year which is <= the latestDate. 3290 * month or day in the specified year which is <= the latestDate.
3278 * Note that rYearNums is sorted in numerical order. 3291 * Note that rYearNums is sorted in numerical order.
3279 */ 3292 */
3280QDate Recurrence::getLastDateInYear(const QDate &latestDate) const 3293QDate Recurrence::getLastDateInYear(const QDate &latestDate) const
3281{ 3294{
3282 QPtrListIterator<int> it(rYearNums); 3295 QPtrListIterator<int> it(rYearNums);
3283 switch (recurs) { 3296 switch (recurs) {
3284 case rYearlyMonth: { 3297 case rYearlyMonth: {
3285 int day = recurStart().date().day(); 3298 int day = recurStart().date().day();
3286 int latestYear = latestDate.year(); 3299 int latestYear = latestDate.year();
3287 int latestMonth = latestDate.month(); 3300 int latestMonth = latestDate.month();
3288 if (latestDate.day() > day) { 3301 if (latestDate.day() > day) {
3289 // The latest date is earlier in the month than the recurrence date, 3302 // The latest date is earlier in the month than the recurrence date,
3290 // so skip to the previous month before starting to check 3303 // so skip to the previous month before starting to check
3291 if (--latestMonth <= 0) 3304 if (--latestMonth <= 0)
3292 return QDate(); 3305 return QDate();
3293 } 3306 }
3294 for (it.toLast(); it.current(); --it) { 3307 for (it.toLast(); it.current(); --it) {
3295 int month = *it.current(); 3308 int month = *it.current();
3296 if (month <= latestMonth) { 3309 if (month <= latestMonth) {
3297 if (day <= 28 || QDate::isValid(latestYear, month, day)) 3310 if (day <= 28 || QDate::isValid(latestYear, month, day))
3298 return QDate(latestYear, month, day); 3311 return QDate(latestYear, month, day);
3299 if (day == 29 && month == 2) { 3312 if (day == 29 && month == 2) {
3300 // It's a recurrence on February 29th, in a non-leap year 3313 // It's a recurrence on February 29th, in a non-leap year
3301 switch (mFeb29YearlyType) { 3314 switch (mFeb29YearlyType) {
3302 case rMar1: 3315 case rMar1:
3303 if (latestMonth >= 3) 3316 if (latestMonth >= 3)
3304 return QDate(latestYear, 3, 1); 3317 return QDate(latestYear, 3, 1);
3305 break; 3318 break;
3306 case rFeb28: 3319 case rFeb28:
3307 return QDate(latestYear, 2, 28); 3320 return QDate(latestYear, 2, 28);
3308 case rFeb29: 3321 case rFeb29:
3309 break; 3322 break;
3310 } 3323 }
3311 } 3324 }
3312 } 3325 }
3313 } 3326 }
3314 break; 3327 break;
3315 } 3328 }
3316 case rYearlyPos: { 3329 case rYearlyPos: {
3317 QValueList<int> dayList; 3330 QValueList<int> dayList;
3318 int latestYear = latestDate.year(); 3331 int latestYear = latestDate.year();
3319 int latestMonth = latestDate.month(); 3332 int latestMonth = latestDate.month();
3320 int latestDay = latestDate.day(); 3333 int latestDay = latestDate.day();
3321 for (it.toLast(); it.current(); --it) { 3334 for (it.toLast(); it.current(); --it) {
3322 int month = *it.current(); 3335 int month = *it.current();
3323 if (month <= latestMonth) { 3336 if (month <= latestMonth) {
3324 QDate monthBegin(latestYear, month, 1); 3337 QDate monthBegin(latestYear, month, 1);
3325 getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek()); 3338 getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek());
3326 for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) { 3339 for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) {
3327 if (*id <= latestDay) 3340 if (*id <= latestDay)
3328 return monthBegin.addDays(*id - 1); 3341 return monthBegin.addDays(*id - 1);
3329 } 3342 }
3330 latestDay = 31; 3343 latestDay = 31;
3331 } 3344 }
3332 } 3345 }
3333 break; 3346 break;
3334 } 3347 }
3335 case rYearlyDay: { 3348 case rYearlyDay: {
3336 int latestDay = latestDate.dayOfYear(); 3349 int latestDay = latestDate.dayOfYear();
3337 for (it.toLast(); it.current(); --it) { 3350 for (it.toLast(); it.current(); --it) {
3338 int day = *it.current(); 3351 int day = *it.current();
3339 if (day <= latestDay) 3352 if (day <= latestDay)
3340 return latestDate.addDays(day - latestDay); 3353 return latestDate.addDays(day - latestDay);
3341 } 3354 }
3342 break; 3355 break;
3343 } 3356 }
3344 } 3357 }
3345 return QDate(); 3358 return QDate();
3346} 3359}
3347 3360
3348void Recurrence::dump() const 3361void Recurrence::dump() const
3349{ 3362{
3350 kdDebug() << "Recurrence::dump():" << endl; 3363 kdDebug() << "Recurrence::dump():" << endl;
3351 3364
3352 kdDebug() << " type: " << recurs << endl; 3365 kdDebug() << " type: " << recurs << endl;
3353 3366
3354 kdDebug() << " rDays: " << endl; 3367 kdDebug() << " rDays: " << endl;
3355 int i; 3368 int i;
3356 for( i = 0; i < 7; ++i ) { 3369 for( i = 0; i < 7; ++i ) {
3357 kdDebug() << " " << i << ": " 3370 kdDebug() << " " << i << ": "
3358 << ( rDays.testBit( i ) ? "true" : "false" ) << endl; 3371 << ( rDays.testBit( i ) ? "true" : "false" ) << endl;
3359 } 3372 }
3360} 3373}