23 #include "karecurrence.h"
25 #ifndef KALARMCAL_USE_KRESOURCES
26 #include <kcalcore/recurrence.h>
29 #include <kcal/recurrence.h>
30 #include <kcal/icalformat.h>
39 #ifndef KALARMCAL_USE_KRESOURCES
40 using namespace KCalCore;
51 using Recurrence::setNewRecurrenceType;
54 Recurrence_p(
const Recurrence_p& r) :
Recurrence(r) {}
57 class KARecurrence::Private
61 : mFeb29Type(Feb29_None), mCachedType(-1) {}
63 : mRecurrence(r), mFeb29Type(Feb29_None), mCachedType(-1) {}
67 mFeb29Type = Feb29_None;
70 bool set(
Type,
int freq,
int count,
int f29,
const KDateTime& start,
const KDateTime& end);
73 void writeRecurrence(
const KARecurrence* q,
Recurrence& recur)
const;
74 KDateTime endDateTime()
const;
77 static Feb29Type mDefaultFeb29;
78 Recurrence_p mRecurrence;
80 mutable int mCachedType;
101 KARecurrence::Feb29Type KARecurrence::Private::mDefaultFeb29 = KARecurrence::Feb29_None;
104 KARecurrence::KARecurrence()
108 KARecurrence::KARecurrence(
const Recurrence& r)
114 KARecurrence::KARecurrence(
const KARecurrence& r)
115 : d(new Private(*r.d))
118 KARecurrence::~KARecurrence()
132 return d->mRecurrence == r.d->mRecurrence
133 && d->mFeb29Type == r.d->mFeb29Type;
138 return d->mFeb29Type;
143 return Private::mDefaultFeb29;
148 Private::mDefaultFeb29 = t;
159 return d->set(t, freq, count, -1, start, end);
164 return d->set(t, freq, count, f29, start, end);
167 bool KARecurrence::Private::set(Type
recurType,
int freq,
int count,
int f29,
const KDateTime& start,
const KDateTime& end)
173 case MINUTELY: rrtype = RecurrenceRule::rMinutely;
break;
174 case DAILY: rrtype = RecurrenceRule::rDaily;
break;
175 case WEEKLY: rrtype = RecurrenceRule::rWeekly;
break;
176 case MONTHLY_DAY: rrtype = RecurrenceRule::rMonthly;
break;
177 case ANNUAL_DATE: rrtype = RecurrenceRule::rYearly;
break;
178 case NO_RECUR: rrtype = RecurrenceRule::rNone;
break;
182 if (!init(rrtype, freq, count, f29, start, end))
189 days.setBit(start.date().dayOfWeek() - 1);
190 mRecurrence.addWeeklyDays(days);
194 mRecurrence.addMonthlyDate(start.date().day());
197 mRecurrence.addYearlyDate(start.date().day());
198 mRecurrence.addYearlyMonth(start.date().month());
212 return d->init(t, freq, count, -1, start, end);
217 return d->init(t, freq, count, f29, start, end);
221 const KDateTime& end)
224 Feb29Type feb29Type = (f29 == -1) ? mDefaultFeb29 : static_cast<Feb29Type>(f29);
227 bool dateOnly = start.isDateOnly();
228 if (!count && ((!dateOnly && !end.isValid())
229 || (dateOnly && !end.date().isValid())))
233 case RecurrenceRule::rMinutely:
234 case RecurrenceRule::rDaily:
235 case RecurrenceRule::rWeekly:
236 case RecurrenceRule::rMonthly:
237 case RecurrenceRule::rYearly:
239 case RecurrenceRule::rNone:
244 mRecurrence.setNewRecurrenceType(recurType, freq);
246 mRecurrence.setDuration(count);
248 mRecurrence.setEndDate(end.date());
250 mRecurrence.setEndDateTime(end);
251 KDateTime startdt = start;
252 if (recurType == RecurrenceRule::rYearly
253 && (feb29Type == Feb29_Feb28 || feb29Type == Feb29_Mar1))
255 int year = startdt.date().year();
256 if (!QDate::isLeapYear(year)
257 && startdt.date().dayOfYear() == (feb29Type == Feb29_Mar1 ? 60 : 59))
266 while (!QDate::isLeapYear(--year)) ;
267 startdt.setDate(QDate(year, 2, 29));
269 mFeb29Type = feb29Type;
271 mRecurrence.setStartDateTime(startdt);
280 static QString RRULE = QLatin1String(
"RRULE:");
282 if (icalRRULE.isEmpty())
285 if (!format.
fromString(d->mRecurrence.defaultRRule(
true),
286 (icalRRULE.startsWith(RRULE) ? icalRRULE.mid(RRULE.length()) : icalRRULE)))
311 void KARecurrence::Private::fix()
314 mFeb29Type = Feb29_None;
316 int days[2] = { 0, 0 };
318 RecurrenceRule::List rrulelist = mRecurrence.rRules();
320 int rrend = rrulelist.count();
321 for (
int i = 0; i < 2 && rri < rrend; ++i, ++rri)
326 int rtype = mRecurrence.recurrenceType(rrule);
329 case Recurrence::rHourly:
331 rrule->setRecurrenceType(RecurrenceRule::rMinutely);
334 case Recurrence::rMinutely:
335 case Recurrence::rDaily:
336 case Recurrence::rWeekly:
337 case Recurrence::rMonthlyDay:
338 case Recurrence::rMonthlyPos:
339 case Recurrence::rYearlyPos:
343 case Recurrence::rOther:
344 if (dailyType(rrule))
350 case Recurrence::rYearlyDay:
362 QList<int> ds = rrule->byYearDays();
363 if (!ds.isEmpty() && ds.first() == 60)
372 case Recurrence::rYearlyMonth:
374 QList<int> ds = rrule->byMonthDays();
377 int day = ds.first();
382 if (day == days[0] || (day == -1 && days[0] == 60)
391 rrule->setByMonthDays(ds);
396 QList<int> months = rrule->byMonths();
397 if (months.count() != 1 || months.first() != 2)
400 if (day == 29 || day == -1)
420 for ( ; rri < rrend; ++rri)
421 mRecurrence.deleteRRule(rrulelist[rri]);
435 rrules[0] = rrules[1];
442 months = rrules[0]->byMonths();
443 if (months.removeAll(2))
444 rrules[0]->setByMonths(months);
446 count = combineDurations(rrules[0], rrules[1], end);
447 mFeb29Type = (days[1] == 60) ? Feb29_Mar1 : Feb29_Feb28;
449 else if (convert == 1 && days[0] == 60)
453 count = mRecurrence.duration();
455 end = mRecurrence.endDate();
456 mFeb29Type = Feb29_Mar1;
462 mRecurrence.setNewRecurrenceType(RecurrenceRule::rYearly, mRecurrence.frequency());
465 rrule->setByMonths(months);
468 rrule->setByMonthDays(ds);
472 mRecurrence.setEndDate(end);
481 d->writeRecurrence(
this, recur);
488 recur.setExDates(mRecurrence.exDates());
489 recur.setExDateTimes(mRecurrence.exDateTimes());
494 int count = mRecurrence.duration();
495 static_cast<Recurrence_p*
>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq);
503 if (rrule->byDays().isEmpty())
508 recur.defaultRRule(
true)->setByDays(rrule->byDays());
511 recur.defaultRRule(
true)->setByMonthDays(rrule->byMonthDays());
514 recur.defaultRRule(
true)->setByMonths(rrule->byMonths());
515 recur.defaultRRule()->setByDays(rrule->byDays());
519 QList<int> months = rrule->byMonths();
520 QList<int> days = mRecurrence.monthDays();
521 bool special = (mFeb29Type != Feb29_None && !days.isEmpty()
522 && days.first() == 29 && months.removeAll(2));
524 rrule1->setByMonths(months);
525 rrule1->setByMonthDays(days);
532 rrule2->setRecurrenceType(RecurrenceRule::rYearly);
534 rrule2->
setStartDt(mRecurrence.startDateTime());
538 if (mFeb29Type == Feb29_Mar1)
542 rrule2->setByYearDays(ds);
548 rrule2->setByMonthDays(ds);
551 rrule2->setByMonths(ms);
554 if (months.isEmpty())
581 KDateTime end = endDateTime();
583 - (rrule1->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
587 rrule1->
setEndDt(mRecurrence.startDateTime());
589 - (rrule2->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
593 rrule2->
setEndDt(mRecurrence.startDateTime());
607 return d->mRecurrence.startDateTime();
612 return d->mRecurrence.startDate();
617 d->mRecurrence.setStartDateTime(dt);
619 d->mRecurrence.setAllDay(
true);
627 return d->endDateTime();
630 KDateTime KARecurrence::Private::endDateTime()
const
632 if (mFeb29Type == Feb29_None || mRecurrence.duration() <= 1)
639 return mRecurrence.endDateTime();
648 rrule->setRecurrenceType(RecurrenceRule::rYearly);
649 KDateTime dt = mRecurrence.startDateTime();
650 QDate da = dt.date();
656 da.setYMD(da.year(), da.month(), 28);
659 if (da.month() != 2 || mFeb29Type != Feb29_Feb28 || QDate::isLeapYear(da.year()))
662 da.setYMD(da.year(), da.month(), 27);
666 if (da.month() == 3 && mFeb29Type == Feb29_Mar1 && !QDate::isLeapYear(da.year()))
670 da.setYMD(da.year(), 2, 28);
683 rrule->setByMonthDays(ds);
684 rrule->setByMonths(mRecurrence.defaultRRuleConst()->byMonths());
690 if (mFeb29Type == Feb29_Feb28 && dt.date().month() == 2 && !QDate::isLeapYear(dt.date().year()))
692 return dt.addDays(1);
700 KDateTime end = endDateTime();
701 return end.isValid() ? end.date() : QDate();
706 d->mRecurrence.setEndDate(endDate);
711 d->mRecurrence.setEndDateTime(endDateTime);
716 return d->mRecurrence.allDay();
721 d->mRecurrence.setRecurReadOnly(readOnly);
726 return d->mRecurrence.recurReadOnly();
731 return d->mRecurrence.recurs();
736 return d->mRecurrence.days();
741 return d->mRecurrence.monthPositions();
746 return d->mRecurrence.monthDays();
751 return d->mRecurrence.yearDays();
756 return d->mRecurrence.yearDates();
761 return d->mRecurrence.yearMonths();
766 return d->mRecurrence.yearPositions();
771 d->mRecurrence.addWeeklyDays(days);
776 d->mRecurrence.addYearlyDay(day);
781 d->mRecurrence.addYearlyDate(date);
786 d->mRecurrence.addYearlyMonth(month);
791 d->mRecurrence.addYearlyPos(pos, days);
796 d->mRecurrence.addMonthlyPos(pos, days);
801 d->mRecurrence.addMonthlyPos(pos, day);
806 d->mRecurrence.addMonthlyDate(day);
820 writeRecurrence(recur);
824 return d->mRecurrence.getNextDateTime(preDateTime);
839 writeRecurrence(recur);
843 return d->mRecurrence.getPreviousDateTime(afterDateTime);
853 if (!d->mRecurrence.recursOn(dt, timeSpec))
855 if (dt != d->mRecurrence.startDate())
859 if (d->mRecurrence.rDates().contains(dt))
861 RecurrenceRule::List rulelist = d->mRecurrence.rRules();
862 for (
int rri = 0, rrend = rulelist.count(); rri < rrend; ++rri)
863 if (rulelist[rri]->recursOn(dt, timeSpec))
866 for (
int dti = 0, dtend = dtlist.count(); dti < dtend; ++dti)
867 if (dtlist[dti].date() == dt)
874 return d->mRecurrence.recursAt(dt);
879 return d->mRecurrence.recurTimesOn(date, timeSpec);
884 return d->mRecurrence.timesInInterval(start, end);
889 return d->mRecurrence.frequency();
894 d->mRecurrence.setFrequency(freq);
899 return d->mRecurrence.duration();
904 d->mRecurrence.setDuration(duration);
909 return d->mRecurrence.durationTo(dt);
914 return d->mRecurrence.durationTo(date);
925 if (count1 == -1 && count2 == -1)
930 if (count1 && !count2 && rrule2->
endDt().date() == mRecurrence.startDateTime().date())
932 if (count2 && !count1 && rrule1->
endDt().date() == mRecurrence.startDateTime().date())
939 if (!count1 || !count2)
942 KDateTime end1 = rrule1->
endDt();
943 KDateTime end2 = rrule2->
endDt();
944 if (end1.date() == end2.date())
947 return count1 + count2;
952 && (!end1.isValid() || end1.date() > end2.date()))
970 KDateTime next1(rr.getNextDate(end1));
971 next1.setDateOnly(
true);
972 if (!next1.isValid())
976 if (end2.isValid() && next1 > end2)
982 return count1 + count2;
985 end = (prev2 > end1.date()) ? prev2 : end1.date();
989 return count1 + count2;
998 int freq = d->mRecurrence.frequency();
1002 return Duration(freq * 60, Duration::Seconds);
1006 QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1008 return Duration(freq, Duration::Days);
1013 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1014 for (
int i = 0, end = days.count(); i < end; ++i)
1015 if (days[i].pos() == 0)
1016 ds[days[i].day() - 1] =
true;
1024 for (
int i = 0; i < freq*7; i += freq)
1030 else if (i - last > maxgap)
1035 int wrap = freq*7 - last + first;
1038 return Duration(maxgap, Duration::Days);
1044 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1045 return Duration(freq, Duration::Days);
1053 QBitArray ds = d->mRecurrence.days();
1059 int weekStart = KGlobal::locale()->weekStartDay() - 1;
1060 for (
int i = 0; i < 7; ++i)
1064 if (ds.testBit((i + weekStart) % 7))
1068 else if (i - last > maxgap)
1075 int span = last - first;
1077 return Duration(freq*7 - span, Duration::Days);
1078 if (7 - span > maxgap)
1079 return Duration(7 - span, Duration::Days);
1080 return Duration(maxgap, Duration::Days);
1084 return Duration(freq * 31, Duration::Days);
1091 const QList<int> months = d->mRecurrence.yearMonths();
1092 if (months.isEmpty())
1094 if (months.count() == 1)
1095 return Duration(freq * 365, Duration::Days);
1099 for (
int i = 0, end = months.count(); i < end; ++i)
1105 int span = QDate(2001, last, 1).daysTo(QDate(2001, months[i], 1));
1111 int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1));
1113 return Duration(freq*365 - span, Duration::Days);
1114 if (365 - span > maxgap)
1115 return Duration(365 - span, Duration::Days);
1116 return Duration(maxgap, Duration::Days);
1131 int freq = d->mRecurrence.frequency();
1135 return Duration(freq * 60, Duration::Seconds);
1138 QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1140 return Duration(freq, Duration::Days);
1144 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1145 for (
int i = 0, end = days.count(); i < end; ++i)
1146 if (days[i].pos() == 0)
1147 ds[days[i].day() - 1] =
true;
1152 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1153 return Duration(freq, Duration::Days);
1157 for (
int i = 0; i < 7; ++i)
1161 return Duration(freq, Duration::Days);
1163 return Duration(freq * 7, Duration::Days);
1168 QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1170 return Duration(freq * 7, Duration::Days);
1174 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1175 for (
int i = 0, end = days.count(); i < end; ++i)
1176 if (days[i].pos() == 0)
1177 ds[days[i].day() - 1] =
true;
1179 for (
int i = 0; i < 7; ++i)
1185 return Duration(freq, Duration::Days);
1189 return Duration(freq * 7, Duration::Days);
1200 return d->mRecurrence.exDateTimes();
1203 DateList KARecurrence::exDates()
const
1205 return d->mRecurrence.exDates();
1208 void KARecurrence::setExDateTimes(
const DateTimeList& exdates)
1210 d->mRecurrence.setExDateTimes(exdates);
1213 void KARecurrence::setExDates(
const DateList& exdates)
1215 d->mRecurrence.setExDates(exdates);
1218 void KARecurrence::addExDateTime(
const KDateTime& exdate)
1220 d->mRecurrence.addExDateTime(exdate);
1223 void KARecurrence::addExDate(
const QDate& exdate)
1225 d->mRecurrence.addExDate(exdate);
1230 d->mRecurrence.shiftTimes(oldSpec, newSpec);
1235 return d->mRecurrence.defaultRRuleConst();
1243 if (d->mCachedType == -1)
1244 d->mCachedType = type(d->mRecurrence.defaultRRuleConst());
1245 return static_cast<Type>(d->mCachedType);
1253 switch (Recurrence::recurrenceType(rrule))
1255 case Recurrence::rMinutely:
return MINUTELY;
1256 case Recurrence::rDaily:
return DAILY;
1257 case Recurrence::rWeekly:
return WEEKLY;
1258 case Recurrence::rMonthlyDay:
return MONTHLY_DAY;
1259 case Recurrence::rMonthlyPos:
return MONTHLY_POS;
1260 case Recurrence::rYearlyMonth:
return ANNUAL_DATE;
1261 case Recurrence::rYearlyPos:
return ANNUAL_POS;
1263 if (dailyType(rrule))
1274 if (rrule->recurrenceType() != RecurrenceRule::rDaily
1275 || !rrule->bySeconds().isEmpty()
1276 || !rrule->byMinutes().isEmpty()
1277 || !rrule->byHours().isEmpty()
1278 || !rrule->byWeekNumbers().isEmpty()
1279 || !rrule->byMonthDays().isEmpty()
1280 || !rrule->byMonths().isEmpty()
1281 || !rrule->bySetPos().isEmpty()
1282 || !rrule->byYearDays().isEmpty())
1284 QList<RecurrenceRule::WDayPos> days = rrule->byDays();
1289 for (
int i = 0, end = days.count(); i < end; ++i)
1291 if (days[i].pos() != 0)