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