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 {
00300 removeRRule(*rr);
00301 delete *rr;
00302 }
00303
00304 QDate end;
00305 int count;
00306 QValueList<int> months;
00307 if (convert == 2)
00308 {
00309
00310
00311
00312 if (days[0] != 29)
00313 {
00314
00315 RecurrenceRule* rr = rrules[0];
00316 rrules[0] = rrules[1];
00317 rrules[1] = rr;
00318 int d = days[0];
00319 days[0] = days[1];
00320 days[1] = d;
00321 }
00322
00323 months = rrules[0]->byMonths();
00324 if (months.remove(2))
00325 rrules[0]->setByMonths(months);
00326
00327 count = combineDurations(rrules[0], rrules[1], end);
00328 mFeb29Type = (days[1] == 60) ? FEB29_MAR1 : FEB29_FEB28;
00329 }
00330 else if (convert == 1 && days[0] == 60)
00331 {
00332
00333
00334 count = duration();
00335 if (!count)
00336 end = endDate();
00337 mFeb29Type = FEB29_MAR1;
00338 }
00339 else
00340 return;
00341
00342
00343 setNewRecurrenceType(RecurrenceRule::rYearly, frequency());
00344 RecurrenceRule* rrule = defaultRRule();
00345 months.append(2);
00346 rrule->setByMonths(months);
00347 QValueList<int> ds;
00348 ds.append(29);
00349 rrule->setByMonthDays(ds);
00350 if (count)
00351 setDuration(count);
00352 else
00353 setEndDate(end);
00354 }
00355
00356
00357
00358
00359 QDateTime KARecurrence::getNextDateTime(const QDateTime& preDateTime) const
00360 {
00361 switch (type())
00362 {
00363 case ANNUAL_DATE:
00364 case ANNUAL_POS:
00365 {
00366 Recurrence recur;
00367 writeRecurrence(recur);
00368 return recur.getNextDateTime(preDateTime);
00369 }
00370 default:
00371 return Recurrence::getNextDateTime(preDateTime);
00372 }
00373 }
00374
00375
00376
00377
00378 QDateTime KARecurrence::getPreviousDateTime(const QDateTime& afterDateTime) const
00379 {
00380 switch (type())
00381 {
00382 case ANNUAL_DATE:
00383 case ANNUAL_POS:
00384 {
00385 Recurrence recur;
00386 writeRecurrence(recur);
00387 return recur.getPreviousDateTime(afterDateTime);
00388 }
00389 default:
00390 return Recurrence::getPreviousDateTime(afterDateTime);
00391 }
00392 }
00393
00394
00395
00396
00397
00398 void KARecurrence::writeRecurrence(KCal::Recurrence& recur) const
00399 {
00400 recur.clear();
00401 recur.setStartDateTime(startDateTime());
00402 recur.setExDates(exDates());
00403 recur.setExDateTimes(exDateTimes());
00404 const RecurrenceRule* rrule = defaultRRuleConst();
00405 if (!rrule)
00406 return;
00407 int freq = frequency();
00408 int count = duration();
00409 static_cast<KARecurrence*>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq);
00410 if (count)
00411 recur.setDuration(count);
00412 else
00413 recur.setEndDateTime(endDateTime());
00414 switch (type())
00415 {
00416 case DAILY:
00417 if (rrule->byDays().isEmpty())
00418 break;
00419
00420 case WEEKLY:
00421 case MONTHLY_POS:
00422 recur.defaultRRule(true)->setByDays(rrule->byDays());
00423 break;
00424 case MONTHLY_DAY:
00425 recur.defaultRRule(true)->setByMonthDays(rrule->byMonthDays());
00426 break;
00427 case ANNUAL_POS:
00428 recur.defaultRRule(true)->setByMonths(rrule->byMonths());
00429 recur.defaultRRule()->setByDays(rrule->byDays());
00430 break;
00431 case ANNUAL_DATE:
00432 {
00433 QValueList<int> months = rrule->byMonths();
00434 QValueList<int> days = monthDays();
00435 bool special = (mFeb29Type != FEB29_FEB29 && !days.isEmpty()
00436 && days.first() == 29 && months.remove(2));
00437 RecurrenceRule* rrule1 = recur.defaultRRule();
00438 rrule1->setByMonths(months);
00439 rrule1->setByMonthDays(days);
00440 if (!special)
00441 break;
00442
00443
00444
00445 RecurrenceRule* rrule2 = new RecurrenceRule();
00446 rrule2->setRecurrenceType(RecurrenceRule::rYearly);
00447 rrule2->setFrequency(freq);
00448 rrule2->setStartDt(startDateTime());
00449 rrule2->setFloats(doesFloat());
00450 if (!count)
00451 rrule2->setEndDt(endDateTime());
00452 if (mFeb29Type == FEB29_MAR1)
00453 {
00454 QValueList<int> ds;
00455 ds.append(60);
00456 rrule2->setByYearDays(ds);
00457 }
00458 else
00459 {
00460 QValueList<int> ds;
00461 ds.append(-1);
00462 rrule2->setByMonthDays(ds);
00463 QValueList<int> ms;
00464 ms.append(2);
00465 rrule2->setByMonths(ms);
00466 }
00467
00468 if (months.isEmpty())
00469 {
00470
00471
00472 if (count)
00473 rrule2->setDuration(count);
00474 recur.unsetRecurs();
00475 }
00476 else
00477 {
00478
00479
00480 if (count)
00481 {
00482 rrule1->setDuration(-1);
00483 rrule2->setDuration(-1);
00484 if (count > 0)
00485 {
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495 QDateTime end = endDateTime();
00496 kdDebug()<<"29th recurrence: count="<<count<<", end date="<<end.toString()<<endl;
00497 int count1 = rrule1->durationTo(end)
00498 - (rrule1->recursOn(startDate()) ? 0 : 1);
00499 if (count1 > 0)
00500 rrule1->setDuration(count1);
00501 else
00502 rrule1->setEndDt(startDateTime());
00503 int count2 = rrule2->durationTo(end)
00504 - (rrule2->recursOn(startDate()) ? 0 : 1);
00505 if (count2 > 0)
00506 rrule2->setDuration(count2);
00507 else
00508 rrule2->setEndDt(startDateTime());
00509 }
00510 }
00511 }
00512 recur.addRRule(rrule2);
00513 break;
00514 }
00515 default:
00516 break;
00517 }
00518 }
00519
00520
00521
00522
00523 QDateTime KARecurrence::endDateTime() const
00524 {
00525 if (mFeb29Type == FEB29_FEB29 || duration() <= 1)
00526 {
00527
00528
00529
00530
00531
00532 return Recurrence::endDateTime();
00533 }
00534
00535
00536
00537
00538
00539
00540 RecurrenceRule* rrule = new RecurrenceRule();
00541 rrule->setRecurrenceType(RecurrenceRule::rYearly);
00542 QDateTime dt = startDateTime();
00543 QDate d = dt.date();
00544 switch (d.day())
00545 {
00546 case 29:
00547
00548
00549 d.setYMD(d.year(), d.month(), 28);
00550 break;
00551 case 28:
00552 if (d.month() != 2 || mFeb29Type != FEB29_FEB28 || QDate::leapYear(d.year()))
00553 {
00554
00555 d.setYMD(d.year(), d.month(), 27);
00556 }
00557 break;
00558 case 1:
00559 if (d.month() == 3 && mFeb29Type == FEB29_MAR1 && !QDate::leapYear(d.year()))
00560 {
00561
00562
00563 d.setYMD(d.year(), 2, 28);
00564 }
00565 break;
00566 default:
00567 break;
00568 }
00569 dt.setDate(d);
00570 rrule->setStartDt(dt);
00571 rrule->setFloats(doesFloat());
00572 rrule->setFrequency(frequency());
00573 rrule->setDuration(duration());
00574 QValueList<int> ds;
00575 ds.append(28);
00576 rrule->setByMonthDays(ds);
00577 rrule->setByMonths(defaultRRuleConst()->byMonths());
00578 dt = rrule->endDt();
00579 delete rrule;
00580
00581
00582
00583 if (mFeb29Type == FEB29_FEB28 && dt.date().month() == 2 && !QDate::leapYear(dt.date().year()))
00584 return dt;
00585 return dt.addDays(1);
00586 }
00587
00588
00589
00590
00591 QDate KARecurrence::endDate() const
00592 {
00593 QDateTime end = endDateTime();
00594 return end.isValid() ? end.date() : QDate();
00595 }
00596
00597
00598
00599
00600
00601 bool KARecurrence::recursOn(const QDate& dt) const
00602 {
00603 if (!Recurrence::recursOn(dt))
00604 return false;
00605 if (dt != startDate())
00606 return true;
00607
00608
00609 if (rDates().contains(dt))
00610 return true;
00611 RecurrenceRule::List rulelist = rRules();
00612 for (RecurrenceRule::List::ConstIterator rr = rulelist.begin(); rr != rulelist.end(); ++rr)
00613 if ((*rr)->recursOn(dt))
00614 return true;
00615 DateTimeList dtlist = rDateTimes();
00616 for (DateTimeList::ConstIterator rdt = dtlist.begin(); rdt != dtlist.end(); ++rdt)
00617 if ((*rdt).date() == dt)
00618 return true;
00619 return false;
00620 }
00621
00622
00623
00624
00625
00626 int KARecurrence::combineDurations(const RecurrenceRule* rrule1, const RecurrenceRule* rrule2, QDate& end) const
00627 {
00628 int count1 = rrule1->duration();
00629 int count2 = rrule2->duration();
00630 if (count1 == -1 && count2 == -1)
00631 return -1;
00632
00633
00634
00635 if (count1 && !count2 && rrule2->endDt().date() == startDateTime().date())
00636 return count1;
00637 if (count2 && !count1 && rrule1->endDt().date() == startDateTime().date())
00638 return count2;
00639
00640
00641
00642
00643
00644 if (!count1 || !count2)
00645 count1 = count2 = 0;
00646
00647 QDateTime end1 = rrule1->endDt();
00648 QDateTime end2 = rrule2->endDt();
00649 if (end1.date() == end2.date())
00650 {
00651 end = end1.date();
00652 return count1 + count2;
00653 }
00654 const RecurrenceRule* rr1;
00655 const RecurrenceRule* rr2;
00656 if (end2.isValid()
00657 && (!end1.isValid() || end1.date() > end2.date()))
00658 {
00659
00660 rr1 = rrule2;
00661 rr2 = rrule1;
00662 QDateTime e = end1;
00663 end1 = end2;
00664 end2 = e;
00665 }
00666 else
00667 {
00668 rr1 = rrule1;
00669 rr2 = rrule2;
00670 }
00671
00672
00673 RecurrenceRule rr(*rr1);
00674 rr.setDuration(-1);
00675 QDateTime next1(rr.getNextDate(end1).date());
00676 if (!next1.isValid())
00677 end = end1.date();
00678 else
00679 {
00680 if (end2.isValid() && next1 > end2)
00681 {
00682
00683
00684
00685 end = end2.date();
00686 return count1 + count2;
00687 }
00688 QDate prev2 = rr2->getPreviousDate(next1).date();
00689 end = (prev2 > end1.date()) ? prev2 : end1.date();
00690 }
00691 if (count2)
00692 count2 = rr2->durationTo(end);
00693 return count1 + count2;
00694 }
00695
00696
00697
00698
00699
00700 int KARecurrence::longestInterval() const
00701 {
00702 int freq = frequency();
00703 switch (type())
00704 {
00705 case MINUTELY:
00706 return freq;
00707
00708 case DAILY:
00709 {
00710 QValueList<RecurrenceRule::WDayPos> days = defaultRRuleConst()->byDays();
00711 if (days.isEmpty())
00712 return freq * 1440;
00713
00714
00715
00716 bool ds[7] = { false, false, false, false, false, false, false };
00717 for (QValueList<RecurrenceRule::WDayPos>::ConstIterator it = days.begin(); it != days.end(); ++it)
00718 if ((*it).pos() == 0)
00719 ds[(*it).day() - 1] = true;
00720 if (freq % 7)
00721 {
00722
00723
00724 int first = -1;
00725 int last = -1;
00726 int maxgap = 1;
00727 for (int i = 0; i < freq*7; i += freq)
00728 {
00729 if (ds[i % 7])
00730 {
00731 if (first < 0)
00732 first = i;
00733 else if (i - last > maxgap)
00734 maxgap = i - last;
00735 last = i;
00736 }
00737 }
00738 int wrap = freq*7 - last + first;
00739 if (wrap > maxgap)
00740 maxgap = wrap;
00741 return maxgap * 1440;
00742 }
00743 else
00744 {
00745
00746
00747 return ds[startDate().dayOfWeek() - 1] ? freq * 1440 : 0;
00748 }
00749 }
00750 case WEEKLY:
00751 {
00752
00753
00754 QBitArray ds = days();
00755 int first = -1;
00756 int last = -1;
00757 int maxgap = 1;
00758 for (int i = 0; i < 7; ++i)
00759 {
00760 if (ds.testBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1))
00761 {
00762 if (first < 0)
00763 first = i;
00764 else if (i - last > maxgap)
00765 maxgap = i - last;
00766 last = i;
00767 }
00768 }
00769 if (first < 0)
00770 break;
00771 int span = last - first;
00772 if (freq > 1)
00773 return (freq*7 - span) * 1440;
00774 if (7 - span > maxgap)
00775 return (7 - span) * 1440;
00776 return maxgap * 1440;
00777 }
00778 case MONTHLY_DAY:
00779 case MONTHLY_POS:
00780 return freq * 1440 * 31;
00781
00782 case ANNUAL_DATE:
00783 case ANNUAL_POS:
00784 {
00785
00786
00787 const QValueList<int> months = yearMonths();
00788 if (months.isEmpty())
00789 break;
00790 if (months.count() == 1)
00791 return freq * 1440 * 365;
00792 int first = -1;
00793 int last = -1;
00794 int maxgap = 0;
00795 for (QValueListConstIterator<int> it = months.begin(); it != months.end(); ++it)
00796 {
00797 if (first < 0)
00798 first = *it;
00799 else
00800 {
00801 int span = QDate(2001, last, 1).daysTo(QDate(2001, *it, 1));
00802 if (span > maxgap)
00803 maxgap = span;
00804 }
00805 last = *it;
00806 }
00807 int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1));
00808 if (freq > 1)
00809 return (freq*365 - span) * 1440;
00810 if (365 - span > maxgap)
00811 return (365 - span) * 1440;
00812 return maxgap * 1440;
00813 }
00814 default:
00815 break;
00816 }
00817 return 0;
00818 }
00819
00820
00821
00822
00823 KARecurrence::Type KARecurrence::type() const
00824 {
00825 if (mCachedType == -1)
00826 mCachedType = type(defaultRRuleConst());
00827 return static_cast<Type>(mCachedType);
00828 }
00829
00830 KARecurrence::Type KARecurrence::type(const RecurrenceRule* rrule)
00831 {
00832 switch (recurrenceType(rrule))
00833 {
00834 case rMinutely: return MINUTELY;
00835 case rDaily: return DAILY;
00836 case rWeekly: return WEEKLY;
00837 case rMonthlyDay: return MONTHLY_DAY;
00838 case rMonthlyPos: return MONTHLY_POS;
00839 case rYearlyMonth: return ANNUAL_DATE;
00840 case rYearlyPos: return ANNUAL_POS;
00841 default:
00842 if (dailyType(rrule))
00843 return DAILY;
00844 return NO_RECUR;
00845 }
00846 }
00847
00848
00849
00850
00851 bool KARecurrence::dailyType(const RecurrenceRule* rrule)
00852 {
00853 if (rrule->recurrenceType() != RecurrenceRule::rDaily
00854 || !rrule->bySeconds().isEmpty()
00855 || !rrule->byMinutes().isEmpty()
00856 || !rrule->byHours().isEmpty()
00857 || !rrule->byWeekNumbers().isEmpty()
00858 || !rrule->byMonthDays().isEmpty()
00859 || !rrule->byMonths().isEmpty()
00860 || !rrule->bySetPos().isEmpty()
00861 || !rrule->byYearDays().isEmpty())
00862 return false;
00863 QValueList<RecurrenceRule::WDayPos> days = rrule->byDays();
00864 if (days.isEmpty())
00865 return true;
00866
00867 bool found = false;
00868 for (QValueList<RecurrenceRule::WDayPos>::ConstIterator it = days.begin(); it != days.end(); ++it)
00869 {
00870 if ((*it).pos() != 0)
00871 return false;
00872 found = true;
00873 }
00874 return found;
00875
00876 }