libkcal Library API Documentation

recurrence.cpp

00001 /* 00002 This file is part of libkcal. 00003 Copyright (c) 1998 Preston Brown 00004 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> 00005 Copyright (c) 2002 David Jarvie <software@astrojar.org.uk> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00020 Boston, MA 02111-1307, USA. 00021 */ 00022 00023 #include <limits.h> 00024 00025 #include <kdebug.h> 00026 #include <kglobal.h> 00027 #include <klocale.h> 00028 00029 #include "incidence.h" 00030 00031 #include "recurrence.h" 00032 00033 using namespace KCal; 00034 00035 Recurrence::Feb29Type Recurrence::mFeb29YearlyDefaultType = Recurrence::rMar1; 00036 00037 00038 Recurrence::Recurrence(Incidence *parent, int compatVersion) 00039 : recurs(rNone), // by default, it's not a recurring event 00040 rWeekStart(1), // default is Monday 00041 rDays(7), 00042 mUseCachedEndDT(false), 00043 mFloats(parent ? parent->doesFloat() : false), 00044 mRecurReadOnly(false), 00045 mFeb29YearlyType(mFeb29YearlyDefaultType), 00046 mCompatVersion(compatVersion ? compatVersion : INT_MAX), 00047 mCompatRecurs(rNone), 00048 mCompatDuration(0), 00049 mParent(parent) 00050 { 00051 rMonthDays.setAutoDelete( true ); 00052 rMonthPositions.setAutoDelete( true ); 00053 rYearNums.setAutoDelete( true ); 00054 } 00055 00056 Recurrence::Recurrence(const Recurrence &r, Incidence *parent) 00057 : recurs(r.recurs), 00058 rWeekStart(r.rWeekStart), 00059 rDays(r.rDays.copy()), 00060 rFreq(r.rFreq), 00061 rDuration(r.rDuration), 00062 rEndDateTime(r.rEndDateTime), 00063 mCachedEndDT(r.mCachedEndDT), 00064 mUseCachedEndDT(r.mUseCachedEndDT), 00065 mRecurStart(r.mRecurStart), 00066 mFloats(r.mFloats), 00067 mRecurReadOnly(r.mRecurReadOnly), 00068 mFeb29YearlyType(r.mFeb29YearlyType), 00069 mCompatVersion(r.mCompatVersion), 00070 mCompatRecurs(r.mCompatRecurs), 00071 mCompatDuration(r.mCompatDuration), 00072 mParent(parent) 00073 { 00074 for (QPtrListIterator<rMonthPos> mp(r.rMonthPositions); mp.current(); ++mp) { 00075 rMonthPos *tmp = new rMonthPos; 00076 tmp->rPos = mp.current()->rPos; 00077 tmp->negative = mp.current()->negative; 00078 tmp->rDays = mp.current()->rDays.copy(); 00079 rMonthPositions.append(tmp); 00080 } 00081 for (QPtrListIterator<int> md(r.rMonthDays); md.current(); ++md) { 00082 int *tmp = new int; 00083 *tmp = *md.current(); 00084 rMonthDays.append(tmp); 00085 } 00086 for (QPtrListIterator<int> yn(r.rYearNums); yn.current(); ++yn) { 00087 int *tmp = new int; 00088 *tmp = *yn.current(); 00089 rYearNums.append(tmp); 00090 } 00091 rMonthDays.setAutoDelete( true ); 00092 rMonthPositions.setAutoDelete( true ); 00093 rYearNums.setAutoDelete( true ); 00094 } 00095 00096 Recurrence::~Recurrence() 00097 { 00098 } 00099 00100 00101 bool Recurrence::operator==( const Recurrence& r2 ) const 00102 { 00103 if ( recurs == rNone && r2.recurs == rNone ) 00104 return true; 00105 if ( recurs != r2.recurs 00106 || rFreq != r2.rFreq 00107 || rDuration != r2.rDuration 00108 || !rDuration && rEndDateTime != r2.rEndDateTime 00109 || mRecurStart != r2.mRecurStart 00110 || mFloats != r2.mFloats 00111 || mRecurReadOnly != r2.mRecurReadOnly ) 00112 return false; 00113 // no need to compare mCompat* and mParent 00114 // OK to compare the pointers 00115 switch ( recurs ) 00116 { 00117 case rWeekly: 00118 return rDays == r2.rDays 00119 && rWeekStart == r2.rWeekStart; 00120 case rMonthlyPos: 00121 return rMonthPositions == r2.rMonthPositions; 00122 case rMonthlyDay: 00123 return rMonthDays == r2.rMonthDays; 00124 case rYearlyPos: 00125 return rYearNums == r2.rYearNums 00126 && rMonthPositions == r2.rMonthPositions; 00127 case rYearlyMonth: 00128 return rYearNums == r2.rYearNums 00129 && rMonthDays == r2.rMonthDays 00130 && mFeb29YearlyType == r2.mFeb29YearlyType; 00131 case rYearlyDay: 00132 return rYearNums == r2.rYearNums; 00133 case rNone: 00134 case rMinutely: 00135 case rHourly: 00136 case rDaily: 00137 default: 00138 return true; 00139 } 00140 } 00141 00142 00143 void Recurrence::setCompatVersion(int version) 00144 { 00145 mCompatVersion = version ? version : INT_MAX; 00146 mUseCachedEndDT = false; 00147 } 00148 00149 ushort Recurrence::doesRecur() const 00150 { 00151 return recurs; 00152 } 00153 00154 bool Recurrence::recursOnPure(const QDate &qd) const 00155 { 00156 switch(recurs) { 00157 case rMinutely: 00158 return recursSecondly(qd, rFreq*60); 00159 case rHourly: 00160 return recursSecondly(qd, rFreq*3600); 00161 case rDaily: 00162 return recursDaily(qd); 00163 case rWeekly: 00164 return recursWeekly(qd); 00165 case rMonthlyPos: 00166 case rMonthlyDay: 00167 return recursMonthly(qd); 00168 case rYearlyMonth: 00169 return recursYearlyByMonth(qd); 00170 case rYearlyDay: 00171 return recursYearlyByDay(qd); 00172 case rYearlyPos: 00173 return recursYearlyByPos(qd); 00174 default: 00175 // catch-all. Should never get here. 00176 kdError(5800) << "Control should never reach here in recursOnPure()!" << endl; 00177 case rNone: 00178 return false; 00179 } // case 00180 } 00181 00182 bool Recurrence::recursAtPure(const QDateTime &dt) const 00183 { 00184 switch(recurs) { 00185 case rMinutely: 00186 return recursMinutelyAt(dt, rFreq); 00187 case rHourly: 00188 return recursMinutelyAt(dt, rFreq*60); 00189 default: 00190 if (dt.time() != mRecurStart.time()) 00191 return false; 00192 switch(recurs) { 00193 case rDaily: 00194 return recursDaily(dt.date()); 00195 case rWeekly: 00196 return recursWeekly(dt.date()); 00197 case rMonthlyPos: 00198 case rMonthlyDay: 00199 return recursMonthly(dt.date()); 00200 case rYearlyMonth: 00201 return recursYearlyByMonth(dt.date()); 00202 case rYearlyDay: 00203 return recursYearlyByDay(dt.date()); 00204 case rYearlyPos: 00205 return recursYearlyByPos(dt.date()); 00206 default: 00207 // catch-all. Should never get here. 00208 kdError(5800) << "Control should never reach here in recursAtPure()!" << endl; 00209 case rNone: 00210 return false; 00211 } 00212 } // case 00213 } 00214 00215 QDate Recurrence::endDate(bool *result) const 00216 { 00217 return endDateTime(result).date(); 00218 } 00219 00220 QDateTime Recurrence::endDateTime(bool *result) const 00221 { 00222 int count = 0; 00223 if (result) 00224 *result = true; 00225 QDate end; 00226 if (recurs != rNone) { 00227 if (rDuration < 0) 00228 return QDateTime(); // infinite recurrence 00229 if (rDuration == 0) 00230 return rEndDateTime; 00231 00232 // The end date is determined by the recurrence count 00233 if (mUseCachedEndDT) { 00234 if (result && !mCachedEndDT.isValid()) 00235 *result = false; // error - there is no recurrence 00236 return mCachedEndDT; // avoid potentially long calculation 00237 } 00238 00239 mUseCachedEndDT = true; 00240 switch (recurs) 00241 { 00242 case rMinutely: 00243 mCachedEndDT = mRecurStart.addSecs((rDuration-1)*rFreq*60); 00244 return mCachedEndDT; 00245 case rHourly: 00246 mCachedEndDT = mRecurStart.addSecs((rDuration-1)*rFreq*3600); 00247 return mCachedEndDT; 00248 case rDaily: 00249 mCachedEndDT = mRecurStart.addDays((rDuration-1)*rFreq); 00250 return mCachedEndDT; 00251 00252 case rWeekly: 00253 count = weeklyCalc(END_DATE_AND_COUNT, end); 00254 break; 00255 case rMonthlyPos: 00256 case rMonthlyDay: 00257 count = monthlyCalc(END_DATE_AND_COUNT, end); 00258 break; 00259 case rYearlyMonth: 00260 count = yearlyMonthCalc(END_DATE_AND_COUNT, end); 00261 break; 00262 case rYearlyDay: 00263 count = yearlyDayCalc(END_DATE_AND_COUNT, end); 00264 break; 00265 case rYearlyPos: 00266 count = yearlyPosCalc(END_DATE_AND_COUNT, end); 00267 break; 00268 default: 00269 // catch-all. Should never get here. 00270 kdError(5800) << "Control should never reach here in endDate()!" << endl; 00271 mUseCachedEndDT = false; 00272 break; 00273 } 00274 } 00275 if (!count) { 00276 if (result) 00277 *result = false; 00278 mCachedEndDT = QDateTime(); // error - there is no recurrence 00279 } 00280 else 00281 mCachedEndDT = QDateTime(end, mRecurStart.time()); 00282 return mCachedEndDT; 00283 } 00284 00285 int Recurrence::durationTo(const QDate &date) const 00286 { 00287 QDate d = date; 00288 return recurCalc(COUNT_TO_DATE, d); 00289 } 00290 00291 int Recurrence::durationTo(const QDateTime &datetime) const 00292 { 00293 QDateTime dt = datetime; 00294 return recurCalc(COUNT_TO_DATE, dt); 00295 } 00296 00297 void Recurrence::unsetRecurs() 00298 { 00299 if (mRecurReadOnly) return; 00300 recurs = rNone; 00301 rMonthPositions.clear(); 00302 rMonthDays.clear(); 00303 rYearNums.clear(); 00304 mUseCachedEndDT = false; 00305 } 00306 00307 void Recurrence::setRecurStart(const QDateTime &start) 00308 { 00309 mRecurStart = start; 00310 mFloats = false; 00311 switch (recurs) 00312 { 00313 case rMinutely: 00314 case rHourly: 00315 break; 00316 case rDaily: 00317 case rWeekly: 00318 case rMonthlyPos: 00319 case rMonthlyDay: 00320 case rYearlyMonth: 00321 case rYearlyDay: 00322 case rYearlyPos: 00323 default: 00324 rEndDateTime.setTime(start.time()); 00325 break; 00326 } 00327 mUseCachedEndDT = false; 00328 } 00329 00330 void Recurrence::setRecurStart(const QDate &start) 00331 { 00332 mRecurStart.setDate(start); 00333 mRecurStart.setTime(QTime(0,0,0)); 00334 switch (recurs) 00335 { 00336 case rMinutely: 00337 case rHourly: 00338 break; 00339 case rDaily: 00340 case rWeekly: 00341 case rMonthlyPos: 00342 case rMonthlyDay: 00343 case rYearlyMonth: 00344 case rYearlyDay: 00345 case rYearlyPos: 00346 default: 00347 mFloats = true; 00348 break; 00349 } 00350 mUseCachedEndDT = false; 00351 } 00352 00353 void Recurrence::setFloats(bool f) 00354 { 00355 if (f && mFloats || !f && !mFloats) 00356 return; // no change 00357 00358 switch (recurs) 00359 { 00360 case rDaily: 00361 case rWeekly: 00362 case rMonthlyPos: 00363 case rMonthlyDay: 00364 case rYearlyMonth: 00365 case rYearlyDay: 00366 case rYearlyPos: 00367 break; 00368 case rMinutely: 00369 case rHourly: 00370 default: 00371 return; // can't set sub-daily to floating 00372 } 00373 mFloats = f; 00374 if (f) { 00375 mRecurStart.setTime(QTime(0,0,0)); 00376 rEndDateTime.setTime(QTime(0,0,0)); 00377 } 00378 mUseCachedEndDT = false; 00379 } 00380 00381 int Recurrence::frequency() const 00382 { 00383 return rFreq; 00384 } 00385 00386 int Recurrence::duration() const 00387 { 00388 return rDuration; 00389 } 00390 00391 void Recurrence::setDuration(int _rDuration) 00392 { 00393 if (mRecurReadOnly) return; 00394 if (_rDuration > 0) { 00395 rDuration = _rDuration; 00396 // Compatibility mode is only needed when reading the calendar in ICalFormatImpl, 00397 // so explicitly setting the duration means no backwards compatibility is needed. 00398 mCompatDuration = 0; 00399 } 00400 mUseCachedEndDT = false; 00401 } 00402 00403 QString Recurrence::endDateStr(bool shortfmt) const 00404 { 00405 return KGlobal::locale()->formatDate(rEndDateTime.date(),shortfmt); 00406 } 00407 00408 const QBitArray &Recurrence::days() const 00409 { 00410 return rDays; 00411 } 00412 00413 const QPtrList<Recurrence::rMonthPos> &Recurrence::monthPositions() const 00414 { 00415 return rMonthPositions; 00416 } 00417 00418 const QPtrList<Recurrence::rMonthPos> &Recurrence::yearMonthPositions() const 00419 { 00420 return rMonthPositions; 00421 } 00422 00423 const QPtrList<int> &Recurrence::monthDays() const 00424 { 00425 return rMonthDays; 00426 } 00427 00428 void Recurrence::setMinutely(int _rFreq, int _rDuration) 00429 { 00430 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 00431 return; 00432 setDailySub(rMinutely, _rFreq, _rDuration); 00433 } 00434 00435 void Recurrence::setMinutely(int _rFreq, const QDateTime &_rEndDateTime) 00436 { 00437 if (mRecurReadOnly) return; 00438 rEndDateTime = _rEndDateTime; 00439 setDailySub(rMinutely, _rFreq, 0); 00440 } 00441 00442 void Recurrence::setHourly(int _rFreq, int _rDuration) 00443 { 00444 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 00445 return; 00446 setDailySub(rHourly, _rFreq, _rDuration); 00447 } 00448 00449 void Recurrence::setHourly(int _rFreq, const QDateTime &_rEndDateTime) 00450 { 00451 if (mRecurReadOnly) return; 00452 rEndDateTime = _rEndDateTime; 00453 setDailySub(rHourly, _rFreq, 0); 00454 } 00455 00456 void Recurrence::setDaily(int _rFreq, int _rDuration) 00457 { 00458 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 00459 return; 00460 setDailySub(rDaily, _rFreq, _rDuration); 00461 } 00462 00463 void Recurrence::setDaily(int _rFreq, const QDate &_rEndDate) 00464 { 00465 if (mRecurReadOnly) return; 00466 rEndDateTime.setDate(_rEndDate); 00467 rEndDateTime.setTime(mRecurStart.time()); 00468 setDailySub(rDaily, _rFreq, 0); 00469 } 00470 00471 void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays, 00472 int _rDuration, int _rWeekStart) 00473 { 00474 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 00475 return; 00476 mUseCachedEndDT = false; 00477 00478 recurs = rWeekly; 00479 rFreq = _rFreq; 00480 rDays = _rDays; 00481 rWeekStart = _rWeekStart; 00482 rDuration = _rDuration; 00483 if (mCompatVersion < 310 && _rDuration > 0) { 00484 // Backwards compatibility for KDE < 3.1. 00485 // rDuration was set to the number of time periods to recur, 00486 // with week start always on a Monday. 00487 // Convert this to the number of occurrences. 00488 mCompatDuration = _rDuration; 00489 int weeks = ((mCompatDuration-1)*7) + (7 - mRecurStart.date().dayOfWeek()); 00490 QDate end(mRecurStart.date().addDays(weeks * rFreq)); 00491 rDuration = INT_MAX; // ensure that weeklyCalc() does its job correctly 00492 rDuration = weeklyCalc(COUNT_TO_DATE, end); 00493 } else { 00494 mCompatDuration = 0; 00495 } 00496 rMonthPositions.clear(); 00497 rMonthDays.clear(); 00498 if (mParent) mParent->updated(); 00499 } 00500 00501 void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays, 00502 const QDate &_rEndDate, int _rWeekStart) 00503 { 00504 if (mRecurReadOnly) return; 00505 mUseCachedEndDT = false; 00506 00507 recurs = rWeekly; 00508 rFreq = _rFreq; 00509 rDays = _rDays; 00510 rWeekStart = _rWeekStart; 00511 rEndDateTime.setDate(_rEndDate); 00512 rEndDateTime.setTime(mRecurStart.time()); 00513 rDuration = 0; // set to 0 because there is an end date 00514 mCompatDuration = 0; 00515 rMonthPositions.clear(); 00516 rMonthDays.clear(); 00517 rYearNums.clear(); 00518 if (mParent) mParent->updated(); 00519 } 00520 00521 void Recurrence::setMonthly(short type, int _rFreq, int _rDuration) 00522 { 00523 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 00524 return; 00525 mUseCachedEndDT = false; 00526 00527 recurs = type; 00528 rFreq = _rFreq; 00529 rDuration = _rDuration; 00530 if (mCompatVersion < 310) 00531 mCompatDuration = (_rDuration > 0) ? _rDuration : 0; 00532 rYearNums.clear(); 00533 if (mParent) mParent->updated(); 00534 } 00535 00536 void Recurrence::setMonthly(short type, int _rFreq, 00537 const QDate &_rEndDate) 00538 { 00539 if (mRecurReadOnly) return; 00540 mUseCachedEndDT = false; 00541 00542 recurs = type; 00543 rFreq = _rFreq; 00544 rEndDateTime.setDate(_rEndDate); 00545 rEndDateTime.setTime(mRecurStart.time()); 00546 rDuration = 0; // set to 0 because there is an end date 00547 mCompatDuration = 0; 00548 rYearNums.clear(); 00549 if (mParent) mParent->updated(); 00550 } 00551 00552 void Recurrence::addMonthlyPos(short _rPos, const QBitArray &_rDays) 00553 { 00554 if (recurs == rMonthlyPos) 00555 addMonthlyPos_(_rPos, _rDays); 00556 } 00557 00558 void Recurrence::addMonthlyPos_(short _rPos, const QBitArray &_rDays) 00559 { 00560 if (mRecurReadOnly 00561 || _rPos == 0 || _rPos > 5 || _rPos < -5) // invalid week number 00562 return; 00563 00564 mUseCachedEndDT = false; 00565 for (rMonthPos* it = rMonthPositions.first(); it; it = rMonthPositions.next()) { 00566 int itPos = it->negative ? -it->rPos : it->rPos; 00567 if (_rPos == itPos) { 00568 // This week is already in the list. 00569 // Combine the specified days with those in the list. 00570 it->rDays |= _rDays; 00571 if (mParent) mParent->updated(); 00572 return; 00573 } 00574 } 00575 // Add the new position to the list 00576 rMonthPos *tmpPos = new rMonthPos; 00577 if (_rPos > 0) { 00578 tmpPos->rPos = _rPos; 00579 tmpPos->negative = false; 00580 } else { 00581 tmpPos->rPos = -_rPos; // take abs() 00582 tmpPos->negative = true; 00583 } 00584 tmpPos->rDays = _rDays; 00585 tmpPos->rDays.detach(); 00586 rMonthPositions.append(tmpPos); 00587 00588 if (mCompatVersion < 310 && mCompatDuration > 0) { 00589 // Backwards compatibility for KDE < 3.1. 00590 // rDuration was set to the number of time periods to recur. 00591 // Convert this to the number of occurrences. 00592 int monthsAhead = (mCompatDuration-1) * rFreq; 00593 int month = mRecurStart.date().month() - 1 + monthsAhead; 00594 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31); 00595 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly 00596 rDuration = recurCalc(COUNT_TO_DATE, end); 00597 } 00598 00599 if (mParent) mParent->updated(); 00600 } 00601 00602 void Recurrence::addMonthlyDay(short _rDay) 00603 { 00604 if (mRecurReadOnly || (recurs != rMonthlyDay && recurs != rYearlyMonth) 00605 || _rDay == 0 || _rDay > 31 || _rDay < -31) // invalid day number 00606 return; 00607 for (int* it = rMonthDays.first(); it; it = rMonthDays.next()) { 00608 if (_rDay == *it) 00609 return; // this day is already in the list - avoid duplication 00610 } 00611 mUseCachedEndDT = false; 00612 00613 int *tmpDay = new int; 00614 *tmpDay = _rDay; 00615 rMonthDays.append(tmpDay); 00616 00617 if (mCompatVersion < 310 && mCompatDuration > 0) { 00618 // Backwards compatibility for KDE < 3.1. 00619 // rDuration was set to the number of time periods to recur. 00620 // Convert this to the number of occurrences. 00621 int monthsAhead = (mCompatDuration-1) * rFreq; 00622 int month = mRecurStart.date().month() - 1 + monthsAhead; 00623 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31); 00624 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly 00625 rDuration = recurCalc(COUNT_TO_DATE, end); 00626 } 00627 00628 if (mParent) mParent->updated(); 00629 } 00630 00631 void Recurrence::setYearly(int type, int _rFreq, int _rDuration) 00632 { 00633 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 00634 return; 00635 if (mCompatVersion < 310) 00636 mCompatDuration = (_rDuration > 0) ? _rDuration : 0; 00637 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, _rDuration); 00638 } 00639 00640 void Recurrence::setYearly(int type, int _rFreq, const QDate &_rEndDate) 00641 { 00642 if (mRecurReadOnly) return; 00643 rEndDateTime.setDate(_rEndDate); 00644 rEndDateTime.setTime(mRecurStart.time()); 00645 mCompatDuration = 0; 00646 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, 0); 00647 } 00648 00649 void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, int _rDuration) 00650 { 00651 setYearlyByDate(0, type, _rFreq, _rDuration); 00652 } 00653 00654 void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, const QDate &_rEndDate) 00655 { 00656 setYearlyByDate(0, type, _rFreq, _rEndDate); 00657 } 00658 00659 void Recurrence::setYearlyByDate(int day, Feb29Type type, int _rFreq, int _rDuration) 00660 { 00661 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 00662 return; 00663 if (mCompatVersion < 310) 00664 mCompatDuration = (_rDuration > 0) ? _rDuration : 0; 00665 setYearly_(rYearlyMonth, type, _rFreq, _rDuration); 00666 if (day) 00667 addMonthlyDay(day); 00668 } 00669 00670 void Recurrence::setYearlyByDate(int day, Feb29Type type, int _rFreq, const QDate &_rEndDate) 00671 { 00672 if (mRecurReadOnly) return; 00673 rEndDateTime.setDate(_rEndDate); 00674 rEndDateTime.setTime(mRecurStart.time()); 00675 mCompatDuration = 0; 00676 setYearly_(rYearlyMonth, type, _rFreq, 0); 00677 if (day) 00678 addMonthlyDay(day); 00679 } 00680 00681 void Recurrence::addYearlyMonthPos(short _rPos, const QBitArray &_rDays) 00682 { 00683 if (recurs == rYearlyPos) 00684 addMonthlyPos_(_rPos, _rDays); 00685 } 00686 00687 const QPtrList<int> &Recurrence::yearNums() const 00688 { 00689 return rYearNums; 00690 } 00691 00692 void Recurrence::addYearlyNum(short _rNum) 00693 { 00694 if (mRecurReadOnly 00695 || (recurs != rYearlyMonth && recurs != rYearlyDay && recurs != rYearlyPos) 00696 || _rNum <= 0) // invalid day/month number 00697 return; 00698 00699 if (mCompatVersion < 310 && mCompatRecurs == rYearlyDay) { 00700 // Backwards compatibility for KDE < 3.1. 00701 // Dates were stored as day numbers, with a fiddle to take account of leap years. 00702 // Convert the day number to a month. 00703 if (_rNum <= 0 || _rNum > 366 || (_rNum == 366 && mRecurStart.date().daysInYear() < 366)) 00704 return; // invalid day number 00705 _rNum = QDate(mRecurStart.date().year(), 1, 1).addDays(_rNum - 1).month(); 00706 } else 00707 if ((recurs == rYearlyMonth || recurs == rYearlyPos) && _rNum > 12 00708 || recurs == rYearlyDay && _rNum > 366) 00709 return; // invalid day number 00710 00711 uint i = 0; 00712 for (int* it = rYearNums.first(); it && _rNum >= *it; it = rYearNums.next()) { 00713 if (_rNum == *it) 00714 return; // this day/month is already in the list - avoid duplication 00715 ++i; 00716 } 00717 mUseCachedEndDT = false; 00718 00719 int *tmpNum = new int; 00720 *tmpNum = _rNum; 00721 rYearNums.insert(i, tmpNum); // insert the day/month in a sorted position 00722 00723 if (mCompatVersion < 310 && mCompatDuration > 0) { 00724 // Backwards compatibility for KDE < 3.1. 00725 // rDuration was set to the number of time periods to recur. 00726 // Convert this to the number of occurrences. 00727 QDate end(mRecurStart.date().year() + (mCompatDuration-1)*rFreq, 12, 31); 00728 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly 00729 rDuration = recurCalc(COUNT_TO_DATE, end); 00730 } 00731 00732 if (mParent) mParent->updated(); 00733 } 00734 00735 00736 QValueList<QTime> Recurrence::recurTimesOn(const QDate &date) const 00737 { 00738 QValueList<QTime> times; 00739 switch (recurs) 00740 { 00741 case rMinutely: 00742 case rHourly: 00743 if ((date >= mRecurStart.date()) && 00744 ((rDuration > 0) && (date <= endDate()) || 00745 ((rDuration == 0) && (date <= rEndDateTime.date())) || 00746 (rDuration == -1))) { 00747 // The date queried falls within the range of the event. 00748 int secondFreq = rFreq * (recurs == rMinutely ? 60 : 3600); 00749 int after = mRecurStart.secsTo(QDateTime(date)) - 1; 00750 int count = (after + 24*3600) / secondFreq - after / secondFreq; 00751 if (count) { 00752 // It recurs at least once on the given date 00753 QTime t = mRecurStart.addSecs((after / secondFreq) * secondFreq).time(); 00754 while (--count >= 0) { 00755 t = t.addSecs(secondFreq); 00756 times.append(t); 00757 } 00758 } 00759 } 00760 break; 00761 case rDaily: 00762 case rWeekly: 00763 case rMonthlyPos: 00764 case rMonthlyDay: 00765 case rYearlyMonth: 00766 case rYearlyDay: 00767 case rYearlyPos: 00768 if (recursOnPure(date)) 00769 times.append(mRecurStart.time()); 00770 break; 00771 default: 00772 break; 00773 } 00774 return times; 00775 } 00776 00777 QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime, bool *last) const 00778 { 00779 int freq; 00780 switch (recurs) 00781 { 00782 case rMinutely: 00783 freq = rFreq * 60; 00784 break; 00785 case rHourly: 00786 freq = rFreq * 3600; 00787 break; 00788 case rDaily: 00789 case rWeekly: 00790 case rMonthlyPos: 00791 case rMonthlyDay: 00792 case rYearlyMonth: 00793 case rYearlyDay: 00794 case rYearlyPos: { 00795 QDate preDate = preDateTime.date(); 00796 if (!mFloats && mRecurStart.time() > preDateTime.time()) 00797 preDate = preDate.addDays(-1); 00798 return QDateTime(getNextDateNoTime(preDate, last), mRecurStart.time()); 00799 } 00800 default: 00801 return QDateTime(); 00802 } 00803 00804 // It's a sub-daily recurrence 00805 if (last) 00806 *last = false; 00807 if (preDateTime < mRecurStart) 00808 return mRecurStart; 00809 int count = mRecurStart.secsTo(preDateTime) / freq + 2; 00810 if (rDuration > 0) { 00811 if (count > rDuration) 00812 return QDateTime(); 00813 if (last && count == rDuration) 00814 *last = true; 00815 } 00816 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq); 00817 if (rDuration == 0) { 00818 if (endtime > rEndDateTime) 00819 return QDateTime(); 00820 if (last && endtime == rEndDateTime) 00821 *last = true; 00822 } 00823 return endtime; 00824 } 00825 00826 QDate Recurrence::getNextDate(const QDate &preDate, bool *last) const 00827 { 00828 switch (recurs) 00829 { 00830 case rMinutely: 00831 case rHourly: 00832 return getNextDateTime(QDateTime(preDate, QTime(23,59,59)), last).date(); 00833 case rDaily: 00834 case rWeekly: 00835 case rMonthlyPos: 00836 case rMonthlyDay: 00837 case rYearlyMonth: 00838 case rYearlyDay: 00839 case rYearlyPos: 00840 return getNextDateNoTime(preDate, last); 00841 default: 00842 return QDate(); 00843 } 00844 } 00845 00846 00847 QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime, bool *last) const 00848 { 00849 int freq; 00850 switch (recurs) 00851 { 00852 case rMinutely: 00853 freq = rFreq * 60; 00854 break; 00855 case rHourly: 00856 freq = rFreq * 3600; 00857 break; 00858 case rDaily: 00859 case rWeekly: 00860 case rMonthlyPos: 00861 case rMonthlyDay: 00862 case rYearlyMonth: 00863 case rYearlyDay: 00864 case rYearlyPos: { 00865 QDate afterDate = afterDateTime.date(); 00866 if (!mFloats && mRecurStart.time() < afterDateTime.time()) 00867 afterDate = afterDate.addDays(1); 00868 return QDateTime(getPreviousDateNoTime(afterDate, last), mRecurStart.time()); 00869 } 00870 default: 00871 return QDateTime(); 00872 } 00873 00874 // It's a sub-daily recurrence 00875 if (last) 00876 *last = false; 00877 if (afterDateTime <= mRecurStart) 00878 return QDateTime(); 00879 int count = (mRecurStart.secsTo(afterDateTime) - 1) / freq + 1; 00880 if (rDuration > 0) { 00881 if (count > rDuration) 00882 count = rDuration; 00883 if (last && count == rDuration) 00884 *last = true; 00885 } 00886 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq); 00887 if (rDuration == 0) { 00888 if (endtime > rEndDateTime) 00889 endtime = rEndDateTime; 00890 if (last && endtime == rEndDateTime) 00891 *last = true; 00892 } 00893 return endtime; 00894 } 00895 00896 QDate Recurrence::getPreviousDate(const QDate &afterDate, bool *last) const 00897 { 00898 switch (recurs) 00899 { 00900 case rMinutely: 00901 case rHourly: 00902 return getPreviousDateTime(QDateTime(afterDate, QTime(0,0,0)), last).date(); 00903 case rDaily: 00904 case rWeekly: 00905 case rMonthlyPos: 00906 case rMonthlyDay: 00907 case rYearlyMonth: 00908 case rYearlyDay: 00909 case rYearlyPos: 00910 return getPreviousDateNoTime(afterDate, last); 00911 default: 00912 return QDate(); 00913 } 00914 } 00915 00916 00917 /***************************** PROTECTED FUNCTIONS ***************************/ 00918 00919 bool Recurrence::recursSecondly(const QDate &qd, int secondFreq) const 00920 { 00921 if ((qd >= mRecurStart.date()) && 00922 ((rDuration > 0) && (qd <= endDate()) || 00923 ((rDuration == 0) && (qd <= rEndDateTime.date())) || 00924 (rDuration == -1))) { 00925 // The date queried falls within the range of the event. 00926 if (secondFreq < 24*3600) 00927 return true; // the event recurs at least once each day 00928 int after = mRecurStart.secsTo(QDateTime(qd)) - 1; 00929 if (after / secondFreq != (after + 24*3600) / secondFreq) 00930 return true; 00931 } 00932 return false; 00933 } 00934 00935 bool Recurrence::recursMinutelyAt(const QDateTime &dt, int minuteFreq) const 00936 { 00937 if ((dt >= mRecurStart) && 00938 ((rDuration > 0) && (dt <= endDateTime()) || 00939 ((rDuration == 0) && (dt <= rEndDateTime)) || 00940 (rDuration == -1))) { 00941 // The time queried falls within the range of the event. 00942 if (((mRecurStart.secsTo(dt) / 60) % minuteFreq) == 0) 00943 return true; 00944 } 00945 return false; 00946 } 00947 00948 bool Recurrence::recursDaily(const QDate &qd) const 00949 { 00950 QDate dStart = mRecurStart.date(); 00951 if ((dStart.daysTo(qd) % rFreq) == 0) { 00952 // The date is a day which recurs 00953 if (qd >= dStart 00954 && ((rDuration > 0 && qd <= endDate()) || 00955 (rDuration == 0 && qd <= rEndDateTime.date()) || 00956 rDuration == -1)) { 00957 // The date queried falls within the range of the event. 00958 return true; 00959 } 00960 } 00961 return false; 00962 } 00963 00964 bool Recurrence::recursWeekly(const QDate &qd) const 00965 { 00966 QDate dStart = mRecurStart.date(); 00967 if ((dStart.daysTo(qd)/7) % rFreq == 0) { 00968 // The date is in a week which recurs 00969 if (qd >= dStart 00970 && ((rDuration > 0 && qd <= endDate()) || 00971 (rDuration == 0 && qd <= rEndDateTime.date()) || 00972 rDuration == -1)) { 00973 // The date queried falls within the range of the event. 00974 // check if the bits set match today. 00975 int i = qd.dayOfWeek()-1; 00976 if (rDays.testBit((uint) i)) 00977 return true; 00978 } 00979 } 00980 return false; 00981 } 00982 00983 bool Recurrence::recursMonthly(const QDate &qd) const 00984 { 00985 QDate dStart = mRecurStart.date(); 00986 int year = qd.year(); 00987 int month = qd.month(); 00988 int day = qd.day(); 00989 // calculate how many months ahead this date is from the original 00990 // event's date 00991 int monthsAhead = (year - dStart.year()) * 12 + (month - dStart.month()); 00992 if ((monthsAhead % rFreq) == 0) { 00993 // The date is in a month which recurs 00994 if (qd >= dStart 00995 && ((rDuration > 0 && qd <= endDate()) || 00996 (rDuration == 0 && qd <= rEndDateTime.date()) || 00997 rDuration == -1)) { 00998 // The date queried falls within the range of the event. 00999 QValueList<int> days; 01000 int daysInMonth = qd.daysInMonth(); 01001 if (recurs == rMonthlyDay) 01002 getMonthlyDayDays(days, daysInMonth); 01003 else if (recurs == rMonthlyPos) 01004 getMonthlyPosDays(days, daysInMonth, QDate(year, month, 1).dayOfWeek()); 01005 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) { 01006 if (*it == day) 01007 return true; 01008 } 01009 // no dates matched 01010 } 01011 } 01012 return false; 01013 } 01014 01015 bool Recurrence::recursYearlyByMonth(const QDate &qd) const 01016 { 01017 QDate dStart = mRecurStart.date(); 01018 int startDay = dStart.day(); 01019 if (rMonthDays.count()) 01020 startDay = *rMonthDays.getFirst(); 01021 int qday = qd.day(); 01022 int qmonth = qd.month(); 01023 int qyear = qd.year(); 01024 bool match = (qday == startDay); 01025 if (startDay < 0) 01026 match = (qday == qd.daysInMonth() + startDay + 1); 01027 if (!match && startDay == 29 && dStart.month() == 2) { 01028 // It's a recurrence on February 29th 01029 switch (mFeb29YearlyType) { 01030 case rFeb28: 01031 if (qday == 28 && qmonth == 2 && !QDate::leapYear(qyear)) 01032 match = true; 01033 break; 01034 case rMar1: 01035 if (qday == 1 && qmonth == 3 && !QDate::leapYear(qyear)) { 01036 qmonth = 2; 01037 match = true; 01038 } 01039 break; 01040 case rFeb29: 01041 break; 01042 } 01043 } 01044 01045 if (match) { 01046 // The day of the month matches. Calculate how many years ahead 01047 // this date is from the original event's date. 01048 int yearsAhead = (qyear - dStart.year()); 01049 if (yearsAhead % rFreq == 0) { 01050 // The date is in a year which recurs 01051 if (qd >= dStart 01052 && ((rDuration > 0 && qd <= endDate()) || 01053 (rDuration == 0 && qd <= rEndDateTime.date()) || 01054 rDuration == -1)) { 01055 // The date queried falls within the range of the event. 01056 int i = qmonth; 01057 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { 01058 if (i == *qlin.current()) 01059 return true; 01060 } 01061 } 01062 } 01063 } 01064 return false; 01065 } 01066 01067 bool Recurrence::recursYearlyByPos(const QDate &qd) const 01068 { 01069 QDate dStart = mRecurStart.date(); 01070 int year = qd.year(); 01071 int month = qd.month(); 01072 int day = qd.day(); 01073 // calculate how many years ahead this date is from the original 01074 // event's date 01075 int yearsAhead = (year - dStart.year()); 01076 if (yearsAhead % rFreq == 0) { 01077 // The date is in a year which recurs 01078 if (qd >= dStart 01079 && ((rDuration > 0 && qd <= endDate()) || 01080 (rDuration == 0 && qd <= rEndDateTime.date()) || 01081 rDuration == -1)) { 01082 // The date queried falls within the range of the event. 01083 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { 01084 if (month == *qlin.current()) { 01085 // The month recurs 01086 QValueList<int> days; 01087 getMonthlyPosDays(days, qd.daysInMonth(), QDate(year, month, 1).dayOfWeek()); 01088 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) { 01089 if (*it == day) 01090 return true; 01091 } 01092 } 01093 } 01094 } 01095 } 01096 return false; 01097 } 01098 01099 bool Recurrence::recursYearlyByDay(const QDate &qd) const 01100 { 01101 QDate dStart = mRecurStart.date(); 01102 // calculate how many years ahead this date is from the original 01103 // event's date 01104 int yearsAhead = (qd.year() - dStart.year()); 01105 if (yearsAhead % rFreq == 0) { 01106 // The date is in a year which recurs 01107 if (qd >= dStart 01108 && ((rDuration > 0 && qd <= endDate()) || 01109 (rDuration == 0 && qd <= rEndDateTime.date()) || 01110 rDuration == -1)) { 01111 // The date queried falls within the range of the event. 01112 int i = qd.dayOfYear(); 01113 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { 01114 if (i == *qlin.current()) 01115 return true; 01116 } 01117 } 01118 } 01119 return false; 01120 } 01121 01122 /* Get the date of the next recurrence, after the specified date. 01123 * If 'last' is non-null, '*last' is set to true if the next recurrence is the 01124 * last recurrence, else false. 01125 * Reply = date of next recurrence, or invalid date if none. 01126 */ 01127 QDate Recurrence::getNextDateNoTime(const QDate &preDate, bool *last) const 01128 { 01129 if (last) 01130 *last = false; 01131 QDate dStart = mRecurStart.date(); 01132 if (preDate < dStart) 01133 return dStart; 01134 QDate earliestDate = preDate.addDays(1); 01135 QDate nextDate; 01136 01137 switch (recurs) { 01138 case rDaily: 01139 nextDate = dStart.addDays((dStart.daysTo(preDate)/rFreq + 1) * rFreq); 01140 break; 01141 01142 case rWeekly: { 01143 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart 01144 int earliestDayOfWeek = earliestDate.dayOfWeek(); 01145 int weeksAhead = start.daysTo(earliestDate) / 7; 01146 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week 01147 weeksAhead -= notThisWeek; // latest week which recurred 01148 int weekday = 0; 01149 // First check for any remaining day this week, if this week is a recurring week 01150 if (!notThisWeek) 01151 weekday = getFirstDayInWeek(earliestDayOfWeek); 01152 // Check for a day in the next scheduled week 01153 if (!weekday && earliestDayOfWeek > 1) 01154 weekday = getFirstDayInWeek(rWeekStart) + rFreq*7; 01155 if (weekday) 01156 nextDate = start.addDays(weeksAhead*7 + weekday - 1); 01157 break; 01158 } 01159 case rMonthlyDay: 01160 case rMonthlyPos: { 01161 int startYear = dStart.year(); 01162 int startMonth = dStart.month(); // 1..12 01163 int earliestYear = earliestDate.year(); 01164 int monthsAhead = (earliestYear - startYear)*12 + earliestDate.month() - startMonth; 01165 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month 01166 monthsAhead -= notThisMonth; // latest month which recurred 01167 // Check for the first later day in the current month 01168 if (!notThisMonth) 01169 nextDate = getFirstDateInMonth(earliestDate); 01170 if (!nextDate.isValid() && earliestDate.day() > 1) { 01171 // Check for a day in the next scheduled month 01172 int months = startMonth - 1 + monthsAhead + rFreq; 01173 nextDate = getFirstDateInMonth(QDate(startYear + months/12, months%12 + 1, 1)); 01174 } 01175 break; 01176 } 01177 case rYearlyMonth: 01178 case rYearlyPos: 01179 case rYearlyDay: { 01180 int startYear = dStart.year(); 01181 int yearsAhead = earliestDate.year() - startYear; 01182 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year 01183 yearsAhead -= notThisYear; // latest year which recurred 01184 // Check for the first later date in the current year 01185 if (!notThisYear) 01186 nextDate = getFirstDateInYear(earliestDate); 01187 // Check for a date in the next scheduled year 01188 if (!nextDate.isValid()) { 01189 startYear += yearsAhead + rFreq; 01190 // The only valid reason for failure after the next check is that it only recurs 01191 // in a leap year. Since certain leap year recurrences could potentially never 01192 // actually occur (e.g. recurring on the 366th day of the year every 4 years, 01193 // starting in a non-leap year), limit the iterations to 8 (to allow for possible 01194 // century non-leap years). 01195 for (int i = 0; i < 8; ++i) { 01196 nextDate = getFirstDateInYear(QDate(startYear, 1, 1)); 01197 if (nextDate.isValid()) 01198 break; 01199 startYear += rFreq; 01200 } 01201 } 01202 break; 01203 } 01204 case rNone: 01205 default: 01206 return QDate(); 01207 } 01208 01209 if (rDuration >= 0 && nextDate.isValid()) { 01210 // Check that the date found is within the range of the recurrence 01211 QDate end = endDate(); 01212 if (nextDate > end) 01213 return QDate(); 01214 if (last && nextDate == end) 01215 *last = true; 01216 } 01217 return nextDate; 01218 } 01219 01220 /* Get the date of the last previous recurrence, before the specified date. 01221 * Reply = date of previous recurrence, or invalid date if none. 01222 */ 01223 QDate Recurrence::getPreviousDateNoTime(const QDate &afterDate, bool *last) const 01224 { 01225 if (last) 01226 *last = false; 01227 QDate dStart = mRecurStart.date(); 01228 QDate latestDate = afterDate.addDays(-1); 01229 if (latestDate < dStart) 01230 return QDate(); 01231 QDate prevDate; 01232 01233 switch (recurs) { 01234 case rDaily: 01235 prevDate = dStart.addDays((dStart.daysTo(latestDate) / rFreq) * rFreq); 01236 break; 01237 01238 case rWeekly: { 01239 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart 01240 int latestDayOfWeek = latestDate.dayOfWeek(); 01241 int weeksAhead = start.daysTo(latestDate) / 7; 01242 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week 01243 weeksAhead -= notThisWeek; // latest week which recurred 01244 int weekday = 0; 01245 // First check for any previous day this week, if this week is a recurring week 01246 if (!notThisWeek) 01247 weekday = getLastDayInWeek(latestDayOfWeek); 01248 // Check for a day in the previous scheduled week 01249 if (!weekday) { 01250 int weekEnd = (rWeekStart + 5)%7 + 1; 01251 if (latestDayOfWeek < weekEnd) { 01252 if (!notThisWeek) 01253 weeksAhead -= rFreq; 01254 weekday = getLastDayInWeek(weekEnd); 01255 } 01256 } 01257 if (weekday) 01258 prevDate = start.addDays(weeksAhead*7 + weekday - 1); 01259 break; 01260 } 01261 case rMonthlyDay: 01262 case rMonthlyPos: { 01263 int startYear = dStart.year(); 01264 int startMonth = dStart.month(); // 1..12 01265 int latestYear = latestDate.year(); 01266 int monthsAhead = (latestYear - startYear)*12 + latestDate.month() - startMonth; 01267 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month 01268 monthsAhead -= notThisMonth; // latest month which recurred 01269 // Check for the last earlier day in the current month 01270 if (!notThisMonth) 01271 prevDate = getLastDateInMonth(latestDate); 01272 if (!prevDate.isValid() && latestDate.day() < latestDate.daysInMonth()) { 01273 // Check for a day in the previous scheduled month 01274 if (!notThisMonth) 01275 monthsAhead -= rFreq; 01276 int months = startMonth + monthsAhead; // get the month after the one that recurs 01277 prevDate = getLastDateInMonth(QDate(startYear + months/12, months%12 + 1, 1).addDays(-1)); 01278 } 01279 break; 01280 } 01281 case rYearlyMonth: 01282 case rYearlyPos: 01283 case rYearlyDay: { 01284 int startYear = dStart.year(); 01285 int yearsAhead = latestDate.year() - startYear; 01286 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year 01287 yearsAhead -= notThisYear; // latest year which recurred 01288 // Check for the first later date in the current year 01289 if (!notThisYear) 01290 prevDate = getLastDateInYear(latestDate); 01291 if (!prevDate.isValid() && latestDate.dayOfYear() < latestDate.daysInYear()) { 01292 // Check for a date in the next scheduled year 01293 if (!notThisYear) 01294 yearsAhead -= rFreq; 01295 prevDate = getLastDateInYear(QDate(startYear + yearsAhead, 12, 31)); 01296 } 01297 break; 01298 } 01299 case rNone: 01300 default: 01301 return QDate(); 01302 } 01303 01304 if (prevDate.isValid()) { 01305 // Check that the date found is within the range of the recurrence 01306 if (prevDate < dStart) 01307 return QDate(); 01308 if (rDuration >= 0) { 01309 QDate end = endDate(); 01310 if (prevDate >= end) { 01311 if (last) 01312 *last = true; 01313 return end; 01314 } 01315 } 01316 } 01317 return prevDate; 01318 } 01319 01320 void Recurrence::setDailySub(short type, int freq, int duration) 01321 { 01322 mUseCachedEndDT = false; 01323 recurs = type; 01324 rFreq = freq; 01325 rDuration = duration; 01326 rMonthPositions.clear(); 01327 rMonthDays.clear(); 01328 rYearNums.clear(); 01329 if (type != rDaily) 01330 mFloats = false; // sub-daily types can't be floating 01331 01332 if (mParent) mParent->updated(); 01333 } 01334 01335 void Recurrence::setYearly_(short type, Feb29Type feb29type, int freq, int duration) 01336 { 01337 mUseCachedEndDT = false; 01338 recurs = type; 01339 if (mCompatVersion < 310 && type == rYearlyDay) { 01340 mCompatRecurs = rYearlyDay; 01341 recurs = rYearlyMonth; // convert old yearly-by-day to yearly-by-month 01342 feb29type = rMar1; // retain the same day number in the year 01343 } 01344 01345 mFeb29YearlyType = feb29type; 01346 rFreq = freq; 01347 rDuration = duration; 01348 if (type != rYearlyPos) 01349 rMonthPositions.clear(); 01350 rMonthDays.clear(); 01351 if (mParent) mParent->updated(); 01352 } 01353 01354 int Recurrence::recurCalc(PeriodFunc func, QDateTime &endtime) const 01355 { 01356 QDate enddate = endtime.date(); 01357 switch (func) { 01358 case END_DATE_AND_COUNT: 01359 if (rDuration < 0) { 01360 endtime = QDateTime(); 01361 return 0; // infinite recurrence 01362 } 01363 if (rDuration == 0) { 01364 endtime = rEndDateTime; 01365 func = COUNT_TO_DATE; 01366 } 01367 break; 01368 case COUNT_TO_DATE: 01369 // Count recurrences up to and including the specified date/time. 01370 if (endtime < mRecurStart) 01371 return 0; 01372 if (rDuration == 0 && endtime > rEndDateTime) 01373 enddate = rEndDateTime.date(); 01374 else if (!mFloats && mRecurStart.time() > endtime.time()) 01375 enddate = enddate.addDays(-1); 01376 break; 01377 case NEXT_AFTER_DATE: 01378 // Find next recurrence AFTER endtime 01379 if (endtime < mRecurStart) { 01380 endtime = mRecurStart; 01381 return 1; 01382 } 01383 if (rDuration == 0 && endtime >= rEndDateTime) { 01384 endtime = QDateTime(); 01385 return 0; 01386 } 01387 if (!mFloats && mRecurStart.time() > endtime.time()) 01388 enddate = enddate.addDays(-1); 01389 break; 01390 default: 01391 endtime = QDateTime(); 01392 return 0; 01393 } 01394 01395 int count = 0; // default = error 01396 bool timed = false; 01397 switch (recurs) { 01398 case rMinutely: 01399 timed = true; 01400 count = secondlyCalc(func, endtime, rFreq*60); 01401 break; 01402 case rHourly: 01403 timed = true; 01404 count = secondlyCalc(func, endtime, rFreq*3600); 01405 break; 01406 case rDaily: 01407 count = dailyCalc(func, enddate); 01408 break; 01409 case rWeekly: 01410 count = weeklyCalc(func, enddate); 01411 break; 01412 case rMonthlyPos: 01413 case rMonthlyDay: 01414 count = monthlyCalc(func, enddate); 01415 break; 01416 case rYearlyMonth: 01417 count = yearlyMonthCalc(func, enddate); 01418 break; 01419 case rYearlyPos: 01420 count = yearlyPosCalc(func, enddate); 01421 break; 01422 case rYearlyDay: 01423 count = yearlyDayCalc(func, enddate); 01424 break; 01425 default: 01426 break; 01427 } 01428 01429 switch (func) { 01430 case END_DATE_AND_COUNT: 01431 case NEXT_AFTER_DATE: 01432 if (count == 0) 01433 endtime = QDateTime(); 01434 else if (!timed) { 01435 endtime.setDate(enddate); 01436 endtime.setTime(mRecurStart.time()); 01437 } 01438 break; 01439 case COUNT_TO_DATE: 01440 break; 01441 } 01442 return count; 01443 } 01444 01445 int Recurrence::recurCalc(PeriodFunc func, QDate &enddate) const 01446 { 01447 QDateTime endtime(enddate, QTime(23,59,59)); 01448 switch (func) { 01449 case END_DATE_AND_COUNT: 01450 if (rDuration < 0) { 01451 enddate = QDate(); 01452 return 0; // infinite recurrence 01453 } 01454 if (rDuration == 0) { 01455 enddate = rEndDateTime.date(); 01456 func = COUNT_TO_DATE; 01457 } 01458 break; 01459 case COUNT_TO_DATE: 01460 // Count recurrences up to and including the specified date. 01461 if (enddate < mRecurStart.date()) 01462 return 0; 01463 if (rDuration == 0 && enddate > rEndDateTime.date()) { 01464 enddate = rEndDateTime.date(); 01465 endtime.setDate(enddate); 01466 } 01467 break; 01468 case NEXT_AFTER_DATE: 01469 if (enddate < mRecurStart.date()) { 01470 enddate = mRecurStart.date(); 01471 return 1; 01472 } 01473 if (rDuration == 0 && enddate >= rEndDateTime.date()) { 01474 enddate = QDate(); 01475 return 0; 01476 } 01477 break; 01478 default: 01479 enddate = QDate(); 01480 return 0; 01481 } 01482 01483 int count = 0; // default = error 01484 bool timed = false; 01485 switch (recurs) { 01486 case rMinutely: 01487 timed = true; 01488 count = secondlyCalc(func, endtime, rFreq*60); 01489 break; 01490 case rHourly: 01491 timed = true; 01492 count = secondlyCalc(func, endtime, rFreq*3600); 01493 break; 01494 case rDaily: 01495 count = dailyCalc(func, enddate); 01496 break; 01497 case rWeekly: 01498 count = weeklyCalc(func, enddate); 01499 break; 01500 case rMonthlyPos: 01501 case rMonthlyDay: 01502 count = monthlyCalc(func, enddate); 01503 break; 01504 case rYearlyMonth: 01505 count = yearlyMonthCalc(func, enddate); 01506 break; 01507 case rYearlyPos: 01508 count = yearlyPosCalc(func, enddate); 01509 break; 01510 case rYearlyDay: 01511 count = yearlyDayCalc(func, enddate); 01512 break; 01513 default: 01514 break; 01515 } 01516 01517 switch (func) { 01518 case END_DATE_AND_COUNT: 01519 case NEXT_AFTER_DATE: 01520 if (count == 0) 01521 endtime = QDate(); 01522 else if (timed) 01523 enddate = endtime.date(); 01524 break; 01525 case COUNT_TO_DATE: 01526 break; 01527 } 01528 return count; 01529 } 01530 01531 /* Find count and, depending on 'func', the end date/time of a secondly recurrence. 01532 * Reply = total number of occurrences up to 'endtime', or 0 if error. 01533 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'endtime' is updated to the 01534 * recurrence end date/time. 01535 */ 01536 int Recurrence::secondlyCalc(PeriodFunc func, QDateTime &endtime, int freq) const 01537 { 01538 switch (func) { 01539 case END_DATE_AND_COUNT: 01540 endtime = mRecurStart.addSecs((rDuration - 1) * freq); 01541 return rDuration; 01542 case COUNT_TO_DATE: { 01543 int n = mRecurStart.secsTo(endtime)/freq + 1; 01544 if (rDuration > 0 && n > rDuration) 01545 return rDuration; 01546 return n; 01547 } 01548 case NEXT_AFTER_DATE: { 01549 int count = mRecurStart.secsTo(endtime) / freq + 2; 01550 if (rDuration > 0 && count > rDuration) 01551 return 0; 01552 endtime = mRecurStart.addSecs((count - 1)*freq); 01553 return count; 01554 } 01555 } 01556 return 0; 01557 } 01558 01559 /* Find count and, depending on 'func', the end date of a daily recurrence. 01560 * Reply = total number of occurrences up to 'enddate', or 0 if error. 01561 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 01562 * recurrence end date. 01563 */ 01564 int Recurrence::dailyCalc(PeriodFunc func, QDate &enddate) const 01565 { 01566 QDate dStart = mRecurStart.date(); 01567 switch (func) { 01568 case END_DATE_AND_COUNT: 01569 enddate = dStart.addDays((rDuration - 1) * rFreq); 01570 return rDuration; 01571 case COUNT_TO_DATE: { 01572 int n = dStart.daysTo(enddate)/rFreq + 1; 01573 if (rDuration > 0 && n > rDuration) 01574 return rDuration; 01575 return n; 01576 } 01577 case NEXT_AFTER_DATE: { 01578 int count = dStart.daysTo(enddate) / rFreq + 2; 01579 if (rDuration > 0 && count > rDuration) 01580 return 0; 01581 enddate = dStart.addDays((count - 1)*rFreq); 01582 return count; 01583 } 01584 } 01585 return 0; 01586 } 01587 01588 /* Find count and, depending on 'func', the end date of a weekly recurrence. 01589 * Reply = total number of occurrences up to 'enddate', or 0 if error. 01590 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 01591 * recurrence end date. 01592 */ 01593 int Recurrence::weeklyCalc(PeriodFunc func, QDate &enddate) const 01594 { 01595 int daysPerWeek = 0; 01596 for (int i = 0; i < 7; ++i) { 01597 if (rDays.testBit((uint)i)) 01598 ++daysPerWeek; 01599 } 01600 if (!daysPerWeek) 01601 return 0; // there are no days to recur on 01602 01603 switch (func) { 01604 case END_DATE_AND_COUNT: 01605 return weeklyCalcEndDate(enddate, daysPerWeek); 01606 case COUNT_TO_DATE: 01607 return weeklyCalcToDate(enddate, daysPerWeek); 01608 case NEXT_AFTER_DATE: 01609 return weeklyCalcNextAfter(enddate, daysPerWeek); 01610 } 01611 return 0; 01612 } 01613 01614 int Recurrence::weeklyCalcEndDate(QDate &enddate, int daysPerWeek) const 01615 { 01616 int startDayOfWeek = mRecurStart.date().dayOfWeek(); // 1..7 01617 int countGone = 0; 01618 int daysGone = 0; 01619 uint countTogo = rDuration; 01620 if (startDayOfWeek != rWeekStart) { 01621 // Check what remains of the start week 01622 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { 01623 ++daysGone; 01624 if (rDays.testBit((uint)i)) { 01625 ++countGone; 01626 if (--countTogo == 0) 01627 break; 01628 } 01629 } 01630 daysGone += 7 * (rFreq - 1); 01631 } 01632 if (countTogo) { 01633 // Skip the remaining whole weeks 01634 // Leave at least 1 recurrence remaining, in order to get its date 01635 int wholeWeeks = (countTogo - 1) / daysPerWeek; 01636 daysGone += wholeWeeks * 7 * rFreq; 01637 countGone += wholeWeeks * daysPerWeek; 01638 countTogo -= wholeWeeks * daysPerWeek; 01639 // Check the last week in the recurrence 01640 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { 01641 ++daysGone; 01642 if (rDays.testBit((uint)i)) { 01643 ++countGone; 01644 if (--countTogo == 0) 01645 break; 01646 } 01647 } 01648 } 01649 enddate = mRecurStart.date().addDays(daysGone); 01650 return countGone; 01651 } 01652 01653 int Recurrence::weeklyCalcToDate(const QDate &enddate, int daysPerWeek) const 01654 { 01655 QDate dStart = mRecurStart.date(); 01656 int startDayOfWeek = dStart.dayOfWeek(); // 1..7 01657 int countGone = 0; 01658 int daysGone = 0; 01659 int totalDays = dStart.daysTo(enddate) + 1; 01660 int countMax = (rDuration > 0) ? rDuration : INT_MAX; 01661 01662 if (startDayOfWeek != rWeekStart) { 01663 // Check what remains of the start week 01664 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { 01665 if (rDays.testBit((uint)i)) { 01666 if (++countGone >= countMax) 01667 return countMax; 01668 } 01669 if (++daysGone == totalDays) 01670 return countGone; 01671 } 01672 daysGone += 7 * (rFreq - 1); 01673 if (daysGone >= totalDays) 01674 return countGone; 01675 } 01676 // Skip the remaining whole weeks 01677 int wholeWeeks = (totalDays - daysGone) / 7; 01678 countGone += (wholeWeeks / rFreq) * daysPerWeek; 01679 if (countGone >= countMax) 01680 return countMax; 01681 daysGone += wholeWeeks * 7; 01682 if (daysGone >= totalDays // have we reached the end date? 01683 || wholeWeeks % rFreq) // is end week a recurrence week? 01684 return countGone; 01685 01686 // Check the last week in the recurrence 01687 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { 01688 if (rDays.testBit((uint)i)) { 01689 if (++countGone >= countMax) 01690 return countMax; 01691 } 01692 if (++daysGone == totalDays) 01693 return countGone; 01694 } 01695 return countGone; 01696 } 01697 01698 int Recurrence::weeklyCalcNextAfter(QDate &enddate, int daysPerWeek) const 01699 { 01700 QDate dStart = mRecurStart.date(); 01701 int startDayOfWeek = dStart.dayOfWeek(); // 1..7 01702 int totalDays = dStart.daysTo(enddate) + 1; 01703 uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX; 01704 int countGone = 0; 01705 int daysGone = 0; 01706 int recurWeeks; 01707 01708 if (startDayOfWeek != rWeekStart) { 01709 // Check what remains of the start week 01710 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { 01711 ++daysGone; 01712 if (rDays.testBit((uint)i)) { 01713 ++countGone; 01714 if (daysGone > totalDays) 01715 goto ex; 01716 if (--countTogo == 0) 01717 return 0; 01718 } 01719 } 01720 daysGone += 7 * (rFreq - 1); 01721 } 01722 01723 // Skip the remaining whole weeks 01724 recurWeeks = (totalDays - daysGone) / (7 * rFreq); 01725 if (recurWeeks) { 01726 int n = recurWeeks * daysPerWeek; 01727 if (static_cast<uint>(n) > countTogo) 01728 return 0; // reached end of recurrence 01729 countGone += n; 01730 countTogo -= n; 01731 daysGone += recurWeeks * 7 * rFreq; 01732 } 01733 01734 // Check the last week or two in the recurrence 01735 for ( ; ; ) { 01736 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { 01737 ++daysGone; 01738 if (rDays.testBit((uint)i)) { 01739 ++countGone; 01740 if (daysGone > totalDays) 01741 goto ex; 01742 if (--countTogo == 0) 01743 return 0; 01744 } 01745 } 01746 daysGone += 7 * (rFreq - 1); 01747 } 01748 ex: 01749 enddate = dStart.addDays(daysGone); 01750 return countGone; 01751 } 01752 01753 /* Find count and, depending on 'func', the end date of a monthly recurrence. 01754 * Reply = total number of occurrences up to 'enddate', or 0 if error. 01755 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 01756 * recurrence end date. 01757 */ 01758 class Recurrence::MonthlyData 01759 { 01760 public: 01761 const Recurrence *recurrence; 01762 int year; // current year 01763 int month; // current month 0..11 01764 int day; // current day of month 1..31 01765 bool varies; // true if recurring days vary between different months 01766 01767 private: 01768 QValueList<int> days28, days29, days30, days31; // recurring days in months of each length 01769 QValueList<int> *recurDays[4]; 01770 01771 public: 01772 MonthlyData(const Recurrence* r, const QDate &date) 01773 : recurrence(r), year(date.year()), month(date.month()-1), day(date.day()) 01774 { recurDays[0] = &days28; 01775 recurDays[1] = &days29; 01776 recurDays[2] = &days30; 01777 recurDays[3] = &days31; 01778 varies = (recurrence->doesRecur() == rMonthlyPos) 01779 ? true : recurrence->getMonthlyDayDays(days31, 31); 01780 } 01781 const QValueList<int>* dayList() const { 01782 if (!varies) 01783 return &days31; 01784 QDate startOfMonth(year, month + 1, 1); 01785 int daysInMonth = startOfMonth.daysInMonth(); 01786 QValueList<int>* days = recurDays[daysInMonth - 28]; 01787 if (recurrence->doesRecur() == rMonthlyPos) 01788 recurrence->getMonthlyPosDays(*days, daysInMonth, startOfMonth.dayOfWeek()); 01789 else if (days->isEmpty()) 01790 recurrence->getMonthlyDayDays(*days, daysInMonth); 01791 return days; 01792 } 01793 int yearMonth() const { return year*12 + month; } 01794 void addMonths(int diff) { month += diff; year += month / 12; month %= 12; } 01795 QDate date() const { return QDate(year, month + 1, day); } 01796 }; 01797 01798 int Recurrence::monthlyCalc(PeriodFunc func, QDate &enddate) const 01799 { 01800 if (recurs == rMonthlyPos && rMonthPositions.isEmpty() 01801 || recurs == rMonthlyDay && rMonthDays.isEmpty()) 01802 return 0; 01803 01804 MonthlyData data(this, mRecurStart.date()); 01805 switch (func) { 01806 case END_DATE_AND_COUNT: 01807 return monthlyCalcEndDate(enddate, data); 01808 case COUNT_TO_DATE: 01809 return monthlyCalcToDate(enddate, data); 01810 case NEXT_AFTER_DATE: 01811 return monthlyCalcNextAfter(enddate, data); 01812 } 01813 return 0; 01814 } 01815 01816 int Recurrence::monthlyCalcEndDate(QDate &enddate, MonthlyData &data) const 01817 { 01818 uint countTogo = rDuration; 01819 int countGone = 0; 01820 QValueList<int>::ConstIterator it; 01821 const QValueList<int>* days = data.dayList(); 01822 01823 if (data.day > 1) { 01824 // Check what remains of the start month 01825 for (it = days->begin(); it != days->end(); ++it) { 01826 if (*it >= data.day) { 01827 ++countGone; 01828 if (--countTogo == 0) { 01829 data.day = *it; 01830 break; 01831 } 01832 } 01833 } 01834 if (countTogo) { 01835 data.day = 1; 01836 data.addMonths(rFreq); 01837 } 01838 } 01839 if (countTogo) { 01840 if (data.varies) { 01841 // The number of recurrence days varies from month to month, 01842 // so we need to check month by month. 01843 for ( ; ; ) { 01844 days = data.dayList(); 01845 uint n = days->count(); // number of recurrence days in this month 01846 if (n >= countTogo) 01847 break; 01848 countTogo -= n; 01849 countGone += n; 01850 data.addMonths(rFreq); 01851 } 01852 } else { 01853 // The number of recurrences is the same every month, 01854 // so skip the month-by-month check. 01855 // Skip the remaining whole months, but leave at least 01856 // 1 recurrence remaining, in order to get its date. 01857 int daysPerMonth = days->count(); 01858 int wholeMonths = (countTogo - 1) / daysPerMonth; 01859 data.addMonths(wholeMonths * rFreq); 01860 countGone += wholeMonths * daysPerMonth; 01861 countTogo -= wholeMonths * daysPerMonth; 01862 } 01863 if (countTogo) { 01864 // Check the last month in the recurrence 01865 for (it = days->begin(); it != days->end(); ++it) { 01866 ++countGone; 01867 if (--countTogo == 0) { 01868 data.day = *it; 01869 break; 01870 } 01871 } 01872 } 01873 } 01874 enddate = data.date(); 01875 return countGone; 01876 } 01877 01878 int Recurrence::monthlyCalcToDate(const QDate &enddate, MonthlyData &data) const 01879 { 01880 int countGone = 0; 01881 int countMax = (rDuration > 0) ? rDuration : INT_MAX; 01882 int endYear = enddate.year(); 01883 int endMonth = enddate.month() - 1; // zero-based 01884 int endDay = enddate.day(); 01885 int endYearMonth = endYear*12 + endMonth; 01886 QValueList<int>::ConstIterator it; 01887 const QValueList<int>* days = data.dayList(); 01888 01889 if (data.day > 1) { 01890 // Check what remains of the start month 01891 for (it = days->begin(); it != days->end(); ++it) { 01892 if (*it >= data.day) { 01893 if (data.yearMonth() == endYearMonth && *it > endDay) 01894 return countGone; 01895 if (++countGone >= countMax) 01896 return countMax; 01897 } 01898 } 01899 data.day = 1; 01900 data.addMonths(rFreq); 01901 } 01902 01903 if (data.varies) { 01904 // The number of recurrence days varies from month to month, 01905 // so we need to check month by month. 01906 while (data.yearMonth() < endYearMonth) { 01907 countGone += data.dayList()->count(); 01908 if (countGone >= countMax) 01909 return countMax; 01910 data.addMonths(rFreq); 01911 } 01912 days = data.dayList(); 01913 } else { 01914 // The number of recurrences is the same every month, 01915 // so skip the month-by-month check. 01916 // Skip the remaining whole months. 01917 int daysPerMonth = days->count(); 01918 int wholeMonths = endYearMonth - data.yearMonth(); 01919 countGone += (wholeMonths / rFreq) * daysPerMonth; 01920 if (countGone >= countMax) 01921 return countMax; 01922 if (wholeMonths % rFreq) 01923 return countGone; // end year isn't a recurrence year 01924 data.year = endYear; 01925 data.month = endMonth; 01926 } 01927 01928 // Check the last month in the recurrence 01929 for (it = days->begin(); it != days->end(); ++it) { 01930 if (*it > endDay) 01931 return countGone; 01932 if (++countGone >= countMax) 01933 return countMax; 01934 } 01935 return countGone; 01936 } 01937 01938 int Recurrence::monthlyCalcNextAfter(QDate &enddate, MonthlyData &data) const 01939 { 01940 uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX; 01941 int countGone = 0; 01942 int endYear = enddate.year(); 01943 int endDay = enddate.day(); 01944 int endYearMonth = endYear*12 + enddate.month() - 1; 01945 QValueList<int>::ConstIterator it; 01946 const QValueList<int>* days = data.dayList(); 01947 01948 if (data.day > 1) { 01949 // Check what remains of the start month 01950 for (it = days->begin(); it != days->end(); ++it) { 01951 if (*it >= data.day) { 01952 ++countGone; 01953 if (data.yearMonth() == endYearMonth && *it > endDay) { 01954 data.day = *it; 01955 goto ex; 01956 } 01957 if (--countTogo == 0) 01958 return 0; 01959 } 01960 } 01961 data.day = 1; 01962 data.addMonths(rFreq); 01963 } 01964 01965 if (data.varies) { 01966 // The number of recurrence days varies from month to month, 01967 // so we need to check month by month. 01968 while (data.yearMonth() <= endYearMonth) { 01969 days = data.dayList(); 01970 uint n = days->count(); // number of recurrence days in this month 01971 if (data.yearMonth() == endYearMonth && days->last() > endDay) 01972 break; 01973 if (n >= countTogo) 01974 return 0; 01975 countGone += n; 01976 countTogo -= n; 01977 data.addMonths(rFreq); 01978 } 01979 days = data.dayList(); 01980 } else { 01981 // The number of recurrences is the same every month, 01982 // so skip the month-by-month check. 01983 // Skip the remaining whole months to at least end year/month. 01984 int daysPerMonth = days->count(); 01985 int elapsed = endYearMonth - data.yearMonth(); 01986 int recurMonths = (elapsed + rFreq - 1) / rFreq; 01987 if (elapsed % rFreq == 0 && days->last() <= endDay) 01988 ++recurMonths; // required month is after endYearMonth 01989 if (recurMonths) { 01990 int n = recurMonths * daysPerMonth; 01991 if (static_cast<uint>(n) > countTogo) 01992 return 0; // reached end of recurrence 01993 countTogo -= n; 01994 countGone += n; 01995 data.addMonths(recurMonths * rFreq); 01996 } 01997 } 01998 01999 // Check the last month in the recurrence 02000 for (it = days->begin(); it != days->end(); ++it) { 02001 ++countGone; 02002 if (data.yearMonth() > endYearMonth || *it > endDay) { 02003 data.day = *it; 02004 break; 02005 } 02006 if (--countTogo == 0) 02007 return 0; 02008 } 02009 ex: 02010 enddate = data.date(); 02011 return countGone; 02012 } 02013 02014 02015 /* Find count and, depending on 'func', the end date of an annual recurrence by date. 02016 * Reply = total number of occurrences up to 'enddate', or 0 if error. 02017 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 02018 * recurrence end date. 02019 * 02020 * WARNING: These methods currently do not cater for day of month < -28 02021 * (which would need different months to be treated differently). 02022 */ 02023 class Recurrence::YearlyMonthData 02024 { 02025 public: 02026 const Recurrence *recurrence; 02027 int year; // current year 02028 int month; // current month 1..12 02029 int day; // current day of month 1..31 02030 bool leapyear; // true if February 29th recurs and current year is a leap year 02031 bool feb29; // true if February 29th recurs 02032 02033 private: 02034 QValueList<int> months; // recurring months in non-leap years 1..12 02035 QValueList<int> leapMonths; // recurring months in leap years 1..12 02036 02037 public: 02038 YearlyMonthData(const Recurrence* r, const QDate &date, int d) 02039 : recurrence(r), year(date.year()), month(date.month()), day(d ? d : date.day()) 02040 { feb29 = recurrence->getYearlyMonthMonths(day, months, leapMonths); 02041 leapyear = feb29 && QDate::leapYear(year); 02042 } 02043 const QValueList<int>* monthList() const 02044 { return leapyear ? &leapMonths : &months; } 02045 const QValueList<int>* leapMonthList() const { return &leapMonths; } 02046 QDate date() const { if (day > 0) return QDate(year, month, day); 02047 return QDate(year, month, QDate(year, month, 1).daysInMonth() + day + 1); 02048 } 02049 }; 02050 02051 int Recurrence::yearlyMonthCalc(PeriodFunc func, QDate &enddate) const 02052 { 02053 if (rYearNums.isEmpty()) 02054 return 0; 02055 YearlyMonthData data(this, mRecurStart.date(), (rMonthDays.count() ? *rMonthDays.getFirst() : 0)); 02056 switch (func) { 02057 case END_DATE_AND_COUNT: 02058 return yearlyMonthCalcEndDate(enddate, data); 02059 case COUNT_TO_DATE: 02060 return yearlyMonthCalcToDate(enddate, data); 02061 case NEXT_AFTER_DATE: 02062 return yearlyMonthCalcNextAfter(enddate, data); 02063 } 02064 return 0; 02065 } 02066 02067 // Find total count and end date of an annual recurrence by date. 02068 // Reply = total number of occurrences. 02069 int Recurrence::yearlyMonthCalcEndDate(QDate &enddate, YearlyMonthData &data) const 02070 { 02071 uint countTogo = rDuration; 02072 int countGone = 0; 02073 QValueList<int>::ConstIterator it; 02074 const QValueList<int>* mons = data.monthList(); // get recurring months for this year 02075 02076 if (data.month > 1) { 02077 // Check what remains of the start year 02078 for (it = mons->begin(); it != mons->end(); ++it) { 02079 if (*it >= data.month) { 02080 ++countGone; 02081 if (--countTogo == 0) { 02082 data.month = *it; 02083 if (data.month == 2 && data.feb29 && !data.leapyear) { 02084 // The recurrence should end on February 29th, but it's a non-leap year 02085 switch (mFeb29YearlyType) { 02086 case rFeb28: 02087 data.day = 28; 02088 break; 02089 case rMar1: 02090 data.month = 3; 02091 data.day = 1; 02092 break; 02093 case rFeb29: 02094 break; 02095 } 02096 } 02097 break; 02098 } 02099 } 02100 } 02101 if (countTogo) { 02102 data.month = 1; 02103 data.year += rFreq; 02104 } 02105 } 02106 if (countTogo) { 02107 if (data.feb29 && mFeb29YearlyType == rFeb29) { 02108 // The number of recurrences is different on leap years, 02109 // so check year-by-year. 02110 for ( ; ; ) { 02111 mons = data.monthList(); 02112 uint n = mons->count(); 02113 if (n >= countTogo) 02114 break; 02115 countTogo -= n; 02116 countGone += n; 02117 data.year += rFreq; 02118 } 02119 } else { 02120 // The number of recurrences is the same every year, 02121 // so skip the year-by-year check. 02122 // Skip the remaining whole years, but leave at least 02123 // 1 recurrence remaining, in order to get its date. 02124 int monthsPerYear = mons->count(); 02125 int wholeYears = (countTogo - 1) / monthsPerYear; 02126 data.year += wholeYears * rFreq; 02127 countGone += wholeYears * monthsPerYear; 02128 countTogo -= wholeYears * monthsPerYear; 02129 } 02130 if (countTogo) { 02131 // Check the last year in the recurrence 02132 for (it = mons->begin(); it != mons->end(); ++it) { 02133 ++countGone; 02134 if (--countTogo == 0) { 02135 data.month = *it; 02136 if (data.month == 2 && data.feb29 && !QDate::leapYear(data.year)) { 02137 // The recurrence should end on February 29th, but it's a non-leap year 02138 switch (mFeb29YearlyType) { 02139 case rFeb28: 02140 data.day = 28; 02141 break; 02142 case rMar1: 02143 data.month = 3; 02144 data.day = 1; 02145 break; 02146 case rFeb29: 02147 break; 02148 } 02149 } 02150 break; 02151 } 02152 } 02153 } 02154 } 02155 enddate = data.date(); 02156 return countGone; 02157 } 02158 02159 // Find count of an annual recurrence by date. 02160 // Reply = total number of occurrences up to 'enddate'. 02161 int Recurrence::yearlyMonthCalcToDate(const QDate &enddate, YearlyMonthData &data) const 02162 { 02163 int countGone = 0; 02164 int countMax = (rDuration > 0) ? rDuration : INT_MAX; 02165 int endYear = enddate.year(); 02166 int endMonth = enddate.month(); 02167 int endDay = enddate.day(); 02168 if (data.day < 0) { 02169 // The end day of the month is relative to the end of the month. 02170 if (endDay < enddate.daysInMonth() + data.day + 1) { 02171 if (--endMonth == 0) { 02172 endMonth = 12; 02173 --endYear; 02174 } 02175 } 02176 } 02177 else if (endDay < data.day) { 02178 /* The end day of the month is earlier than the recurrence day of the month. 02179 * If Feb 29th recurs and: 02180 * 1) it recurs on Feb 28th in non-leap years, don't adjust the end month 02181 * if enddate is Feb 28th on a non-leap year. 02182 * 2) it recurs on Mar 1st in non-leap years, allow the end month to be 02183 * adjusted to February, to simplify calculations. 02184 */ 02185 if (data.feb29 && !QDate::leapYear(endYear) 02186 && mFeb29YearlyType == rFeb28 && endDay == 28 && endMonth == 2) { 02187 } 02188 else if (--endMonth == 0) { 02189 endMonth = 12; 02190 --endYear; 02191 } 02192 } 02193 QValueList<int>::ConstIterator it; 02194 const QValueList<int>* mons = data.monthList(); 02195 02196 if (data.month > 1) { 02197 // Check what remains of the start year 02198 for (it = mons->begin(); it != mons->end(); ++it) { 02199 if (*it >= data.month) { 02200 if (data.year == endYear && *it > endMonth) 02201 return countGone; 02202 if (++countGone >= countMax) 02203 return countMax; 02204 } 02205 } 02206 data.month = 1; 02207 data.year += rFreq; 02208 } 02209 if (data.feb29 && mFeb29YearlyType == rFeb29) { 02210 // The number of recurrences is different on leap years, 02211 // so check year-by-year. 02212 while (data.year < endYear) { 02213 countGone += data.monthList()->count(); 02214 if (countGone >= countMax) 02215 return countMax; 02216 data.year += rFreq; 02217 } 02218 mons = data.monthList(); 02219 } else { 02220 // The number of recurrences is the same every year, 02221 // so skip the year-by-year check. 02222 // Skip the remaining whole years. 02223 int monthsPerYear = mons->count(); 02224 int wholeYears = endYear - data.year; 02225 countGone += (wholeYears / rFreq) * monthsPerYear; 02226 if (countGone >= countMax) 02227 return countMax; 02228 if (wholeYears % rFreq) 02229 return countGone; // end year isn't a recurrence year 02230 data.year = endYear; 02231 } 02232 02233 // Check the last year in the recurrence 02234 for (it = mons->begin(); it != mons->end(); ++it) { 02235 if (*it > endMonth) 02236 return countGone; 02237 if (++countGone >= countMax) 02238 return countMax; 02239 } 02240 return countGone; 02241 } 02242 02243 // Find count and date of first recurrence after 'enddate' of an annual recurrence by date. 02244 // Reply = total number of occurrences up to 'enddate'. 02245 int Recurrence::yearlyMonthCalcNextAfter(QDate &enddate, YearlyMonthData &data) const 02246 { 02247 uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX; 02248 int countGone = 0; 02249 int endYear = enddate.year(); 02250 int endMonth = enddate.month(); 02251 int endDay = enddate.day(); 02252 bool mar1TooEarly = false; 02253 bool feb28ok = false; 02254 if (data.day < 0) { 02255 // The end day of the month is relative to the end of the month. 02256 if (endDay < enddate.daysInMonth() + data.day + 1) { 02257 if (--endMonth == 0) { 02258 endMonth = 12; 02259 --endYear; 02260 } 02261 } 02262 } 02263 else if (endDay < data.day) { 02264 if (data.feb29 && mFeb29YearlyType == rMar1 && endMonth == 3) 02265 mar1TooEarly = true; 02266 if (data.feb29 && mFeb29YearlyType == rFeb28 && endMonth == 2 && endDay == 28) 02267 feb28ok = true; 02268 else if (--endMonth == 0) { 02269 endMonth = 12; 02270 --endYear; 02271 } 02272 } 02273 QValueList<int>::ConstIterator it; 02274 const QValueList<int>* mons = data.monthList(); 02275 02276 if (data.month > 1) { 02277 // Check what remains of the start year 02278 for (it = mons->begin(); it != mons->end(); ++it) { 02279 if (*it >= data.month) { 02280 ++countGone; 02281 if (data.year == endYear 02282 && ( *it > endMonth && (*it > 3 || !mar1TooEarly) 02283 || *it == 2 && feb28ok && data.leapyear)) { 02284 if (*it == 2 && data.feb29 && !data.leapyear) { 02285 // The next recurrence should be on February 29th, but it's a non-leap year 02286 switch (mFeb29YearlyType) { 02287 case rFeb28: 02288 data.month = 2; 02289 data.day = 28; 02290 break; 02291 case rMar1: 02292 data.month = 3; 02293 data.day = 1; 02294 break; 02295 case rFeb29: // impossible in this context! 02296 break; 02297 } 02298 } 02299 else 02300 data.month = *it; 02301 goto ex; 02302 } 02303 if (--countTogo == 0) 02304 return 0; 02305 } 02306 } 02307 data.month = 1; 02308 data.year += rFreq; 02309 } 02310 02311 if (data.feb29 && mFeb29YearlyType == rFeb29) { 02312 // The number of recurrences is different on leap years, 02313 // so check year-by-year. 02314 while (data.year <= endYear) { 02315 mons = data.monthList(); 02316 if (data.year == endYear && mons->last() > endMonth) 02317 break; 02318 uint n = mons->count(); 02319 if (n >= countTogo) 02320 break; 02321 countTogo -= n; 02322 countGone += n; 02323 data.year += rFreq; 02324 } 02325 mons = data.monthList(); 02326 } else { 02327 // The number of recurrences is the same every year, 02328 // so skip the year-by-year check. 02329 // Skip the remaining whole years to at least endYear. 02330 int monthsPerYear = mons->count(); 02331 int recurYears = (endYear - data.year + rFreq - 1) / rFreq; 02332 if ((endYear - data.year)%rFreq == 0 02333 && mons->last() <= endMonth) 02334 ++recurYears; // required year is after endYear 02335 if (recurYears) { 02336 int n = recurYears * monthsPerYear; 02337 if (static_cast<uint>(n) > countTogo) 02338 return 0; // reached end of recurrence 02339 countTogo -= n; 02340 countGone += n; 02341 data.year += recurYears * rFreq; 02342 } 02343 } 02344 02345 // Check the last year in the recurrence 02346 for (it = mons->begin(); it != mons->end(); ++it) { 02347 ++countGone; 02348 if (data.year > endYear 02349 || ( *it > endMonth && (*it > 3 || !mar1TooEarly) 02350 || *it == 2 && feb28ok && QDate::leapYear(data.year))) { 02351 if (*it == 2 && data.feb29 && !QDate::leapYear(data.year)) { 02352 // The next recurrence should be on February 29th, but it's a non-leap year 02353 switch (mFeb29YearlyType) { 02354 case rFeb28: 02355 data.month = 2; 02356 data.day = 28; 02357 break; 02358 case rMar1: 02359 data.month = 3; 02360 data.day = 1; 02361 break; 02362 case rFeb29: // impossible in this context! 02363 break; 02364 } 02365 } 02366 else 02367 data.month = *it; 02368 break; 02369 } 02370 if (--countTogo == 0) 02371 return 0; 02372 } 02373 ex: 02374 enddate = data.date(); 02375 return countGone; 02376 } 02377 02378 02379 /* Find count and, depending on 'func', the end date of an annual recurrence by date. 02380 * Reply = total number of occurrences up to 'enddate', or 0 if error. 02381 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 02382 * recurrence end date. 02383 */ 02384 class Recurrence::YearlyPosData 02385 { 02386 public: 02387 const Recurrence *recurrence; 02388 int year; // current year 02389 int month; // current month 1..12 02390 int day; // current day of month 1..31 02391 int daysPerMonth; // number of days which recur each month, or -1 if variable 02392 int count; // number of days which recur each year, or -1 if variable 02393 bool varies; // true if number of days varies from year to year 02394 02395 private: 02396 mutable QValueList<int> days; 02397 02398 public: 02399 YearlyPosData(const Recurrence* r, const QDate &date) 02400 : recurrence(r), year(date.year()), month(date.month()), day(date.day()), count(-1) 02401 { if ((daysPerMonth = r->countMonthlyPosDays()) > 0) 02402 count = daysPerMonth * r->yearNums().count(); 02403 varies = (daysPerMonth < 0); 02404 } 02405 const QValueList<int>* dayList() const { 02406 QDate startOfMonth(year, month, 1); 02407 recurrence->getMonthlyPosDays(days, startOfMonth.daysInMonth(), startOfMonth.dayOfWeek()); 02408 return &days; 02409 } 02410 int yearMonth() const { return year*12 + month - 1; } 02411 void addMonths(int diff) { month += diff - 1; year += month / 12; month = month % 12 + 1; } 02412 QDate date() const { return QDate(year, month, day); } 02413 }; 02414 02415 int Recurrence::yearlyPosCalc(PeriodFunc func, QDate &enddate) const 02416 { 02417 if (rYearNums.isEmpty() || rMonthPositions.isEmpty()) 02418 return 0; 02419 YearlyPosData data(this, mRecurStart.date()); 02420 switch (func) { 02421 case END_DATE_AND_COUNT: 02422 return yearlyPosCalcEndDate(enddate, data); 02423 case COUNT_TO_DATE: 02424 return yearlyPosCalcToDate(enddate, data); 02425 case NEXT_AFTER_DATE: 02426 return yearlyPosCalcNextAfter(enddate, data); 02427 } 02428 return 0; 02429 } 02430 02431 int Recurrence::yearlyPosCalcEndDate(QDate &enddate, YearlyPosData &data) const 02432 { 02433 uint countTogo = rDuration; 02434 int countGone = 0; 02435 QValueList<int>::ConstIterator id; 02436 const QValueList<int>* days; 02437 02438 if (data.month > 1 || data.day > 1) { 02439 // Check what remains of the start year 02440 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 02441 if (*im.current() >= data.month) { 02442 // Check what remains of the start month 02443 if (data.day > 1 || data.varies 02444 || static_cast<uint>(data.daysPerMonth) >= countTogo) { 02445 data.month = *im.current(); 02446 days = data.dayList(); 02447 for (id = days->begin(); id != days->end(); ++id) { 02448 if (*id >= data.day) { 02449 ++countGone; 02450 if (--countTogo == 0) { 02451 data.month = *im.current(); 02452 data.day = *id; 02453 goto ex; 02454 } 02455 } 02456 } 02457 data.day = 1; 02458 } else { 02459 // The number of days per month is constant, so skip 02460 // the whole month. 02461 countTogo -= data.daysPerMonth; 02462 countGone += data.daysPerMonth; 02463 } 02464 } 02465 } 02466 data.month = 1; 02467 data.year += rFreq; 02468 } 02469 02470 if (data.varies) { 02471 // The number of recurrences varies from year to year. 02472 for ( ; ; ) { 02473 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 02474 data.month = *im.current(); 02475 days = data.dayList(); 02476 int n = days->count(); 02477 if (static_cast<uint>(n) >= countTogo) { 02478 // Check the last month in the recurrence 02479 for (id = days->begin(); id != days->end(); ++id) { 02480 ++countGone; 02481 if (--countTogo == 0) { 02482 data.day = *id; 02483 goto ex; 02484 } 02485 } 02486 } 02487 countTogo -= n; 02488 countGone += n; 02489 } 02490 data.year += rFreq; 02491 } 02492 } else { 02493 // The number of recurrences is the same every year, 02494 // so skip the year-by-year check. 02495 // Skip the remaining whole years, but leave at least 02496 // 1 recurrence remaining, in order to get its date. 02497 int wholeYears = (countTogo - 1) / data.count; 02498 data.year += wholeYears * rFreq; 02499 countGone += wholeYears * data.count; 02500 countTogo -= wholeYears * data.count; 02501 02502 // Check the last year in the recurrence. 02503 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 02504 if (static_cast<uint>(data.daysPerMonth) >= countTogo) { 02505 // Check the last month in the recurrence 02506 data.month = *im.current(); 02507 days = data.dayList(); 02508 for (id = days->begin(); id != days->end(); ++id) { 02509 ++countGone; 02510 if (--countTogo == 0) { 02511 data.day = *id; 02512 goto ex; 02513 } 02514 } 02515 } 02516 countTogo -= data.daysPerMonth; 02517 countGone += data.daysPerMonth; 02518 } 02519 data.year += rFreq; 02520 } 02521 ex: 02522 enddate = data.date(); 02523 return countGone; 02524 } 02525 02526 int Recurrence::yearlyPosCalcToDate(const QDate &enddate, YearlyPosData &data) const 02527 { 02528 int countGone = 0; 02529 int countMax = (rDuration > 0) ? rDuration : INT_MAX; 02530 int endYear = enddate.year(); 02531 int endMonth = enddate.month(); 02532 int endDay = enddate.day(); 02533 if (endDay < data.day && --endMonth == 0) { 02534 endMonth = 12; 02535 --endYear; 02536 } 02537 int endYearMonth = endYear*12 + endMonth; 02538 QValueList<int>::ConstIterator id; 02539 const QValueList<int>* days; 02540 02541 if (data.month > 1 || data.day > 1) { 02542 // Check what remains of the start year 02543 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 02544 if (*im.current() >= data.month) { 02545 data.month = *im.current(); 02546 if (data.yearMonth() > endYearMonth) 02547 return countGone; 02548 // Check what remains of the start month 02549 bool lastMonth = (data.yearMonth() == endYearMonth); 02550 if (lastMonth || data.day > 1 || data.varies) { 02551 days = data.dayList(); 02552 if (lastMonth || data.day > 1) { 02553 for (id = days->begin(); id != days->end(); ++id) { 02554 if (*id >= data.day) { 02555 if (lastMonth && *id > endDay) 02556 return countGone; 02557 if (++countGone >= countMax) 02558 return countMax; 02559 } 02560 } 02561 } else { 02562 countGone += days->count(); 02563 if (countGone >= countMax) 02564 return countMax; 02565 } 02566 data.day = 1; 02567 } else { 02568 // The number of days per month is constant, so skip 02569 // the whole month. 02570 countGone += data.daysPerMonth; 02571 if (countGone >= countMax) 02572 return countMax; 02573 } 02574 } 02575 } 02576 data.month = 1; 02577 data.year += rFreq; 02578 } 02579 02580 if (data.varies) { 02581 // The number of recurrences varies from year to year. 02582 for ( ; ; ) { 02583 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 02584 data.month = *im.current(); 02585 days = data.dayList(); 02586 if (data.yearMonth() >= endYearMonth) { 02587 if (data.yearMonth() > endYearMonth) 02588 return countGone; 02589 // Check the last month in the recurrence 02590 for (id = days->begin(); id != days->end(); ++id) { 02591 if (*id > endDay) 02592 return countGone; 02593 if (++countGone >= countMax) 02594 return countMax; 02595 } 02596 } else { 02597 countGone += days->count(); 02598 if (countGone >= countMax) 02599 return countMax; 02600 } 02601 } 02602 data.year += rFreq; 02603 } 02604 } else { 02605 // The number of recurrences is the same every year, 02606 // so skip the year-by-year check. 02607 // Skip the remaining whole years, but leave at least 02608 // 1 recurrence remaining, in order to get its date. 02609 int wholeYears = endYear - data.year; 02610 countGone += (wholeYears / rFreq) * data.count; 02611 if (countGone >= countMax) 02612 return countMax; 02613 if (wholeYears % rFreq) 02614 return countGone; // end year isn't a recurrence year 02615 data.year = endYear; 02616 02617 // Check the last year in the recurrence. 02618 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 02619 data.month = *im.current(); 02620 if (data.month >= endMonth) { 02621 if (data.month > endMonth) 02622 return countGone; 02623 // Check the last month in the recurrence 02624 days = data.dayList(); 02625 for (id = days->begin(); id != days->end(); ++id) { 02626 if (*id > endDay) 02627 return countGone; 02628 if (++countGone >= countMax) 02629 return countMax; 02630 } 02631 } else { 02632 countGone += data.daysPerMonth; 02633 if (countGone >= countMax) 02634 return countMax; 02635 } 02636 } 02637 } 02638 return countGone; 02639 } 02640 02641 int Recurrence::yearlyPosCalcNextAfter(QDate &enddate, YearlyPosData &data) const 02642 { 02643 uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX; 02644 int countGone = 0; 02645 int endYear = enddate.year(); 02646 int endMonth = enddate.month(); 02647 int endDay = enddate.day(); 02648 if (endDay < data.day && --endMonth == 0) { 02649 endMonth = 12; 02650 --endYear; 02651 } 02652 int endYearMonth = endYear*12 + endMonth; 02653 QValueList<int>::ConstIterator id; 02654 const QValueList<int>* days; 02655 02656 if (data.varies) { 02657 // The number of recurrences varies from year to year. 02658 for ( ; ; ) { 02659 // Check the next year 02660 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 02661 if (*im.current() >= data.month) { 02662 // Check the next month 02663 data.month = *im.current(); 02664 int ended = data.yearMonth() - endYearMonth; 02665 days = data.dayList(); 02666 if (ended >= 0 || data.day > 1) { 02667 // This is the start or end month, so check each day 02668 for (id = days->begin(); id != days->end(); ++id) { 02669 if (*id >= data.day) { 02670 ++countGone; 02671 if (ended > 0 || (ended == 0 && *id > endDay)) { 02672 data.day = *id; 02673 goto ex; 02674 } 02675 if (--countTogo == 0) 02676 return 0; 02677 } 02678 } 02679 } else { 02680 // Skip the whole month 02681 uint n = days->count(); 02682 if (n >= countTogo) 02683 return 0; 02684 countGone += n; 02685 } 02686 data.day = 1; // we've checked the start month now 02687 } 02688 } 02689 data.month = 1; // we've checked the start year now 02690 data.year += rFreq; 02691 } 02692 } else { 02693 // The number of recurrences is the same every year. 02694 if (data.month > 1 || data.day > 1) { 02695 // Check what remains of the start year 02696 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 02697 if (*im.current() >= data.month) { 02698 // Check what remains of the start month 02699 data.month = *im.current(); 02700 int ended = data.yearMonth() - endYearMonth; 02701 if (ended >= 0 || data.day > 1) { 02702 // This is the start or end month, so check each day 02703 days = data.dayList(); 02704 for (id = days->begin(); id != days->end(); ++id) { 02705 if (*id >= data.day) { 02706 ++countGone; 02707 if (ended > 0 || (ended == 0 && *id > endDay)) { 02708 data.day = *id; 02709 goto ex; 02710 } 02711 if (--countTogo == 0) 02712 return 0; 02713 } 02714 } 02715 data.day = 1; // we've checked the start month now 02716 } else { 02717 // Skip the whole month. 02718 if (static_cast<uint>(data.daysPerMonth) >= countTogo) 02719 return 0; 02720 countGone += data.daysPerMonth; 02721 } 02722 } 02723 } 02724 data.year += rFreq; 02725 } 02726 // Skip the remaining whole years to at least endYear. 02727 int recurYears = (endYear - data.year + rFreq - 1) / rFreq; 02728 if ((endYear - data.year)%rFreq == 0 02729 && *rYearNums.getLast() <= endMonth) 02730 ++recurYears; // required year is after endYear 02731 if (recurYears) { 02732 int n = recurYears * data.count; 02733 if (static_cast<uint>(n) > countTogo) 02734 return 0; // reached end of recurrence 02735 countTogo -= n; 02736 countGone += n; 02737 data.year += recurYears * rFreq; 02738 } 02739 02740 // Check the last year in the recurrence 02741 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 02742 data.month = *im.current(); 02743 int ended = data.yearMonth() - endYearMonth; 02744 if (ended >= 0) { 02745 // This is the end month, so check each day 02746 days = data.dayList(); 02747 for (id = days->begin(); id != days->end(); ++id) { 02748 ++countGone; 02749 if (ended > 0 || (ended == 0 && *id > endDay)) { 02750 data.day = *id; 02751 goto ex; 02752 } 02753 if (--countTogo == 0) 02754 return 0; 02755 } 02756 } else { 02757 // Skip the whole month. 02758 if (static_cast<uint>(data.daysPerMonth) >= countTogo) 02759 return 0; 02760 countGone += data.daysPerMonth; 02761 } 02762 } 02763 } 02764 ex: 02765 enddate = data.date(); 02766 return countGone; 02767 } 02768 02769 02770 /* Find count and, depending on 'func', the end date of an annual recurrence by day. 02771 * Reply = total number of occurrences up to 'enddate', or 0 if error. 02772 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 02773 * recurrence end date. 02774 */ 02775 class Recurrence::YearlyDayData 02776 { 02777 public: 02778 int year; // current year 02779 int day; // current day of year 1..366 02780 bool varies; // true if day 366 recurs 02781 02782 private: 02783 int daycount; 02784 02785 public: 02786 YearlyDayData(const Recurrence* r, const QDate &date) 02787 : year( date.year() ), day( date.dayOfYear() ), 02788 varies( *r->yearNums().getLast() == 366 ), 02789 daycount( r->yearNums().count() ) { } 02790 bool leapYear() const { return QDate::leapYear(year); } 02791 int dayCount() const { return daycount - (varies && !QDate::leapYear(year) ? 1 : 0); } 02792 bool isMaxDayCount() const { return !varies || QDate::leapYear(year); } 02793 QDate date() const { return QDate(year, 1, 1).addDays(day - 1); } 02794 }; 02795 02796 int Recurrence::yearlyDayCalc(PeriodFunc func, QDate &enddate) const 02797 { 02798 if (rYearNums.isEmpty()) 02799 return 0; 02800 YearlyDayData data(this, mRecurStart.date()); 02801 switch (func) { 02802 case END_DATE_AND_COUNT: 02803 return yearlyDayCalcEndDate(enddate, data); 02804 case COUNT_TO_DATE: 02805 return yearlyDayCalcToDate(enddate, data); 02806 case NEXT_AFTER_DATE: 02807 return yearlyDayCalcNextAfter(enddate, data); 02808 } 02809 return 0; 02810 } 02811 02812 int Recurrence::yearlyDayCalcEndDate(QDate &enddate, YearlyDayData &data) const 02813 { 02814 uint countTogo = rDuration; 02815 int countGone = 0; 02816 02817 if (data.day > 1) { 02818 // Check what remains of the start year 02819 bool leapOK = data.isMaxDayCount(); 02820 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 02821 int d = *it.current(); 02822 if (d >= data.day && (leapOK || d < 366)) { 02823 ++countGone; 02824 if (--countTogo == 0) { 02825 data.day = d; 02826 goto ex; 02827 } 02828 } 02829 } 02830 data.day = 1; 02831 data.year += rFreq; 02832 } 02833 02834 if (data.varies) { 02835 // The number of recurrences is different in leap years, 02836 // so check year-by-year. 02837 for ( ; ; ) { 02838 uint n = data.dayCount(); 02839 if (n >= countTogo) 02840 break; 02841 countTogo -= n; 02842 countGone += n; 02843 data.year += rFreq; 02844 } 02845 } else { 02846 // The number of recurrences is the same every year, 02847 // so skip the year-by-year check. 02848 // Skip the remaining whole years, but leave at least 02849 // 1 recurrence remaining, in order to get its date. 02850 int daysPerYear = rYearNums.count(); 02851 int wholeYears = (countTogo - 1) / daysPerYear; 02852 data.year += wholeYears * rFreq; 02853 countGone += wholeYears * daysPerYear; 02854 countTogo -= wholeYears * daysPerYear; 02855 } 02856 if (countTogo) { 02857 // Check the last year in the recurrence 02858 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 02859 ++countGone; 02860 if (--countTogo == 0) { 02861 data.day = *it.current(); 02862 break; 02863 } 02864 } 02865 } 02866 ex: 02867 enddate = data.date(); 02868 return countGone; 02869 } 02870 02871 int Recurrence::yearlyDayCalcToDate(const QDate &enddate, YearlyDayData &data) const 02872 { 02873 int countGone = 0; 02874 int countMax = (rDuration > 0) ? rDuration : INT_MAX; 02875 int endYear = enddate.year(); 02876 int endDay = enddate.dayOfYear(); 02877 02878 if (data.day > 1) { 02879 // Check what remains of the start year 02880 bool leapOK = data.isMaxDayCount(); 02881 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 02882 int d = *it.current(); 02883 if (d >= data.day && (leapOK || d < 366)) { 02884 if (data.year == endYear && d > endDay) 02885 return countGone; 02886 if (++countGone >= countMax) 02887 return countMax; 02888 } 02889 } 02890 data.day = 1; 02891 data.year += rFreq; 02892 } 02893 02894 if (data.varies) { 02895 // The number of recurrences is different in leap years, 02896 // so check year-by-year. 02897 while (data.year < endYear) { 02898 uint n = data.dayCount(); 02899 countGone += n; 02900 if (countGone >= countMax) 02901 return countMax; 02902 data.year += rFreq; 02903 } 02904 if (data.year > endYear) 02905 return countGone; 02906 } else { 02907 // The number of recurrences is the same every year. 02908 // Skip the remaining whole years. 02909 int wholeYears = endYear - data.year; 02910 countGone += (wholeYears / rFreq) * rYearNums.count(); 02911 if (countGone >= countMax) 02912 return countMax; 02913 if (wholeYears % rFreq) 02914 return countGone; // end year isn't a recurrence year 02915 data.year = endYear; 02916 } 02917 02918 if (data.year <= endYear) { 02919 // Check the last year in the recurrence 02920 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 02921 if (*it.current() > endDay) 02922 return countGone; 02923 if (++countGone >= countMax) 02924 return countMax; 02925 } 02926 } 02927 return countGone; 02928 } 02929 02930 int Recurrence::yearlyDayCalcNextAfter(QDate &enddate, YearlyDayData &data) const 02931 { 02932 uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX; 02933 int countGone = 0; 02934 int endYear = enddate.year(); 02935 int endDay = enddate.dayOfYear(); 02936 02937 if (data.day > 1) { 02938 // Check what remains of the start year 02939 bool leapOK = data.isMaxDayCount(); 02940 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 02941 int d = *it.current(); 02942 if (d >= data.day && (leapOK || d < 366)) { 02943 ++countGone; 02944 if (data.year == endYear && d > endDay) { 02945 data.day = d; 02946 goto ex; 02947 } 02948 if (--countTogo == 0) 02949 return 0; 02950 } 02951 } 02952 data.day = 1; 02953 data.year += rFreq; 02954 } 02955 02956 if (data.varies) { 02957 // The number of recurrences is different in leap years, 02958 // so check year-by-year. 02959 while (data.year <= endYear) { 02960 uint n = data.dayCount(); 02961 if (data.year == endYear && *rYearNums.getLast() > endDay) 02962 break; 02963 if (n >= countTogo) 02964 break; 02965 countTogo -= n; 02966 countGone += n; 02967 data.year += rFreq; 02968 } 02969 } else { 02970 // The number of recurrences is the same every year, 02971 // so skip the year-by-year check. 02972 // Skip the remaining whole years to at least endYear. 02973 int daysPerYear = rYearNums.count(); 02974 int recurYears = (endYear - data.year + rFreq - 1) / rFreq; 02975 if ((endYear - data.year)%rFreq == 0 02976 && *rYearNums.getLast() <= endDay) 02977 ++recurYears; // required year is after endYear 02978 if (recurYears) { 02979 int n = recurYears * daysPerYear; 02980 if (static_cast<uint>(n) > countTogo) 02981 return 0; // reached end of recurrence 02982 countTogo -= n; 02983 countGone += n; 02984 data.year += recurYears * rFreq; 02985 } 02986 } 02987 02988 // Check the last year in the recurrence 02989 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 02990 ++countGone; 02991 int d = *it.current(); 02992 if (data.year > endYear || d > endDay) { 02993 data.day = d; 02994 break; 02995 } 02996 if (--countTogo == 0) 02997 return 0; 02998 } 02999 ex: 03000 enddate = data.date(); 03001 return countGone; 03002 } 03003 03004 // Get the days in this month which recur, in numerical order. 03005 // Parameters: daysInMonth = number of days in this month 03006 // startDayOfWeek = day of week for first day of month. 03007 void Recurrence::getMonthlyPosDays(QValueList<int> &list, int daysInMonth, int startDayOfWeek) const 03008 { 03009 list.clear(); 03010 int endDayOfWeek = (startDayOfWeek + daysInMonth - 2) % 7 + 1; 03011 // Go through the list, compiling a bit list of actual day numbers 03012 Q_UINT32 days = 0; 03013 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) { 03014 int weeknum = pos.current()->rPos - 1; // get 0-based week number 03015 QBitArray &rdays = pos.current()->rDays; 03016 if (pos.current()->negative) { 03017 // nth days before the end of the month 03018 for (uint i = 1; i <= 7; ++i) { 03019 if (rdays.testBit(i - 1)) { 03020 int day = daysInMonth - weeknum*7 - (endDayOfWeek - i + 7) % 7; 03021 if (day > 0) 03022 days |= 1 << (day - 1); 03023 } 03024 } 03025 } else { 03026 // nth days after the start of the month 03027 for (uint i = 1; i <= 7; ++i) { 03028 if (rdays.testBit(i - 1)) { 03029 int day = 1 + weeknum*7 + (i - startDayOfWeek + 7) % 7; 03030 if (day <= daysInMonth) 03031 days |= 1 << (day - 1); 03032 } 03033 } 03034 } 03035 } 03036 // Compile the ordered list 03037 Q_UINT32 mask = 1; 03038 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) { 03039 if (days & mask) 03040 list.append(i + 1); 03041 } 03042 } 03043 03044 // Get the number of days in the month which recur. 03045 // Reply = -1 if the number varies from month to month. 03046 int Recurrence::countMonthlyPosDays() const 03047 { 03048 int count = 0; 03049 Q_UINT8 positive[5] = { 0, 0, 0, 0, 0 }; 03050 Q_UINT8 negative[4] = { 0, 0, 0, 0 }; 03051 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) { 03052 int weeknum = pos.current()->rPos; 03053 Q_UINT8* wk; 03054 if (pos.current()->negative) { 03055 // nth days before the end of the month 03056 if (weeknum > 4) 03057 return -1; // days in 5th week are often missing 03058 wk = &negative[4 - weeknum]; 03059 } else { 03060 // nth days after the start of the month 03061 if (weeknum > 4) 03062 return -1; // days in 5th week are often missing 03063 wk = &positive[weeknum - 1]; 03064 } 03065 QBitArray &rdays = pos.current()->rDays; 03066 for (uint i = 0; i < 7; ++i) { 03067 if (rdays.testBit(i)) { 03068 ++count; 03069 *wk |= (1 << i); 03070 } 03071 } 03072 } 03073 // Check for any possible days which could be duplicated by 03074 // a positive and a negative position. 03075 for (int i = 0; i < 4; ++i) { 03076 if (negative[i] & (positive[i] | positive[i+1])) 03077 return -1; 03078 } 03079 return count; 03080 } 03081 03082 // Get the days in this month which recur, in numerical order. 03083 // Reply = true if day numbers varies from month to month. 03084 bool Recurrence::getMonthlyDayDays(QValueList<int> &list, int daysInMonth) const 03085 { 03086 list.clear(); 03087 bool variable = false; 03088 Q_UINT32 days = 0; 03089 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { 03090 int day = *it.current(); 03091 if (day > 0) { 03092 // date in the month 03093 if (day <= daysInMonth) 03094 days |= 1 << (day - 1); 03095 if (day > 28 && day <= 31) 03096 variable = true; // this date does not appear in some months 03097 } else if (day < 0) { 03098 // days before the end of the month 03099 variable = true; // this date varies depending on the month length 03100 day = daysInMonth + day; // zero-based day of month 03101 if (day >= 0) 03102 days |= 1 << day; 03103 } 03104 } 03105 // Compile the ordered list 03106 Q_UINT32 mask = 1; 03107 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) { 03108 if (days & mask) 03109 list.append(i + 1); 03110 } 03111 return variable; 03112 } 03113 03114 // Get the months which recur, in numerical order, for both leap years and non-leap years. 03115 // N.B. If February 29th recurs on March 1st in non-leap years, February (not March) is 03116 // included in the non-leap year month list. 03117 // Reply = true if February 29th also recurs. 03118 bool Recurrence::getYearlyMonthMonths(int day, QValueList<int> &list, QValueList<int> &leaplist) const 03119 { 03120 list.clear(); 03121 leaplist.clear(); 03122 bool feb29 = false; 03123 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 03124 int month = *it.current(); 03125 if (month == 2) { 03126 if (day <= 28) { 03127 list.append(month); // date appears in February 03128 leaplist.append(month); 03129 } 03130 else if (day == 29) { 03131 // February 29th 03132 leaplist.append(month); 03133 switch (mFeb29YearlyType) { 03134 case rFeb28: 03135 case rMar1: 03136 list.append(2); 03137 break; 03138 case rFeb29: 03139 break; 03140 } 03141 feb29 = true; 03142 } 03143 } 03144 else if (day <= 30 || QDate(2000, month, 1).daysInMonth() == 31) { 03145 list.append(month); // date appears in every month 03146 leaplist.append(month); 03147 } 03148 } 03149 return feb29; 03150 } 03151 03152 /* From the recurrence day of the week list, get the earliest day in the 03153 * specified week which is >= the startDay. 03154 * Parameters: startDay = 1..7 (Monday..Sunday) 03155 * useWeekStart = true to end search at day before next rWeekStart 03156 * = false to search for a full 7 days 03157 * Reply = day of the week (1..7), or 0 if none found. 03158 */ 03159 int Recurrence::getFirstDayInWeek(int startDay, bool useWeekStart) const 03160 { 03161 int last = ((useWeekStart ? rWeekStart : startDay) + 5)%7; 03162 for (int i = startDay - 1; ; i = (i + 1)%7) { 03163 if (rDays.testBit(i)) 03164 return i + 1; 03165 if (i == last) 03166 return 0; 03167 } 03168 } 03169 03170 /* From the recurrence day of the week list, get the latest day in the 03171 * specified week which is <= the endDay. 03172 * Parameters: endDay = 1..7 (Monday..Sunday) 03173 * useWeekStart = true to end search at rWeekStart 03174 * = false to search for a full 7 days 03175 * Reply = day of the week (1..7), or 0 if none found. 03176 */ 03177 int Recurrence::getLastDayInWeek(int endDay, bool useWeekStart) const 03178 { 03179 int last = useWeekStart ? rWeekStart - 1 : endDay%7; 03180 for (int i = endDay - 1; ; i = (i + 6)%7) { 03181 if (rDays.testBit(i)) 03182 return i + 1; 03183 if (i == last) 03184 return 0; 03185 } 03186 } 03187 03188 /* From the recurrence monthly day number list or monthly day of week/week of 03189 * month list, get the earliest day in the specified month which is >= the 03190 * earliestDate. 03191 */ 03192 QDate Recurrence::getFirstDateInMonth(const QDate &earliestDate) const 03193 { 03194 int earliestDay = earliestDate.day(); 03195 int daysInMonth = earliestDate.daysInMonth(); 03196 switch (recurs) { 03197 case rMonthlyDay: { 03198 int minday = daysInMonth + 1; 03199 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { 03200 int day = *it.current(); 03201 if (day < 0) 03202 day = daysInMonth + day + 1; 03203 if (day >= earliestDay && day < minday) 03204 minday = day; 03205 } 03206 if (minday <= daysInMonth) 03207 return earliestDate.addDays(minday - earliestDay); 03208 break; 03209 } 03210 case rMonthlyPos: 03211 case rYearlyPos: { 03212 QDate monthBegin(earliestDate.addDays(1 - earliestDay)); 03213 QValueList<int> dayList; 03214 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek()); 03215 for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) { 03216 if (*id >= earliestDay) 03217 return monthBegin.addDays(*id - 1); 03218 } 03219 break; 03220 } 03221 } 03222 return QDate(); 03223 } 03224 03225 /* From the recurrence monthly day number list or monthly day of week/week of 03226 * month list, get the latest day in the specified month which is <= the 03227 * latestDate. 03228 */ 03229 QDate Recurrence::getLastDateInMonth(const QDate &latestDate) const 03230 { 03231 int latestDay = latestDate.day(); 03232 int daysInMonth = latestDate.daysInMonth(); 03233 switch (recurs) { 03234 case rMonthlyDay: { 03235 int maxday = -1; 03236 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { 03237 int day = *it.current(); 03238 if (day < 0) 03239 day = daysInMonth + day + 1; 03240 if (day <= latestDay && day > maxday) 03241 maxday = day; 03242 } 03243 if (maxday > 0) 03244 return QDate(latestDate.year(), latestDate.month(), maxday); 03245 break; 03246 } 03247 case rMonthlyPos: 03248 case rYearlyPos: { 03249 QDate monthBegin(latestDate.addDays(1 - latestDay)); 03250 QValueList<int> dayList; 03251 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek()); 03252 for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) { 03253 if (*id <= latestDay) 03254 return monthBegin.addDays(*id - 1); 03255 } 03256 break; 03257 } 03258 } 03259 return QDate(); 03260 } 03261 03262 /* From the recurrence yearly month list or yearly day list, get the earliest 03263 * month or day in the specified year which is >= the earliestDate. 03264 * Note that rYearNums is sorted in numerical order. 03265 */ 03266 QDate Recurrence::getFirstDateInYear(const QDate &earliestDate) const 03267 { 03268 QPtrListIterator<int> it(rYearNums); 03269 switch (recurs) { 03270 case rYearlyMonth: { 03271 int day = recurStart().date().day(); 03272 int earliestYear = earliestDate.year(); 03273 int earliestMonth = earliestDate.month(); 03274 int earliestDay = earliestDate.day(); 03275 if (earliestDay > day) { 03276 // The earliest date is later in the month than the recurrence date, 03277 // so skip to the next month before starting to check 03278 if (++earliestMonth > 12) 03279 return QDate(); 03280 } 03281 for ( ; it.current(); ++it) { 03282 int month = *it.current(); 03283 if (month >= earliestMonth) { 03284 if (day <= 28 || QDate::isValid(earliestYear, month, day)) 03285 return QDate(earliestYear, month, day); 03286 if (day == 29 && month == 2) { 03287 // It's a recurrence on February 29th, in a non-leap year 03288 switch (mFeb29YearlyType) { 03289 case rMar1: 03290 return QDate(earliestYear, 3, 1); 03291 case rFeb28: 03292 if (earliestDay <= 28) 03293 return QDate(earliestYear, 2, 28); 03294 break; 03295 case rFeb29: 03296 break; 03297 } 03298 } 03299 } 03300 } 03301 break; 03302 } 03303 case rYearlyPos: { 03304 QValueList<int> dayList; 03305 int earliestYear = earliestDate.year(); 03306 int earliestMonth = earliestDate.month(); 03307 int earliestDay = earliestDate.day(); 03308 for ( ; it.current(); ++it) { 03309 int month = *it.current(); 03310 if (month >= earliestMonth) { 03311 QDate monthBegin(earliestYear, month, 1); 03312 getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek()); 03313 for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) { 03314 if (*id >= earliestDay) 03315 return monthBegin.addDays(*id - 1); 03316 } 03317 earliestDay = 1; 03318 } 03319 } 03320 break; 03321 } 03322 case rYearlyDay: { 03323 int earliestDay = earliestDate.dayOfYear(); 03324 for ( ; it.current(); ++it) { 03325 int day = *it.current(); 03326 if (day >= earliestDay && (day <= 365 || day <= earliestDate.daysInYear())) 03327 return earliestDate.addDays(day - earliestDay); 03328 } 03329 break; 03330 } 03331 } 03332 return QDate(); 03333 } 03334 03335 /* From the recurrence yearly month list or yearly day list, get the latest 03336 * month or day in the specified year which is <= the latestDate. 03337 * Note that rYearNums is sorted in numerical order. 03338 */ 03339 QDate Recurrence::getLastDateInYear(const QDate &latestDate) const 03340 { 03341 QPtrListIterator<int> it(rYearNums); 03342 switch (recurs) { 03343 case rYearlyMonth: { 03344 int day = recurStart().date().day(); 03345 int latestYear = latestDate.year(); 03346 int latestMonth = latestDate.month(); 03347 if (latestDate.day() > day) { 03348 // The latest date is earlier in the month than the recurrence date, 03349 // so skip to the previous month before starting to check 03350 if (--latestMonth <= 0) 03351 return QDate(); 03352 } 03353 for (it.toLast(); it.current(); --it) { 03354 int month = *it.current(); 03355 if (month <= latestMonth) { 03356 if (day <= 28 || QDate::isValid(latestYear, month, day)) 03357 return QDate(latestYear, month, day); 03358 if (day == 29 && month == 2) { 03359 // It's a recurrence on February 29th, in a non-leap year 03360 switch (mFeb29YearlyType) { 03361 case rMar1: 03362 if (latestMonth >= 3) 03363 return QDate(latestYear, 3, 1); 03364 break; 03365 case rFeb28: 03366 return QDate(latestYear, 2, 28); 03367 case rFeb29: 03368 break; 03369 } 03370 } 03371 } 03372 } 03373 break; 03374 } 03375 case rYearlyPos: { 03376 QValueList<int> dayList; 03377 int latestYear = latestDate.year(); 03378 int latestMonth = latestDate.month(); 03379 int latestDay = latestDate.day(); 03380 for (it.toLast(); it.current(); --it) { 03381 int month = *it.current(); 03382 if (month <= latestMonth) { 03383 QDate monthBegin(latestYear, month, 1); 03384 getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek()); 03385 for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) { 03386 if (*id <= latestDay) 03387 return monthBegin.addDays(*id - 1); 03388 } 03389 latestDay = 31; 03390 } 03391 } 03392 break; 03393 } 03394 case rYearlyDay: { 03395 int latestDay = latestDate.dayOfYear(); 03396 for (it.toLast(); it.current(); --it) { 03397 int day = *it.current(); 03398 if (day <= latestDay) 03399 return latestDate.addDays(day - latestDay); 03400 } 03401 break; 03402 } 03403 } 03404 return QDate(); 03405 } 03406 03407 void Recurrence::dump() const 03408 { 03409 kdDebug() << "Recurrence::dump():" << endl; 03410 03411 kdDebug() << " type: " << recurs << endl; 03412 03413 kdDebug() << " rDays: " << endl; 03414 int i; 03415 for( i = 0; i < 7; ++i ) { 03416 kdDebug() << " " << i << ": " 03417 << ( rDays.testBit( i ) ? "true" : "false" ) << endl; 03418 } 03419 }
KDE Logo
This file is part of the documentation for libkcal Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jul 28 23:57:44 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003