00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kalarm.h"
00022
00023 #include <qbitarray.h>
00024 #include <kdebug.h>
00025
00026 #include <libkcal/icalformat.h>
00027
00028 #include "datetime.h"
00029 #include "functions.h"
00030 #include "karecurrence.h"
00031
00032 using namespace KCal;
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051 KARecurrence::Feb29Type KARecurrence::mDefaultFeb29 = KARecurrence::FEB29_FEB29;
00052
00053
00054
00055
00056
00057
00058
00059
00060 bool KARecurrence::set(Type recurType, int freq, int count, int f29, const DateTime& start, const QDateTime& end)
00061 {
00062 mCachedType = -1;
00063 RecurrenceRule::PeriodType rrtype;
00064 switch (recurType)
00065 {
00066 case MINUTELY: rrtype = RecurrenceRule::rMinutely; break;
00067 case DAILY: rrtype = RecurrenceRule::rDaily; break;
00068 case WEEKLY: rrtype = RecurrenceRule::rWeekly; break;
00069 case MONTHLY_DAY: rrtype = RecurrenceRule::rMonthly; break;
00070 case ANNUAL_DATE: rrtype = RecurrenceRule::rYearly; break;
00071 case NO_RECUR: rrtype = RecurrenceRule::rNone; break;
00072 default:
00073 return false;
00074 }
00075 if (!init(rrtype, freq, count, f29, start, end))
00076 return false;
00077 switch (recurType)
00078 {
00079 case WEEKLY:
00080 {
00081 QBitArray days(7);
00082 days.setBit(start.date().dayOfWeek() - 1);
00083 addWeeklyDays(days);
00084 break;
00085 }
00086 case MONTHLY_DAY:
00087 addMonthlyDate(start.date().day());
00088 break;
00089 case ANNUAL_DATE:
00090 addYearlyDate(start.date().day());
00091 addYearlyMonth(start.date().month());
00092 break;
00093 default:
00094 break;
00095 }
00096 return true;
00097 }
00098
00099
00100
00101
00102
00103 bool KARecurrence::init(RecurrenceRule::PeriodType recurType, int freq, int count, int f29, const DateTime& start,
00104 const QDateTime& end)
00105 {
00106 mCachedType = -1;
00107 Feb29Type feb29Type = (f29 == -1) ? mDefaultFeb29 : static_cast<Feb29Type>(f29);
00108 mFeb29Type = FEB29_FEB29;
00109 clear();
00110 if (count < -1)
00111 return false;
00112 bool dateOnly = start.isDateOnly();
00113 if (!count && (!dateOnly && !end.isValid()
00114 || dateOnly && !end.date().isValid()))
00115 return false;
00116 switch (recurType)
00117 {
00118 case RecurrenceRule::rMinutely:
00119 case RecurrenceRule::rDaily:
00120 case RecurrenceRule::rWeekly:
00121 case RecurrenceRule::rMonthly:
00122 case RecurrenceRule::rYearly:
00123 break;
00124 case rNone:
00125 return true;
00126 default:
00127 return false;
00128 }
00129 setNewRecurrenceType(recurType, freq);
00130 if (count)
00131 setDuration(count);
00132 else if (dateOnly)
00133 setEndDate(end.date());
00134 else
00135 setEndDateTime(end);
00136 QDateTime startdt = start.dateTime();
00137 if (recurType == RecurrenceRule::rYearly
00138 && feb29Type == FEB29_FEB28 || feb29Type == FEB29_MAR1)
00139 {
00140 int year = startdt.date().year();
00141 if (!QDate::leapYear(year)
00142 && startdt.date().dayOfYear() == (feb29Type == FEB29_MAR1 ? 60 : 59))
00143 {
00144
00145
00146
00147
00148
00149
00150
00151 while (!QDate::leapYear(--year)) ;
00152 startdt.setDate(QDate(year, 2, 29));
00153 }
00154 mFeb29Type = feb29Type;
00155 }
00156 if (dateOnly)
00157 setStartDate(startdt.date());
00158 else
00159 setStartDateTime(startdt);
00160 return true;
00161 }
00162
00163
00164
00165
00166 bool KARecurrence::set(const QString& icalRRULE)
00167 {
00168 static QString RRULE = QString::fromLatin1("RRULE:");
00169 mCachedType = -1;
00170 clear();
00171 if (icalRRULE.isEmpty())
00172 return true;
00173 ICalFormat format;
00174 if (!format.fromString(defaultRRule(true),
00175 (icalRRULE.startsWith(RRULE) ? icalRRULE.mid(RRULE.length()) : icalRRULE)))
00176 return false;
00177 fix();
00178 return true;
00179 }
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190 void KARecurrence::fix()
00191 {
00192 mCachedType = -1;
00193 mFeb29Type = FEB29_FEB29;
00194 int convert = 0;
00195 int days[2] = { 0, 0 };
00196 RecurrenceRule* rrules[2];
00197 RecurrenceRule::List rrulelist = rRules();
00198 RecurrenceRule::List::ConstIterator rr = rrulelist.begin();
00199 for (int i = 0; i < 2 && rr != rrulelist.end(); ++i, ++rr)
00200 {
00201 RecurrenceRule* rrule = *rr;
00202 rrules[i] = rrule;
00203 bool stop = true;
00204 int rtype = recurrenceType(rrule);
00205 switch (rtype)
00206 {
00207 case rHourly:
00208
00209 rrule->setRecurrenceType(RecurrenceRule::rMinutely);
00210 rrule->setFrequency(rrule->frequency() * 60);
00211
00212 case rMinutely:
00213 case rDaily:
00214 case rWeekly:
00215 case rMonthlyDay:
00216 case rMonthlyPos:
00217 case rYearlyPos:
00218 if (!convert)
00219 ++rr;
00220 break;
00221 case rOther:
00222 if (dailyType(rrule))
00223 {
00224 if (!convert)
00225 ++rr;
00226 }
00227 break;
00228 case rYearlyDay:
00229 {
00230
00231 if (convert)
00232 {
00233
00234
00235 if (days[0] != 29
00236 || rrule->frequency() != rrules[0]->frequency()
00237 || rrule->startDt() != rrules[0]->startDt())
00238 break;
00239 }
00240 QValueList<int> ds = rrule->byYearDays();
00241 if (!ds.isEmpty() && ds.first() == 60)
00242 {
00243 ++convert;
00244 days[i] = 60;
00245 stop = false;
00246 break;
00247 }
00248 break;
00249 }
00250 case rYearlyMonth:
00251 {
00252 QValueList<int> ds = rrule->byMonthDays();
00253 if (!ds.isEmpty())
00254 {
00255 int day = ds.first();
00256 if (convert)
00257 {
00258
00259
00260 if (day == days[0] || day == -1 && days[0] == 60
00261 || rrule->frequency() != rrules[0]->frequency()
00262 || rrule->startDt() != rrules[0]->startDt())
00263 break;
00264 }
00265 if (ds.count() > 1)
00266 {
00267 ds.clear();
00268 ds.append(day);
00269 rrule->setByMonthDays(ds);
00270 }
00271 if (day == -1)
00272 {
00273
00274 QValueList<int> months = rrule->byMonths();
00275 if (months.count() != 1 || months.first() != 2)
00276 day = 0;
00277 }
00278 if (day == 29 || day == -1)
00279 {
00280 ++convert;
00281 days[i] = day;
00282 stop = false;
00283 break;
00284 }
00285 }
00286 if (!convert)
00287 ++rr;
00288 break;
00289 }
00290 default:
00291 break;
00292 }
00293 if (stop)
00294 break;
00295 }
00296
00297
00298 for ( ; rr != rrulelist.end(); ++rr)
00299 removeRRule(*rr);
00300
00301 QDate end;
00302 int count;
00303 QValueList<int> months;
00304 if (convert == 2)
00305 {
00306
00307
00308
00309 if (days[0] != 29)
00310 {
00311
00312 RecurrenceRule* rr = rrules[0];
00313 rrules[0] = rrules[1];
00314 rrules[1] = rr;
00315 int d = days[0];
00316 days[0] = days[1];
00317 days[1] = d;
00318 }
00319
00320 months = rrules[0]->byMonths();
00321 if (months.remove(2))
00322 rrules[0]->setByMonths(months);
00323
00324 count = combineDurations(rrules[0], rrules[1], end);
00325 mFeb29Type = (days[1] == 60) ? FEB29_MAR1 : FEB29_FEB28;
00326 }
00327 else if (convert == 1 && days[0] == 60)
00328 {
00329
00330
00331 count = duration();
00332 if (!count)
00333 end = endDate();
00334 mFeb29Type = FEB29_MAR1;
00335 }
00336 else
00337 return;
00338
00339
00340 setNewRecurrenceType(RecurrenceRule::rYearly, frequency());
00341 RecurrenceRule* rrule = defaultRRule();
00342 months.append(2);
00343 rrule->setByMonths(months);
00344 QValueList<int> ds;
00345 ds.append(29);
00346 rrule->setByMonthDays(ds);
00347 if (count)
00348 setDuration(count);
00349 else
00350 setEndDate(end);
00351 }
00352
00353
00354
00355
00356 QDateTime KARecurrence::getNextDateTime(const QDateTime& preDateTime) const
00357 {
00358 switch (type())
00359 {
00360 case ANNUAL_DATE:
00361 case ANNUAL_POS:
00362 {
00363 Recurrence recur;
00364 writeRecurrence(recur);
00365 return recur.getNextDateTime(preDateTime);
00366 }
00367 default:
00368 return Recurrence::getNextDateTime(preDateTime);
00369 }
00370 }
00371
00372
00373
00374
00375 QDateTime KARecurrence::getPreviousDateTime(const QDateTime& afterDateTime) const
00376 {
00377 switch (type())
00378 {
00379 case ANNUAL_DATE:
00380 case ANNUAL_POS:
00381 {
00382 Recurrence recur;
00383 writeRecurrence(recur);
00384 return recur.getPreviousDateTime(afterDateTime);
00385 }
00386 default:
00387 return Recurrence::getPreviousDateTime(afterDateTime);
00388 }
00389 }
00390
00391
00392
00393
00394
00395 void KARecurrence::writeRecurrence(KCal::Recurrence& recur) const
00396 {
00397 recur.clear();
00398 recur.setStartDateTime(startDateTime());
00399 recur.setExDates(exDates());
00400 recur.setExDateTimes(exDateTimes());
00401 const RecurrenceRule* rrule = defaultRRuleConst();
00402 if (!rrule)
00403 return;
00404 int freq = frequency();
00405 int count = duration();
00406 static_cast<KARecurrence*>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq);
00407 if (count)
00408 recur.setDuration(count);
00409 else
00410 recur.setEndDateTime(endDateTime());
00411 switch (type())
00412 {
00413 case DAILY:
00414 if (rrule->byDays().isEmpty())
00415 break;
00416
00417 case WEEKLY:
00418 case MONTHLY_POS:
00419 recur.defaultRRule(true)->setByDays(rrule->byDays());
00420 break;
00421 case MONTHLY_DAY:
00422 recur.defaultRRule(true)->setByMonthDays(rrule->byMonthDays());
00423 break;
00424 case ANNUAL_POS:
00425 recur.defaultRRule(true)->setByMonths(rrule->byMonths());
00426 recur.defaultRRule()->setByDays(rrule->byDays());
00427 break;
00428 case ANNUAL_DATE:
00429 {
00430 QValueList<int> months = rrule->byMonths();
00431 QValueList<int> days = monthDays();
00432 bool special = (mFeb29Type != FEB29_FEB29 && !days.isEmpty()
00433 && days.first() == 29 && months.remove(2));
00434 RecurrenceRule* rrule1 = recur.defaultRRule();
00435 rrule1->setByMonths(months);
00436 rrule1->setByMonthDays(days);
00437 if (!special)
00438 break;
00439
00440
00441
00442 RecurrenceRule* rrule2 = new RecurrenceRule();
00443 rrule2->setRecurrenceType(RecurrenceRule::rYearly);
00444 rrule2->setFrequency(freq);
00445 rrule2->setStartDt(startDateTime());
00446 rrule2->setFloats(doesFloat());
00447 if (!count)
00448 rrule2->setEndDt(endDateTime());
00449 if (mFeb29Type == FEB29_MAR1)
00450 {
00451 QValueList<int> ds;
00452 ds.append(60);
00453 rrule2->setByYearDays(ds);
00454 }
00455 else
00456 {
00457 QValueList<int> ds;
00458 ds.append(-1);
00459 rrule2->setByMonthDays(ds);
00460 QValueList<int> ms;
00461 ms.append(2);
00462 rrule2->setByMonths(ms);
00463 }
00464
00465 if (months.isEmpty())
00466 {
00467
00468
00469 if (count)
00470 rrule2->setDuration(count);
00471 recur.unsetRecurs();
00472 }
00473 else
00474 {
00475
00476
00477 if (count)
00478 {
00479 rrule1->setDuration(-1);
00480 rrule2->setDuration(-1);
00481 if (count > 0)
00482 {
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492 QDateTime end = endDateTime();
00493 kdDebug()<<"29th recurrence: count="<<count<<", end date="<<end.toString()<<endl;
00494 int count1 = rrule1->durationTo(end)
00495 - (rrule1->recursOn(startDate()) ? 0 : 1);
00496 if (count1 > 0)
00497 rrule1->setDuration(count1);
00498 else
00499 rrule1->setEndDt(startDateTime());
00500 int count2 = rrule2->durationTo(end)
00501 - (rrule2->recursOn(startDate()) ? 0 : 1);
00502 if (count2 > 0)
00503 rrule2->setDuration(count2);
00504 else
00505 rrule2->setEndDt(startDateTime());
00506 }
00507 }
00508 }
00509 recur.addRRule(rrule2);
00510 break;
00511 }
00512 default:
00513 break;
00514 }
00515 }
00516
00517
00518
00519
00520 QDateTime KARecurrence::endDateTime() const
00521 {
00522 if (mFeb29Type == FEB29_FEB29 || duration() <= 1)
00523 {
00524
00525
00526
00527
00528
00529 return Recurrence::endDateTime();
00530 }
00531
00532
00533
00534
00535
00536
00537 RecurrenceRule* rrule = new RecurrenceRule();
00538 rrule->setRecurrenceType(RecurrenceRule::rYearly);
00539 QDateTime dt = startDateTime();
00540 QDate d = dt.date();
00541 switch (d.day())
00542 {
00543 case 29:
00544
00545
00546 d.setYMD(d.year(), d.month(), 28);
00547 break;
00548 case 28:
00549 if (d.month() != 2 || mFeb29Type != FEB29_FEB28 || QDate::leapYear(d.year()))
00550 {
00551
00552 d.setYMD(d.year(), d.month(), 27);
00553 }
00554 break;
00555 case 1:
00556 if (d.month() == 3 && mFeb29Type == FEB29_MAR1 && !QDate::leapYear(d.year()))
00557 {
00558
00559
00560 d.setYMD(d.year(), 2, 28);
00561 }
00562 break;
00563 default:
00564 break;
00565 }
00566 dt.setDate(d);
00567 rrule->setStartDt(dt);
00568 rrule->setFloats(doesFloat());
00569 rrule->setFrequency(frequency());
00570 rrule->setDuration(duration());
00571 QValueList<int> ds;
00572 ds.append(28);
00573 rrule->setByMonthDays(ds);
00574 rrule->setByMonths(defaultRRuleConst()->byMonths());
00575 dt = rrule->endDt();
00576 delete rrule;
00577
00578
00579
00580 if (mFeb29Type == FEB29_FEB28 && dt.date().month() == 2 && !QDate::leapYear(dt.date().year()))
00581 return dt;
00582 return dt.addDays(1);
00583 }
00584
00585
00586
00587
00588 QDate KARecurrence::endDate() const
00589 {
00590 QDateTime end = endDateTime();
00591 return end.isValid() ? end.date() : QDate();
00592 }
00593
00594
00595
00596
00597
00598 bool KARecurrence::recursOn(const QDate& dt) const
00599 {
00600 if (!Recurrence::recursOn(dt))
00601 return false;
00602 if (dt != startDate())
00603 return true;
00604
00605
00606 if (rDates().contains(dt))
00607 return true;
00608 RecurrenceRule::List rulelist = rRules();
00609 for (RecurrenceRule::List::ConstIterator rr = rulelist.begin(); rr != rulelist.end(); ++rr)
00610 if ((*rr)->recursOn(dt))
00611 return true;
00612 DateTimeList dtlist = rDateTimes();
00613 for (DateTimeList::ConstIterator rdt = dtlist.begin(); rdt != dtlist.end(); ++rdt)
00614 if ((*rdt).date() == dt)
00615 return true;
00616 return false;
00617 }
00618
00619
00620
00621
00622
00623 int KARecurrence::combineDurations(const RecurrenceRule* rrule1, const RecurrenceRule* rrule2, QDate& end) const
00624 {
00625 int count1 = rrule1->duration();
00626 int count2 = rrule2->duration();
00627 if (count1 == -1 && count2 == -1)
00628 return -1;
00629
00630
00631
00632 if (count1 && !count2 && rrule2->endDt().date() == startDateTime().date())
00633 return count1;
00634 if (count2 && !count1 && rrule1->endDt().date() == startDateTime().date())
00635 return count2;
00636
00637
00638
00639
00640
00641 if (!count1 || !count2)
00642 count1 = count2 = 0;
00643
00644 QDateTime end1 = rrule1->endDt();
00645 QDateTime end2 = rrule2->endDt();
00646 if (end1.date() == end2.date())
00647 {
00648 end = end1.date();
00649 return count1 + count2;
00650 }
00651 const RecurrenceRule* rr1;
00652 const RecurrenceRule* rr2;
00653 if (end2.isValid()
00654 && (!end1.isValid() || end1.date() > end2.date()))
00655 {
00656
00657 rr1 = rrule2;
00658 rr2 = rrule1;
00659 QDateTime e = end1;
00660 end1 = end2;
00661 end2 = e;
00662 }
00663 else
00664 {
00665 rr1 = rrule1;
00666 rr2 = rrule2;
00667 }
00668
00669
00670 RecurrenceRule rr(*rr1);
00671 rr.setDuration(-1);
00672 QDateTime next1(rr.getNextDate(end1).date());
00673 if (!next1.isValid())
00674 end = end1.date();
00675 else
00676 {
00677 if (end2.isValid() && next1 > end2)
00678 {
00679
00680
00681
00682 end = end2.date();
00683 return count1 + count2;
00684 }
00685 QDate prev2 = rr2->getPreviousDate(next1).date();
00686 end = (prev2 > end1.date()) ? prev2 : end1.date();
00687 }
00688 if (count2)
00689 count2 = rr2->durationTo(end);
00690 return count1 + count2;
00691 }
00692
00693
00694
00695
00696
00697 int KARecurrence::longestInterval() const
00698 {
00699 int freq = frequency();
00700 switch (type())
00701 {
00702 case MINUTELY:
00703 return freq;
00704
00705 case DAILY:
00706 {
00707 QValueList<RecurrenceRule::WDayPos> days = defaultRRuleConst()->byDays();
00708 if (days.isEmpty())
00709 return freq * 1440;
00710
00711
00712
00713 bool ds[7] = { false, false, false, false, false, false, false };
00714 for (QValueList<RecurrenceRule::WDayPos>::ConstIterator it = days.begin(); it != days.end(); ++it)
00715 if ((*it).pos() == 0)
00716 ds[(*it).day() - 1] = true;
00717 if (freq % 7)
00718 {
00719
00720
00721 int first = -1;
00722 int last = -1;
00723 int maxgap = 1;
00724 for (int i = 0; i < freq*7; i += freq)
00725 {
00726 if (ds[i % 7])
00727 {
00728 if (first < 0)
00729 first = i;
00730 else if (i - last > maxgap)
00731 maxgap = i - last;
00732 last = i;
00733 }
00734 }
00735 int wrap = freq*7 - last + first;
00736 if (wrap > maxgap)
00737 maxgap = wrap;
00738 return maxgap * 1440;
00739 }
00740 else
00741 {
00742
00743
00744 return ds[startDate().dayOfWeek() - 1] ? freq * 1440 : 0;
00745 }
00746 }
00747 case WEEKLY:
00748 {
00749
00750
00751 QBitArray ds = days();
00752 int first = -1;
00753 int last = -1;
00754 int maxgap = 1;
00755 for (int i = 0; i < 7; ++i)
00756 {
00757 if (ds.testBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1))
00758 {
00759 if (first < 0)
00760 first = i;
00761 else if (i - last > maxgap)
00762 maxgap = i - last;
00763 last = i;
00764 }
00765 }
00766 if (first < 0)
00767 break;
00768 int span = last - first;
00769 if (freq > 1)
00770 return (freq*7 - span) * 1440;
00771 if (7 - span > maxgap)
00772 return (7 - span) * 1440;
00773 return maxgap * 1440;
00774 }
00775 case MONTHLY_DAY:
00776 case MONTHLY_POS:
00777 return freq * 1440 * 31;
00778
00779 case ANNUAL_DATE:
00780 case ANNUAL_POS:
00781 {
00782
00783
00784 const QValueList<int> months = yearMonths();
00785 if (months.isEmpty())
00786 break;
00787 if (months.count() > 1)
00788 {
00789 int first = -1;
00790 int last = -1;
00791 int maxgap = 0;
00792 for (QValueListConstIterator<int> it = months.begin(); it != months.end(); ++it)
00793 {
00794 if (first < 0)
00795 first = *it;
00796 else
00797 {
00798 int span = QDate(2001, last, 1).daysTo(QDate(2001, *it, 1));
00799 if (span > maxgap)
00800 maxgap = span;
00801 }
00802 last = *it;
00803 }
00804 int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1));
00805 if (freq > 1)
00806 return (freq*365 - span) * 1440;
00807 if (365 - span > maxgap)
00808 return (365 - span) * 1440;
00809 return maxgap * 1440;
00810 }
00811
00812 }
00813 default:
00814 break;
00815 }
00816 return 0;
00817 }
00818
00819
00820
00821
00822 KARecurrence::Type KARecurrence::type() const
00823 {
00824 if (mCachedType == -1)
00825 mCachedType = type(defaultRRuleConst());
00826 return static_cast<Type>(mCachedType);
00827 }
00828
00829 KARecurrence::Type KARecurrence::type(const RecurrenceRule* rrule)
00830 {
00831 switch (recurrenceType(rrule))
00832 {
00833 case rMinutely: return MINUTELY;
00834 case rDaily: return DAILY;
00835 case rWeekly: return WEEKLY;
00836 case rMonthlyDay: return MONTHLY_DAY;
00837 case rMonthlyPos: return MONTHLY_POS;
00838 case rYearlyMonth: return ANNUAL_DATE;
00839 case rYearlyPos: return ANNUAL_POS;
00840 default:
00841 if (dailyType(rrule))
00842 return DAILY;
00843 return NO_RECUR;
00844 }
00845 }
00846
00847
00848
00849
00850 bool KARecurrence::dailyType(const RecurrenceRule* rrule)
00851 {
00852 if (rrule->recurrenceType() != RecurrenceRule::rDaily
00853 || !rrule->bySeconds().isEmpty()
00854 || !rrule->byMinutes().isEmpty()
00855 || !rrule->byHours().isEmpty()
00856 || !rrule->byWeekNumbers().isEmpty()
00857 || !rrule->byMonthDays().isEmpty()
00858 || !rrule->byMonths().isEmpty()
00859 || !rrule->bySetPos().isEmpty()
00860 || !rrule->byYearDays().isEmpty())
00861 return false;
00862 QValueList<RecurrenceRule::WDayPos> days = rrule->byDays();
00863 if (days.isEmpty())
00864 return true;
00865
00866 bool found = false;
00867 for (QValueList<RecurrenceRule::WDayPos>::ConstIterator it = days.begin(); it != days.end(); ++it)
00868 {
00869 if ((*it).pos() != 0)
00870 return false;
00871 found = true;
00872 }
00873 return found;
00874
00875 }