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>
39#ifndef KALARMCAL_USE_KRESOURCES
51 using Recurrence::setNewRecurrenceType;
54 Recurrence_p(
const Recurrence_p& r) :
Recurrence(r) {}
57class KARecurrence::Private
63 : mRecurrence(r), mFeb29Type(
Feb29_None), mCachedType(-1) {}
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;
101KARecurrence::KARecurrence()
105KARecurrence::KARecurrence(
const Recurrence& r)
111KARecurrence::KARecurrence(
const KARecurrence& r)
112 : d(new Private(*r.d))
115KARecurrence::~KARecurrence()
129 return d->mRecurrence == r.d->mRecurrence
130 && d->mFeb29Type == r.d->mFeb29Type;
135 return d->mFeb29Type;
140 return Private::mDefaultFeb29;
145 Private::mDefaultFeb29 = t;
154bool KARecurrence::set(
Type t,
int freq,
int count,
const KDateTime& start,
const KDateTime& end)
156 return d->set(t, freq, count, -1, start, end);
159bool KARecurrence::set(
Type t,
int freq,
int count,
const KDateTime& start,
const KDateTime& end,
Feb29Type f29)
161 return d->set(t, freq, count, f29, start, end);
164bool KARecurrence::Private::set(Type recurType,
int freq,
int count,
int f29,
const KDateTime& start,
const KDateTime& end)
170 case MINUTELY: rrtype = RecurrenceRule::rMinutely;
break;
171 case DAILY: rrtype = RecurrenceRule::rDaily;
break;
172 case WEEKLY: rrtype = RecurrenceRule::rWeekly;
break;
173 case MONTHLY_DAY: rrtype = RecurrenceRule::rMonthly;
break;
174 case ANNUAL_DATE: rrtype = RecurrenceRule::rYearly;
break;
175 case NO_RECUR: rrtype = RecurrenceRule::rNone;
break;
179 if (!init(rrtype, freq, count, f29, start, end))
186 days.setBit(start.date().dayOfWeek() - 1);
187 mRecurrence.addWeeklyDays(days);
191 mRecurrence.addMonthlyDate(start.date().day());
194 mRecurrence.addYearlyDate(start.date().day());
195 mRecurrence.addYearlyMonth(start.date().month());
209 return d->init(t, freq, count, -1, start, end);
214 return d->init(t, freq, count, f29, start, end);
218 const KDateTime& end)
221 const Feb29Type feb29Type = (f29 == -1) ? mDefaultFeb29 : static_cast<Feb29Type>(f29);
224 const bool dateOnly = start.isDateOnly();
225 if (!count && ((!dateOnly && !end.isValid())
226 || (dateOnly && !end.date().isValid())))
230 case RecurrenceRule::rMinutely:
231 case RecurrenceRule::rDaily:
232 case RecurrenceRule::rWeekly:
233 case RecurrenceRule::rMonthly:
234 case RecurrenceRule::rYearly:
236 case RecurrenceRule::rNone:
241 mRecurrence.setNewRecurrenceType(recurType, freq);
243 mRecurrence.setDuration(count);
245 mRecurrence.setEndDate(end.date());
247 mRecurrence.setEndDateTime(end);
248 KDateTime startdt = start;
249 if (recurType == RecurrenceRule::rYearly
250 && (feb29Type == Feb29_Feb28 || feb29Type == Feb29_Mar1))
252 int year = startdt.date().year();
253 if (!QDate::isLeapYear(year)
254 && startdt.date().dayOfYear() == (feb29Type == Feb29_Mar1 ? 60 : 59))
263 while (!QDate::isLeapYear(--year)) ;
264 startdt.setDate(QDate(year, 2, 29));
266 mFeb29Type = feb29Type;
268 mRecurrence.setStartDateTime(startdt);
275bool KARecurrence::set(
const QString& icalRRULE)
277 static const QString RRULE = QLatin1String(
"RRULE:");
279 if (icalRRULE.isEmpty())
282 if (!format.
fromString(d->mRecurrence.defaultRRule(
true),
283 (icalRRULE.startsWith(RRULE) ? icalRRULE.mid(RRULE.length()) : icalRRULE)))
289void KARecurrence::clear()
303void KARecurrence::fix()
308void KARecurrence::Private::fix()
311 mFeb29Type = Feb29_None;
313 int days[2] = { 0, 0 };
315 const RecurrenceRule::List rrulelist = mRecurrence.rRules();
317 const int rrend = rrulelist.count();
318 for (
int i = 0; i < 2 && rri < rrend; ++i, ++rri)
323 switch (mRecurrence.recurrenceType(rrule))
325 case Recurrence::rHourly:
327 rrule->setRecurrenceType(RecurrenceRule::rMinutely);
330 case Recurrence::rMinutely:
331 case Recurrence::rDaily:
332 case Recurrence::rWeekly:
333 case Recurrence::rMonthlyDay:
334 case Recurrence::rMonthlyPos:
335 case Recurrence::rYearlyPos:
339 case Recurrence::rOther:
340 if (dailyType(rrule))
346 case Recurrence::rYearlyDay:
354 || rrule->
frequency() != rrules[0]->frequency()
355 || rrule->
startDt() != rrules[0]->startDt())
358 const QList<int> ds = rrule->byYearDays();
359 if (!ds.isEmpty() && ds.first() == 60)
368 case Recurrence::rYearlyMonth:
370 QList<int> ds = rrule->byMonthDays();
373 int day = ds.first();
378 if (day == days[0] || (day == -1 && days[0] == 60)
379 || rrule->
frequency() != rrules[0]->frequency()
380 || rrule->
startDt() != rrules[0]->startDt())
387 rrule->setByMonthDays(ds);
392 const QList<int> months = rrule->byMonths();
393 if (months.count() != 1 || months.first() != 2)
396 if (day == 29 || day == -1)
416 for ( ; rri < rrend; ++rri)
417 mRecurrence.deleteRRule(rrulelist[rri]);
431 rrules[0] = rrules[1];
433 const int d = days[0];
438 months = rrules[0]->byMonths();
439 if (months.removeAll(2))
440 rrules[0]->setByMonths(months);
442 count = combineDurations(rrules[0], rrules[1], end);
443 mFeb29Type = (days[1] == 60) ? Feb29_Mar1 : Feb29_Feb28;
445 else if (convert == 1 && days[0] == 60)
449 count = mRecurrence.duration();
451 end = mRecurrence.endDate();
452 mFeb29Type = Feb29_Mar1;
458 mRecurrence.setNewRecurrenceType(RecurrenceRule::rYearly, mRecurrence.frequency());
461 rrule->setByMonths(months);
464 rrule->setByMonthDays(ds);
468 mRecurrence.setEndDate(end);
477 d->writeRecurrence(
this, recur);
484 recur.setExDates(mRecurrence.exDates());
485 recur.setExDateTimes(mRecurrence.exDateTimes());
489 int freq = mRecurrence.frequency();
490 int count = mRecurrence.duration();
491 static_cast<Recurrence_p*
>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq);
499 if (rrule->byDays().isEmpty())
504 recur.defaultRRule(
true)->setByDays(rrule->byDays());
507 recur.defaultRRule(
true)->setByMonthDays(rrule->byMonthDays());
510 recur.defaultRRule(
true)->setByMonths(rrule->byMonths());
511 recur.defaultRRule()->setByDays(rrule->byDays());
515 QList<int> months = rrule->byMonths();
516 const QList<int> days = mRecurrence.monthDays();
517 const bool special = (mFeb29Type != Feb29_None && !days.isEmpty()
518 && days.first() == 29 && months.removeAll(2));
520 rrule1->setByMonths(months);
521 rrule1->setByMonthDays(days);
528 rrule2->setRecurrenceType(RecurrenceRule::rYearly);
530 rrule2->
setStartDt(mRecurrence.startDateTime());
534 if (mFeb29Type == Feb29_Mar1)
538 rrule2->setByYearDays(ds);
544 rrule2->setByMonthDays(ds);
547 rrule2->setByMonths(ms);
550 if (months.isEmpty())
577 const KDateTime end = endDateTime();
579 - (rrule1->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
583 rrule1->
setEndDt(mRecurrence.startDateTime());
585 - (rrule2->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
589 rrule2->
setEndDt(mRecurrence.startDateTime());
601KDateTime KARecurrence::startDateTime()
const
603 return d->mRecurrence.startDateTime();
606QDate KARecurrence::startDate()
const
608 return d->mRecurrence.startDate();
611void KARecurrence::setStartDateTime(
const KDateTime& dt,
bool dateOnly)
613 d->mRecurrence.setStartDateTime(dt);
615 d->mRecurrence.setAllDay(
true);
621KDateTime KARecurrence::endDateTime()
const
623 return d->endDateTime();
626KDateTime KARecurrence::Private::endDateTime()
const
628 if (mFeb29Type == Feb29_None || mRecurrence.duration() <= 1)
635 return mRecurrence.endDateTime();
644 rrule->setRecurrenceType(RecurrenceRule::rYearly);
645 KDateTime dt = mRecurrence.startDateTime();
646 QDate da = dt.date();
652 da.setYMD(da.year(), da.month(), 28);
655 if (da.month() != 2 || mFeb29Type != Feb29_Feb28 || QDate::isLeapYear(da.year()))
658 da.setYMD(da.year(), da.month(), 27);
662 if (da.month() == 3 && mFeb29Type == Feb29_Mar1 && !QDate::isLeapYear(da.year()))
666 da.setYMD(da.year(), 2, 28);
679 rrule->setByMonthDays(ds);
680 rrule->setByMonths(mRecurrence.defaultRRuleConst()->byMonths());
686 if (mFeb29Type == Feb29_Feb28 && dt.date().month() == 2 && !QDate::isLeapYear(dt.date().year()))
688 return dt.addDays(1);
694QDate KARecurrence::endDate()
const
696 KDateTime end = endDateTime();
697 return end.isValid() ? end.date() : QDate();
700void KARecurrence::setEndDate(
const QDate& endDate)
702 d->mRecurrence.setEndDate(endDate);
705void KARecurrence::setEndDateTime(
const KDateTime& endDateTime)
707 d->mRecurrence.setEndDateTime(endDateTime);
710bool KARecurrence::allDay()
const
712 return d->mRecurrence.allDay();
715void KARecurrence::setRecurReadOnly(
bool readOnly)
717 d->mRecurrence.setRecurReadOnly(readOnly);
720bool KARecurrence::recurReadOnly()
const
722 return d->mRecurrence.recurReadOnly();
725bool KARecurrence::recurs()
const
727 return d->mRecurrence.recurs();
730QBitArray KARecurrence::days()
const
732 return d->mRecurrence.days();
735QList<RecurrenceRule::WDayPos> KARecurrence::monthPositions()
const
737 return d->mRecurrence.monthPositions();
740QList<int> KARecurrence::monthDays()
const
742 return d->mRecurrence.monthDays();
745QList<int> KARecurrence::yearDays()
const
747 return d->mRecurrence.yearDays();
750QList<int> KARecurrence::yearDates()
const
752 return d->mRecurrence.yearDates();
755QList<int> KARecurrence::yearMonths()
const
757 return d->mRecurrence.yearMonths();
760QList<RecurrenceRule::WDayPos> KARecurrence::yearPositions()
const
762 return d->mRecurrence.yearPositions();
765void KARecurrence::addWeeklyDays(
const QBitArray& days)
767 d->mRecurrence.addWeeklyDays(days);
770void KARecurrence::addYearlyDay(
int day)
772 d->mRecurrence.addYearlyDay(day);
775void KARecurrence::addYearlyDate(
int date)
777 d->mRecurrence.addYearlyDate(date);
780void KARecurrence::addYearlyMonth(
short month)
782 d->mRecurrence.addYearlyMonth(month);
785void KARecurrence::addYearlyPos(
short pos,
const QBitArray& days)
787 d->mRecurrence.addYearlyPos(pos, days);
790void KARecurrence::addMonthlyPos(
short pos,
const QBitArray& days)
792 d->mRecurrence.addMonthlyPos(pos, days);
795void KARecurrence::addMonthlyPos(
short pos, ushort day)
797 d->mRecurrence.addMonthlyPos(pos, day);
800void KARecurrence::addMonthlyDate(
short day)
802 d->mRecurrence.addMonthlyDate(day);
808KDateTime KARecurrence::getNextDateTime(
const KDateTime& preDateTime)
const
816 writeRecurrence(recur);
820 return d->mRecurrence.getNextDateTime(preDateTime);
827KDateTime KARecurrence::getPreviousDateTime(
const KDateTime& afterDateTime)
const
835 writeRecurrence(recur);
839 return d->mRecurrence.getPreviousDateTime(afterDateTime);
847bool KARecurrence::recursOn(
const QDate& dt,
const KDateTime::Spec& timeSpec)
const
849 if (!d->mRecurrence.recursOn(dt, timeSpec))
851 if (dt != d->mRecurrence.startDate())
855 if (d->mRecurrence.rDates().contains(dt))
857 const RecurrenceRule::List rulelist = d->mRecurrence.rRules();
858 for (
int rri = 0, rrend = rulelist.count(); rri < rrend; ++rri)
859 if (rulelist[rri]->recursOn(dt, timeSpec))
861 const DateTimeList dtlist = d->mRecurrence.rDateTimes();
862 for (
int dti = 0, dtend = dtlist.count(); dti < dtend; ++dti)
863 if (dtlist[dti].date() == dt)
868bool KARecurrence::recursAt(
const KDateTime& dt)
const
870 return d->mRecurrence.recursAt(dt);
873TimeList KARecurrence::recurTimesOn(
const QDate& date,
const KDateTime::Spec& timeSpec)
const
875 return d->mRecurrence.recurTimesOn(date, timeSpec);
878DateTimeList KARecurrence::timesInInterval(
const KDateTime& start,
const KDateTime& end)
const
880 return d->mRecurrence.timesInInterval(start, end);
883int KARecurrence::frequency()
const
885 return d->mRecurrence.frequency();
888void KARecurrence::setFrequency(
int freq)
890 d->mRecurrence.setFrequency(freq);
893int KARecurrence::duration()
const
895 return d->mRecurrence.duration();
898void KARecurrence::setDuration(
int duration)
900 d->mRecurrence.setDuration(duration);
903int KARecurrence::durationTo(
const KDateTime& dt)
const
905 return d->mRecurrence.durationTo(dt);
908int KARecurrence::durationTo(
const QDate& date)
const
910 return d->mRecurrence.durationTo(date);
921 if (count1 == -1 && count2 == -1)
926 if (count1 && !count2 && rrule2->
endDt().date() == mRecurrence.startDateTime().date())
928 if (count2 && !count1 && rrule1->
endDt().date() == mRecurrence.startDateTime().date())
935 if (!count1 || !count2)
938 KDateTime end1 = rrule1->
endDt();
939 KDateTime end2 = rrule2->
endDt();
940 if (end1.date() == end2.date())
943 return count1 + count2;
948 && (!end1.isValid() || end1.date() > end2.date()))
953 const KDateTime e = end1;
966 KDateTime next1(rr.getNextDate(end1));
967 next1.setDateOnly(
true);
968 if (!next1.isValid())
972 if (end2.isValid() && next1 > end2)
978 return count1 + count2;
981 end = (prev2 > end1.date()) ? prev2 : end1.date();
985 return count1 + count2;
994 const int freq = d->mRecurrence.frequency();
1002 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1009 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1010 for (
int i = 0, end = days.count(); i < end; ++i)
1011 if (days[i].pos() == 0)
1012 ds[days[i].day() - 1] =
true;
1020 for (
int i = 0; i < freq*7; i += freq)
1026 else if (i - last > maxgap)
1031 const int wrap = freq*7 - last + first;
1040 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1049 const QBitArray ds = d->mRecurrence.days();
1055 const int weekStart = KGlobal::locale()->weekStartDay() - 1;
1056 for (
int i = 0; i < 7; ++i)
1060 if (ds.testBit((i + weekStart) % 7))
1064 else if (i - last > maxgap)
1071 const int span = last - first;
1074 if (7 - span > maxgap)
1087 const QList<int> months = d->mRecurrence.yearMonths();
1088 if (months.isEmpty())
1090 if (months.count() == 1)
1095 for (
int i = 0, end = months.count(); i < end; ++i)
1101 const int span = QDate(2001, last, 1).daysTo(QDate(2001, months[i], 1));
1107 const int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1));
1110 if (365 - span > maxgap)
1127 int freq = d->mRecurrence.frequency();
1134 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1140 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1141 for (
int i = 0, end = days.count(); i < end; ++i)
1142 if (days[i].pos() == 0)
1143 ds[days[i].day() - 1] =
true;
1148 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1153 for (
int i = 0; i < 7; ++i)
1164 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1170 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1171 for (
int i = 0, end = days.count(); i < end; ++i)
1172 if (days[i].pos() == 0)
1173 ds[days[i].day() - 1] =
true;
1175 for (
int i = 0; i < 7; ++i)
1196 return d->mRecurrence.exDateTimes();
1199DateList KARecurrence::exDates()
const
1201 return d->mRecurrence.exDates();
1204void KARecurrence::setExDateTimes(
const DateTimeList& exdates)
1206 d->mRecurrence.setExDateTimes(exdates);
1209void KARecurrence::setExDates(
const DateList& exdates)
1211 d->mRecurrence.setExDates(exdates);
1214void KARecurrence::addExDateTime(
const KDateTime& exdate)
1216 d->mRecurrence.addExDateTime(exdate);
1219void KARecurrence::addExDate(
const QDate& exdate)
1221 d->mRecurrence.addExDate(exdate);
1224void KARecurrence::shiftTimes(
const KDateTime::Spec& oldSpec,
const KDateTime::Spec& newSpec)
1226 d->mRecurrence.shiftTimes(oldSpec, newSpec);
1231 return d->mRecurrence.defaultRRuleConst();
1239 if (d->mCachedType == -1)
1240 d->mCachedType = type(d->mRecurrence.defaultRRuleConst());
1241 return static_cast<Type>(d->mCachedType);
1251 case Recurrence::rMinutely:
return MINUTELY;
1252 case Recurrence::rDaily:
return DAILY;
1253 case Recurrence::rWeekly:
return WEEKLY;
1254 case Recurrence::rMonthlyDay:
return MONTHLY_DAY;
1255 case Recurrence::rMonthlyPos:
return MONTHLY_POS;
1256 case Recurrence::rYearlyMonth:
return ANNUAL_DATE;
1257 case Recurrence::rYearlyPos:
return ANNUAL_POS;
1259 if (dailyType(rrule))
1270 if (rrule->recurrenceType() != RecurrenceRule::rDaily
1271 || !rrule->bySeconds().isEmpty()
1272 || !rrule->byMinutes().isEmpty()
1273 || !rrule->byHours().isEmpty()
1274 || !rrule->byWeekNumbers().isEmpty()
1275 || !rrule->byMonthDays().isEmpty()
1276 || !rrule->byMonths().isEmpty()
1277 || !rrule->bySetPos().isEmpty()
1278 || !rrule->byYearDays().isEmpty())
1280 const QList<RecurrenceRule::WDayPos> days = rrule->byDays();
1285 for (
int i = 0, end = days.count(); i < end; ++i)
1287 if (days[i].pos() != 0)
Represents recurrences for KAlarm.
Type
The recurrence's period type.
Feb29Type
When annual February 29th recurrences should occur in non-leap years.
@ Feb29_None
does not occur in non-leap years
Type type() const
Return the recurrence's period type.
Feb29Type feb29Type() const
Return when 29th February annual recurrences should occur in non-leap years.
void setAllDay(bool allDay)
void setDuration(int duration)
void setFrequency(int freq)
int durationTo(const KDateTime &dt) const
KDateTime startDt() const
void setEndDt(const KDateTime &endDateTime)
bool recursOn(const QDate &date, const KDateTime::Spec &timeSpec) const
KDateTime getPreviousDate(const KDateTime &afterDateTime) const
KDateTime endDt(bool *result=0) const
void setStartDt(const KDateTime &start)
ushort recurrenceType() const
void setEndDateTime(const KDateTime &endDateTime)
void setStartDateTime(const KDateTime &start)
KDateTime getPreviousDateTime(const KDateTime &afterDateTime) const
void addRRule(RecurrenceRule *rrule)
KDateTime getNextDateTime(const KDateTime &preDateTime) const
void setDuration(int duration)