23 #include "karecurrence.h"
25 #ifndef KALARMCAL_USE_KRESOURCES
26 #include <kcalcore/recurrence.h>
29 #include <kcal/recurrence.h>
30 #include <kcal/icalformat.h>
34 #include <klocalizedstring.h>
41 #ifndef KALARMCAL_USE_KRESOURCES
42 using namespace KCalCore;
53 using Recurrence::setNewRecurrenceType;
56 Recurrence_p(
const Recurrence_p& r) :
Recurrence(r) {}
59 class KARecurrence::Private
63 : mFeb29Type(Feb29_None), mCachedType(-1) {}
65 : mRecurrence(r), mFeb29Type(Feb29_None), mCachedType(-1) {}
69 mFeb29Type = Feb29_None;
72 bool set(
Type,
int freq,
int count,
int f29,
const KDateTime& start,
const KDateTime& end);
75 void writeRecurrence(
const KARecurrence* q,
Recurrence& recur)
const;
76 KDateTime endDateTime()
const;
79 static Feb29Type mDefaultFeb29;
80 Recurrence_p mRecurrence;
82 mutable int mCachedType;
103 KARecurrence::Feb29Type KARecurrence::Private::mDefaultFeb29 = KARecurrence::Feb29_None;
106 KARecurrence::KARecurrence()
110 KARecurrence::KARecurrence(
const Recurrence& r)
116 KARecurrence::KARecurrence(
const KARecurrence& r)
117 : d(new Private(*r.d))
120 KARecurrence::~KARecurrence()
134 return d->mRecurrence == r.d->mRecurrence
135 && d->mFeb29Type == r.d->mFeb29Type;
140 return d->mFeb29Type;
145 return Private::mDefaultFeb29;
150 Private::mDefaultFeb29 = t;
161 return d->set(t, freq, count, -1, start, end);
166 return d->set(t, freq, count, f29, start, end);
169 bool KARecurrence::Private::set(Type
recurType,
int freq,
int count,
int f29,
const KDateTime& start,
const KDateTime& end)
175 case MINUTELY: rrtype = RecurrenceRule::rMinutely;
break;
176 case DAILY: rrtype = RecurrenceRule::rDaily;
break;
177 case WEEKLY: rrtype = RecurrenceRule::rWeekly;
break;
178 case MONTHLY_DAY: rrtype = RecurrenceRule::rMonthly;
break;
179 case ANNUAL_DATE: rrtype = RecurrenceRule::rYearly;
break;
180 case NO_RECUR: rrtype = RecurrenceRule::rNone;
break;
184 if (!init(rrtype, freq, count, f29, start, end))
191 days.setBit(start.date().dayOfWeek() - 1);
192 mRecurrence.addWeeklyDays(days);
196 mRecurrence.addMonthlyDate(start.date().day());
199 mRecurrence.addYearlyDate(start.date().day());
200 mRecurrence.addYearlyMonth(start.date().month());
214 return d->init(t, freq, count, -1, start, end);
219 return d->init(t, freq, count, f29, start, end);
223 const KDateTime& end)
226 Feb29Type feb29Type = (f29 == -1) ? mDefaultFeb29 : static_cast<Feb29Type>(f29);
229 bool dateOnly = start.isDateOnly();
230 if (!count && ((!dateOnly && !end.isValid())
231 || (dateOnly && !end.date().isValid())))
235 case RecurrenceRule::rMinutely:
236 case RecurrenceRule::rDaily:
237 case RecurrenceRule::rWeekly:
238 case RecurrenceRule::rMonthly:
239 case RecurrenceRule::rYearly:
241 case RecurrenceRule::rNone:
246 mRecurrence.setNewRecurrenceType(recurType, freq);
248 mRecurrence.setDuration(count);
250 mRecurrence.setEndDate(end.date());
252 mRecurrence.setEndDateTime(end);
253 KDateTime startdt = start;
254 if (recurType == RecurrenceRule::rYearly
255 && (feb29Type == Feb29_Feb28 || feb29Type == Feb29_Mar1))
257 int year = startdt.date().year();
258 if (!QDate::isLeapYear(year)
259 && startdt.date().dayOfYear() == (feb29Type == Feb29_Mar1 ? 60 : 59))
268 while (!QDate::isLeapYear(--year)) ;
269 startdt.setDate(QDate(year, 2, 29));
271 mFeb29Type = feb29Type;
273 mRecurrence.setStartDateTime(startdt);
282 static QString RRULE = QLatin1String(
"RRULE:");
284 if (icalRRULE.isEmpty())
287 if (!format.
fromString(d->mRecurrence.defaultRRule(
true),
288 (icalRRULE.startsWith(RRULE) ? icalRRULE.mid(RRULE.length()) : icalRRULE)))
313 void KARecurrence::Private::fix()
316 mFeb29Type = Feb29_None;
318 int days[2] = { 0, 0 };
320 const RecurrenceRule::List rrulelist = mRecurrence.rRules();
322 int rrend = rrulelist.count();
323 for (
int i = 0; i < 2 && rri < rrend; ++i, ++rri)
328 int rtype = mRecurrence.recurrenceType(rrule);
331 case Recurrence::rHourly:
333 rrule->setRecurrenceType(RecurrenceRule::rMinutely);
336 case Recurrence::rMinutely:
337 case Recurrence::rDaily:
338 case Recurrence::rWeekly:
339 case Recurrence::rMonthlyDay:
340 case Recurrence::rMonthlyPos:
341 case Recurrence::rYearlyPos:
345 case Recurrence::rOther:
346 if (dailyType(rrule))
352 case Recurrence::rYearlyDay:
364 QList<int> ds = rrule->byYearDays();
365 if (!ds.isEmpty() && ds.first() == 60)
374 case Recurrence::rYearlyMonth:
376 QList<int> ds = rrule->byMonthDays();
379 int day = ds.first();
384 if (day == days[0] || (day == -1 && days[0] == 60)
393 rrule->setByMonthDays(ds);
398 QList<int> months = rrule->byMonths();
399 if (months.count() != 1 || months.first() != 2)
402 if (day == 29 || day == -1)
422 for ( ; rri < rrend; ++rri)
423 mRecurrence.deleteRRule(rrulelist[rri]);
437 rrules[0] = rrules[1];
444 months = rrules[0]->byMonths();
445 if (months.removeAll(2))
446 rrules[0]->setByMonths(months);
448 count = combineDurations(rrules[0], rrules[1], end);
449 mFeb29Type = (days[1] == 60) ? Feb29_Mar1 : Feb29_Feb28;
451 else if (convert == 1 && days[0] == 60)
455 count = mRecurrence.duration();
457 end = mRecurrence.endDate();
458 mFeb29Type = Feb29_Mar1;
464 mRecurrence.setNewRecurrenceType(RecurrenceRule::rYearly, mRecurrence.frequency());
467 rrule->setByMonths(months);
470 rrule->setByMonthDays(ds);
474 mRecurrence.setEndDate(end);
483 d->writeRecurrence(
this, recur);
490 recur.setExDates(mRecurrence.exDates());
491 recur.setExDateTimes(mRecurrence.exDateTimes());
496 int count = mRecurrence.duration();
497 static_cast<Recurrence_p*
>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq);
505 if (rrule->byDays().isEmpty())
510 recur.defaultRRule(
true)->setByDays(rrule->byDays());
513 recur.defaultRRule(
true)->setByMonthDays(rrule->byMonthDays());
516 recur.defaultRRule(
true)->setByMonths(rrule->byMonths());
517 recur.defaultRRule()->setByDays(rrule->byDays());
521 QList<int> months = rrule->byMonths();
522 QList<int> days = mRecurrence.monthDays();
523 bool special = (mFeb29Type != Feb29_None && !days.isEmpty()
524 && days.first() == 29 && months.removeAll(2));
526 rrule1->setByMonths(months);
527 rrule1->setByMonthDays(days);
534 rrule2->setRecurrenceType(RecurrenceRule::rYearly);
536 rrule2->
setStartDt(mRecurrence.startDateTime());
540 if (mFeb29Type == Feb29_Mar1)
544 rrule2->setByYearDays(ds);
550 rrule2->setByMonthDays(ds);
553 rrule2->setByMonths(ms);
556 if (months.isEmpty())
583 KDateTime end = endDateTime();
585 - (rrule1->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
589 rrule1->
setEndDt(mRecurrence.startDateTime());
591 - (rrule2->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
595 rrule2->
setEndDt(mRecurrence.startDateTime());
609 return d->mRecurrence.startDateTime();
614 return d->mRecurrence.startDate();
619 d->mRecurrence.setStartDateTime(dt);
621 d->mRecurrence.setAllDay(
true);
629 return d->endDateTime();
632 KDateTime KARecurrence::Private::endDateTime()
const
634 if (mFeb29Type == Feb29_None || mRecurrence.duration() <= 1)
641 return mRecurrence.endDateTime();
650 rrule->setRecurrenceType(RecurrenceRule::rYearly);
651 KDateTime dt = mRecurrence.startDateTime();
652 QDate da = dt.date();
658 da.setYMD(da.year(), da.month(), 28);
661 if (da.month() != 2 || mFeb29Type != Feb29_Feb28 || QDate::isLeapYear(da.year()))
664 da.setYMD(da.year(), da.month(), 27);
668 if (da.month() == 3 && mFeb29Type == Feb29_Mar1 && !QDate::isLeapYear(da.year()))
672 da.setYMD(da.year(), 2, 28);
685 rrule->setByMonthDays(ds);
686 rrule->setByMonths(mRecurrence.defaultRRuleConst()->byMonths());
692 if (mFeb29Type == Feb29_Feb28 && dt.date().month() == 2 && !QDate::isLeapYear(dt.date().year()))
694 return dt.addDays(1);
702 KDateTime end = endDateTime();
703 return end.isValid() ? end.date() : QDate();
708 d->mRecurrence.setEndDate(endDate);
713 d->mRecurrence.setEndDateTime(endDateTime);
718 return d->mRecurrence.allDay();
723 d->mRecurrence.setRecurReadOnly(readOnly);
728 return d->mRecurrence.recurReadOnly();
733 return d->mRecurrence.recurs();
738 return d->mRecurrence.days();
743 return d->mRecurrence.monthPositions();
748 return d->mRecurrence.monthDays();
753 return d->mRecurrence.yearDays();
758 return d->mRecurrence.yearDates();
763 return d->mRecurrence.yearMonths();
768 return d->mRecurrence.yearPositions();
773 d->mRecurrence.addWeeklyDays(days);
778 d->mRecurrence.addYearlyDay(day);
783 d->mRecurrence.addYearlyDate(date);
788 d->mRecurrence.addYearlyMonth(month);
793 d->mRecurrence.addYearlyPos(pos, days);
798 d->mRecurrence.addMonthlyPos(pos, days);
803 d->mRecurrence.addMonthlyPos(pos, day);
808 d->mRecurrence.addMonthlyDate(day);
822 writeRecurrence(recur);
826 return d->mRecurrence.getNextDateTime(preDateTime);
841 writeRecurrence(recur);
845 return d->mRecurrence.getPreviousDateTime(afterDateTime);
855 if (!d->mRecurrence.recursOn(dt, timeSpec))
857 if (dt != d->mRecurrence.startDate())
861 if (d->mRecurrence.rDates().contains(dt))
863 const RecurrenceRule::List rulelist = d->mRecurrence.rRules();
864 for (
int rri = 0, rrend = rulelist.count(); rri < rrend; ++rri)
865 if (rulelist[rri]->recursOn(dt, timeSpec))
867 const DateTimeList dtlist = d->mRecurrence.rDateTimes();
868 for (
int dti = 0, dtend = dtlist.count(); dti < dtend; ++dti)
869 if (dtlist[dti].date() == dt)
876 return d->mRecurrence.recursAt(dt);
881 return d->mRecurrence.recurTimesOn(date, timeSpec);
886 return d->mRecurrence.timesInInterval(start, end);
891 return d->mRecurrence.frequency();
896 d->mRecurrence.setFrequency(freq);
901 return d->mRecurrence.duration();
906 d->mRecurrence.setDuration(duration);
911 return d->mRecurrence.durationTo(dt);
916 return d->mRecurrence.durationTo(date);
927 if (count1 == -1 && count2 == -1)
932 if (count1 && !count2 && rrule2->
endDt().date() == mRecurrence.startDateTime().date())
934 if (count2 && !count1 && rrule1->
endDt().date() == mRecurrence.startDateTime().date())
941 if (!count1 || !count2)
944 KDateTime end1 = rrule1->
endDt();
945 KDateTime end2 = rrule2->
endDt();
946 if (end1.date() == end2.date())
949 return count1 + count2;
954 && (!end1.isValid() || end1.date() > end2.date()))
972 KDateTime next1(rr.getNextDate(end1));
973 next1.setDateOnly(
true);
974 if (!next1.isValid())
978 if (end2.isValid() && next1 > end2)
984 return count1 + count2;
987 end = (prev2 > end1.date()) ? prev2 : end1.date();
991 return count1 + count2;
1000 int freq = d->mRecurrence.frequency();
1004 return Duration(freq * 60, Duration::Seconds);
1008 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1010 return Duration(freq, Duration::Days);
1015 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1016 for (
int i = 0, end = days.count(); i < end; ++i)
1017 if (days[i].pos() == 0)
1018 ds[days[i].day() - 1] =
true;
1026 for (
int i = 0; i < freq*7; i += freq)
1032 else if (i - last > maxgap)
1037 int wrap = freq*7 - last + first;
1040 return Duration(maxgap, Duration::Days);
1046 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1047 return Duration(freq, Duration::Days);
1055 QBitArray ds = d->mRecurrence.days();
1061 int weekStart = KGlobal::locale()->weekStartDay() - 1;
1062 for (
int i = 0; i < 7; ++i)
1066 if (ds.testBit((i + weekStart) % 7))
1070 else if (i - last > maxgap)
1077 int span = last - first;
1079 return Duration(freq*7 - span, Duration::Days);
1080 if (7 - span > maxgap)
1081 return Duration(7 - span, Duration::Days);
1082 return Duration(maxgap, Duration::Days);
1086 return Duration(freq * 31, Duration::Days);
1093 const QList<int> months = d->mRecurrence.yearMonths();
1094 if (months.isEmpty())
1096 if (months.count() == 1)
1097 return Duration(freq * 365, Duration::Days);
1101 for (
int i = 0, end = months.count(); i < end; ++i)
1107 int span = QDate(2001, last, 1).daysTo(QDate(2001, months[i], 1));
1113 int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1));
1115 return Duration(freq*365 - span, Duration::Days);
1116 if (365 - span > maxgap)
1117 return Duration(365 - span, Duration::Days);
1118 return Duration(maxgap, Duration::Days);
1133 int freq = d->mRecurrence.frequency();
1137 return Duration(freq * 60, Duration::Seconds);
1140 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1142 return Duration(freq, Duration::Days);
1146 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1147 for (
int i = 0, end = days.count(); i < end; ++i)
1148 if (days[i].pos() == 0)
1149 ds[days[i].day() - 1] =
true;
1154 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1155 return Duration(freq, Duration::Days);
1159 for (
int i = 0; i < 7; ++i)
1163 return Duration(freq, Duration::Days);
1165 return Duration(freq * 7, Duration::Days);
1170 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1172 return Duration(freq * 7, Duration::Days);
1176 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1177 for (
int i = 0, end = days.count(); i < end; ++i)
1178 if (days[i].pos() == 0)
1179 ds[days[i].day() - 1] =
true;
1181 for (
int i = 0; i < 7; ++i)
1187 return Duration(freq, Duration::Days);
1191 return Duration(freq * 7, Duration::Days);
1202 return d->mRecurrence.exDateTimes();
1205 DateList KARecurrence::exDates()
const
1207 return d->mRecurrence.exDates();
1210 void KARecurrence::setExDateTimes(
const DateTimeList& exdates)
1212 d->mRecurrence.setExDateTimes(exdates);
1215 void KARecurrence::setExDates(
const DateList& exdates)
1217 d->mRecurrence.setExDates(exdates);
1220 void KARecurrence::addExDateTime(
const KDateTime& exdate)
1222 d->mRecurrence.addExDateTime(exdate);
1225 void KARecurrence::addExDate(
const QDate& exdate)
1227 d->mRecurrence.addExDate(exdate);
1232 d->mRecurrence.shiftTimes(oldSpec, newSpec);
1237 return d->mRecurrence.defaultRRuleConst();
1245 if (d->mCachedType == -1)
1246 d->mCachedType = type(d->mRecurrence.defaultRRuleConst());
1247 return static_cast<Type>(d->mCachedType);
1255 switch (Recurrence::recurrenceType(rrule))
1257 case Recurrence::rMinutely:
return MINUTELY;
1258 case Recurrence::rDaily:
return DAILY;
1259 case Recurrence::rWeekly:
return WEEKLY;
1260 case Recurrence::rMonthlyDay:
return MONTHLY_DAY;
1261 case Recurrence::rMonthlyPos:
return MONTHLY_POS;
1262 case Recurrence::rYearlyMonth:
return ANNUAL_DATE;
1263 case Recurrence::rYearlyPos:
return ANNUAL_POS;
1265 if (dailyType(rrule))
1276 if (rrule->recurrenceType() != RecurrenceRule::rDaily
1277 || !rrule->bySeconds().isEmpty()
1278 || !rrule->byMinutes().isEmpty()
1279 || !rrule->byHours().isEmpty()
1280 || !rrule->byWeekNumbers().isEmpty()
1281 || !rrule->byMonthDays().isEmpty()
1282 || !rrule->byMonths().isEmpty()
1283 || !rrule->bySetPos().isEmpty()
1284 || !rrule->byYearDays().isEmpty())
1286 const QList<RecurrenceRule::WDayPos> days = rrule->byDays();
1291 for (
int i = 0, end = days.count(); i < end; ++i)
1293 if (days[i].pos() != 0)