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
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;
99 KARecurrence::Feb29Type KARecurrence::Private::mDefaultFeb29 = KARecurrence::Feb29_None;
101 KARecurrence::KARecurrence()
105 KARecurrence::KARecurrence(
const Recurrence& r)
111 KARecurrence::KARecurrence(
const KARecurrence& r)
112 : d(new Private(*r.d))
115 KARecurrence::~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;
156 return d->set(t, freq, count, -1, start, end);
161 return d->set(t, freq, count, f29, start, end);
164 bool 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 Feb29Type feb29Type = (f29 == -1) ? mDefaultFeb29 : static_cast<Feb29Type>(f29);
224 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);
277 static 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)))
308 void KARecurrence::Private::fix()
311 mFeb29Type = Feb29_None;
313 int days[2] = { 0, 0 };
315 const RecurrenceRule::List rrulelist = mRecurrence.rRules();
317 int rrend = rrulelist.count();
318 for (
int i = 0; i < 2 && rri < rrend; ++i, ++rri)
323 int rtype = mRecurrence.recurrenceType(rrule);
326 case Recurrence::rHourly:
328 rrule->setRecurrenceType(RecurrenceRule::rMinutely);
331 case Recurrence::rMinutely:
332 case Recurrence::rDaily:
333 case Recurrence::rWeekly:
334 case Recurrence::rMonthlyDay:
335 case Recurrence::rMonthlyPos:
336 case Recurrence::rYearlyPos:
340 case Recurrence::rOther:
341 if (dailyType(rrule))
347 case Recurrence::rYearlyDay:
359 QList<int> ds = rrule->byYearDays();
360 if (!ds.isEmpty() && ds.first() == 60)
369 case Recurrence::rYearlyMonth:
371 QList<int> ds = rrule->byMonthDays();
374 int day = ds.first();
379 if (day == days[0] || (day == -1 && days[0] == 60)
388 rrule->setByMonthDays(ds);
393 QList<int> months = rrule->byMonths();
394 if (months.count() != 1 || months.first() != 2)
397 if (day == 29 || day == -1)
417 for ( ; rri < rrend; ++rri)
418 mRecurrence.deleteRRule(rrulelist[rri]);
432 rrules[0] = rrules[1];
439 months = rrules[0]->byMonths();
440 if (months.removeAll(2))
441 rrules[0]->setByMonths(months);
443 count = combineDurations(rrules[0], rrules[1], end);
444 mFeb29Type = (days[1] == 60) ? Feb29_Mar1 : Feb29_Feb28;
446 else if (convert == 1 && days[0] == 60)
450 count = mRecurrence.duration();
452 end = mRecurrence.endDate();
453 mFeb29Type = Feb29_Mar1;
459 mRecurrence.setNewRecurrenceType(RecurrenceRule::rYearly, mRecurrence.frequency());
462 rrule->setByMonths(months);
465 rrule->setByMonthDays(ds);
469 mRecurrence.setEndDate(end);
478 d->writeRecurrence(
this, recur);
485 recur.setExDates(mRecurrence.exDates());
486 recur.setExDateTimes(mRecurrence.exDateTimes());
491 int count = mRecurrence.duration();
492 static_cast<Recurrence_p*
>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq);
500 if (rrule->byDays().isEmpty())
505 recur.defaultRRule(
true)->setByDays(rrule->byDays());
508 recur.defaultRRule(
true)->setByMonthDays(rrule->byMonthDays());
511 recur.defaultRRule(
true)->setByMonths(rrule->byMonths());
512 recur.defaultRRule()->setByDays(rrule->byDays());
516 QList<int> months = rrule->byMonths();
517 QList<int> days = mRecurrence.monthDays();
518 bool special = (mFeb29Type != Feb29_None && !days.isEmpty()
519 && days.first() == 29 && months.removeAll(2));
521 rrule1->setByMonths(months);
522 rrule1->setByMonthDays(days);
529 rrule2->setRecurrenceType(RecurrenceRule::rYearly);
531 rrule2->
setStartDt(mRecurrence.startDateTime());
535 if (mFeb29Type == Feb29_Mar1)
539 rrule2->setByYearDays(ds);
545 rrule2->setByMonthDays(ds);
548 rrule2->setByMonths(ms);
551 if (months.isEmpty())
578 KDateTime end = endDateTime();
580 - (rrule1->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
584 rrule1->
setEndDt(mRecurrence.startDateTime());
586 - (rrule2->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
590 rrule2->
setEndDt(mRecurrence.startDateTime());
604 return d->mRecurrence.startDateTime();
609 return d->mRecurrence.startDate();
614 d->mRecurrence.setStartDateTime(dt);
616 d->mRecurrence.setAllDay(
true);
624 return d->endDateTime();
627 KDateTime KARecurrence::Private::endDateTime()
const
629 if (mFeb29Type == Feb29_None || mRecurrence.duration() <= 1)
636 return mRecurrence.endDateTime();
645 rrule->setRecurrenceType(RecurrenceRule::rYearly);
646 KDateTime dt = mRecurrence.startDateTime();
647 QDate da = dt.date();
653 da.setYMD(da.year(), da.month(), 28);
656 if (da.month() != 2 || mFeb29Type != Feb29_Feb28 || QDate::isLeapYear(da.year()))
659 da.setYMD(da.year(), da.month(), 27);
663 if (da.month() == 3 && mFeb29Type == Feb29_Mar1 && !QDate::isLeapYear(da.year()))
667 da.setYMD(da.year(), 2, 28);
680 rrule->setByMonthDays(ds);
681 rrule->setByMonths(mRecurrence.defaultRRuleConst()->byMonths());
687 if (mFeb29Type == Feb29_Feb28 && dt.date().month() == 2 && !QDate::isLeapYear(dt.date().year()))
689 return dt.addDays(1);
697 KDateTime end = endDateTime();
698 return end.isValid() ? end.date() : QDate();
703 d->mRecurrence.setEndDate(endDate);
708 d->mRecurrence.setEndDateTime(endDateTime);
713 return d->mRecurrence.allDay();
718 d->mRecurrence.setRecurReadOnly(readOnly);
723 return d->mRecurrence.recurReadOnly();
728 return d->mRecurrence.recurs();
733 return d->mRecurrence.days();
738 return d->mRecurrence.monthPositions();
743 return d->mRecurrence.monthDays();
748 return d->mRecurrence.yearDays();
753 return d->mRecurrence.yearDates();
758 return d->mRecurrence.yearMonths();
763 return d->mRecurrence.yearPositions();
768 d->mRecurrence.addWeeklyDays(days);
773 d->mRecurrence.addYearlyDay(day);
778 d->mRecurrence.addYearlyDate(date);
783 d->mRecurrence.addYearlyMonth(month);
788 d->mRecurrence.addYearlyPos(pos, days);
793 d->mRecurrence.addMonthlyPos(pos, days);
798 d->mRecurrence.addMonthlyPos(pos, day);
803 d->mRecurrence.addMonthlyDate(day);
817 writeRecurrence(recur);
821 return d->mRecurrence.getNextDateTime(preDateTime);
836 writeRecurrence(recur);
840 return d->mRecurrence.getPreviousDateTime(afterDateTime);
850 if (!d->mRecurrence.recursOn(dt, timeSpec))
852 if (dt != d->mRecurrence.startDate())
856 if (d->mRecurrence.rDates().contains(dt))
858 const RecurrenceRule::List rulelist = d->mRecurrence.rRules();
859 for (
int rri = 0, rrend = rulelist.count(); rri < rrend; ++rri)
860 if (rulelist[rri]->recursOn(dt, timeSpec))
862 const DateTimeList dtlist = d->mRecurrence.rDateTimes();
863 for (
int dti = 0, dtend = dtlist.count(); dti < dtend; ++dti)
864 if (dtlist[dti].date() == dt)
871 return d->mRecurrence.recursAt(dt);
876 return d->mRecurrence.recurTimesOn(date, timeSpec);
881 return d->mRecurrence.timesInInterval(start, end);
886 return d->mRecurrence.frequency();
891 d->mRecurrence.setFrequency(freq);
896 return d->mRecurrence.duration();
901 d->mRecurrence.setDuration(duration);
906 return d->mRecurrence.durationTo(dt);
911 return d->mRecurrence.durationTo(date);
922 if (count1 == -1 && count2 == -1)
927 if (count1 && !count2 && rrule2->
endDt().date() == mRecurrence.startDateTime().date())
929 if (count2 && !count1 && rrule1->
endDt().date() == mRecurrence.startDateTime().date())
936 if (!count1 || !count2)
939 KDateTime end1 = rrule1->
endDt();
940 KDateTime end2 = rrule2->
endDt();
941 if (end1.date() == end2.date())
944 return count1 + count2;
949 && (!end1.isValid() || end1.date() > end2.date()))
967 KDateTime next1(rr.getNextDate(end1));
968 next1.setDateOnly(
true);
969 if (!next1.isValid())
973 if (end2.isValid() && next1 > end2)
979 return count1 + count2;
982 end = (prev2 > end1.date()) ? prev2 : end1.date();
986 return count1 + count2;
995 int freq = d->mRecurrence.frequency();
999 return Duration(freq * 60, Duration::Seconds);
1003 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1005 return Duration(freq, Duration::Days);
1010 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1011 for (
int i = 0, end = days.count(); i < end; ++i)
1012 if (days[i].pos() == 0)
1013 ds[days[i].day() - 1] =
true;
1021 for (
int i = 0; i < freq*7; i += freq)
1027 else if (i - last > maxgap)
1032 int wrap = freq*7 - last + first;
1035 return Duration(maxgap, Duration::Days);
1041 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1042 return Duration(freq, Duration::Days);
1050 QBitArray ds = d->mRecurrence.days();
1056 int weekStart = KGlobal::locale()->weekStartDay() - 1;
1057 for (
int i = 0; i < 7; ++i)
1061 if (ds.testBit((i + weekStart) % 7))
1065 else if (i - last > maxgap)
1072 int span = last - first;
1074 return Duration(freq*7 - span, Duration::Days);
1075 if (7 - span > maxgap)
1076 return Duration(7 - span, Duration::Days);
1077 return Duration(maxgap, Duration::Days);
1081 return Duration(freq * 31, Duration::Days);
1088 const QList<int> months = d->mRecurrence.yearMonths();
1089 if (months.isEmpty())
1091 if (months.count() == 1)
1092 return Duration(freq * 365, Duration::Days);
1096 for (
int i = 0, end = months.count(); i < end; ++i)
1102 int span = QDate(2001, last, 1).daysTo(QDate(2001, months[i], 1));
1108 int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1));
1110 return Duration(freq*365 - span, Duration::Days);
1111 if (365 - span > maxgap)
1112 return Duration(365 - span, Duration::Days);
1113 return Duration(maxgap, Duration::Days);
1128 int freq = d->mRecurrence.frequency();
1132 return Duration(freq * 60, Duration::Seconds);
1135 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1137 return Duration(freq, Duration::Days);
1141 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1142 for (
int i = 0, end = days.count(); i < end; ++i)
1143 if (days[i].pos() == 0)
1144 ds[days[i].day() - 1] =
true;
1149 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1150 return Duration(freq, Duration::Days);
1154 for (
int i = 0; i < 7; ++i)
1158 return Duration(freq, Duration::Days);
1160 return Duration(freq * 7, Duration::Days);
1165 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1167 return Duration(freq * 7, Duration::Days);
1171 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1172 for (
int i = 0, end = days.count(); i < end; ++i)
1173 if (days[i].pos() == 0)
1174 ds[days[i].day() - 1] =
true;
1176 for (
int i = 0; i < 7; ++i)
1182 return Duration(freq, Duration::Days);
1186 return Duration(freq * 7, Duration::Days);
1197 return d->mRecurrence.exDateTimes();
1200 DateList KARecurrence::exDates()
const
1202 return d->mRecurrence.exDates();
1205 void KARecurrence::setExDateTimes(
const DateTimeList& exdates)
1207 d->mRecurrence.setExDateTimes(exdates);
1210 void KARecurrence::setExDates(
const DateList& exdates)
1212 d->mRecurrence.setExDates(exdates);
1215 void KARecurrence::addExDateTime(
const KDateTime& exdate)
1217 d->mRecurrence.addExDateTime(exdate);
1220 void KARecurrence::addExDate(
const QDate& exdate)
1222 d->mRecurrence.addExDate(exdate);
1227 d->mRecurrence.shiftTimes(oldSpec, newSpec);
1232 return d->mRecurrence.defaultRRuleConst();
1240 if (d->mCachedType == -1)
1241 d->mCachedType = type(d->mRecurrence.defaultRRuleConst());
1242 return static_cast<Type>(d->mCachedType);
1250 switch (Recurrence::recurrenceType(rrule))
1252 case Recurrence::rMinutely:
return MINUTELY;
1253 case Recurrence::rDaily:
return DAILY;
1254 case Recurrence::rWeekly:
return WEEKLY;
1255 case Recurrence::rMonthlyDay:
return MONTHLY_DAY;
1256 case Recurrence::rMonthlyPos:
return MONTHLY_POS;
1257 case Recurrence::rYearlyMonth:
return ANNUAL_DATE;
1258 case Recurrence::rYearlyPos:
return ANNUAL_POS;
1260 if (dailyType(rrule))
1271 if (rrule->recurrenceType() != RecurrenceRule::rDaily
1272 || !rrule->bySeconds().isEmpty()
1273 || !rrule->byMinutes().isEmpty()
1274 || !rrule->byHours().isEmpty()
1275 || !rrule->byWeekNumbers().isEmpty()
1276 || !rrule->byMonthDays().isEmpty()
1277 || !rrule->byMonths().isEmpty()
1278 || !rrule->bySetPos().isEmpty()
1279 || !rrule->byYearDays().isEmpty())
1281 const QList<RecurrenceRule::WDayPos> days = rrule->byDays();
1286 for (
int i = 0, end = days.count(); i < end; ++i)
1288 if (days[i].pos() != 0)
void setAllDay(bool allDay)
bool recursOn(const QDate &date, const KDateTime::Spec &timeSpec) const
QList< KCalCore::RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
void fix()
Convert the recurrence to KARecurrence types.
Type
The recurrence's period type.
int durationTo(const KDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
void setDuration(int duration)
void setStartDateTime(const KDateTime &start)
KDateTime endDt(bool *result=0) const
KARecurrence & operator=(const KARecurrence &r)
Assignment operator.
QBitArray days() const
Returns week day mask (bit 0 = Monday).
void setRecurReadOnly(bool readOnly)
Set if recurrence is read-only or can be changed.
void addWeeklyDays(const QBitArray &days)
Adds days to the weekly day recurrence list.
void setEndDateTime(const KDateTime &endDateTime)
Feb29Type
When annual February 29th recurrences should occur in non-leap years.
void addMonthlyDate(short day)
Adds a date (e.g.
KDateTime getPreviousDate(const KDateTime &afterDateTime) const
void setDuration(int duration)
void setEndDt(const KDateTime &endDateTime)
KDateTime endDateTime() const
Return the date/time of the last recurrence.
void setFrequency(int freq)
KCalCore::DateTimeList timesInInterval(const KDateTime &start, const KDateTime &end) const
Returns a list of all the times at which the recurrence will occur between two specified times...
void writeRecurrence(KCalCore::Recurrence &) const
Initialise a KCalCore::Recurrence to be the same as this instance.
QList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
Represents recurrences for KAlarm.
KDateTime getPreviousDateTime(const KDateTime &afterDateTime) const
Get the previous time the recurrence occurred, strictly before a specified time.
KCalCore::Duration longestInterval() const
Return the longest interval between recurrences.
bool recursAt(const KDateTime &dt) const
Returns true if the date/time specified is one at which the event will recur.
QDate startDate() const
Return the start date/time of the recurrence.
Type type() const
Return the recurrence's period type.
QList< int > monthDays() const
Returns list of day numbers of a month.
bool recurs() const
Returns whether the event recurs at all.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last...
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
KDateTime getNextDateTime(const KDateTime &preDateTime) const
Get the next time the recurrence occurs, strictly after a specified time.
void addYearlyMonth(short month)
Adds month in yearly recurrence.
QList< KCalCore::RecurrenceRule::WDayPos > yearPositions() const
Returns the positions within a yearly recurrence.
static void setDefaultFeb29Type(Feb29Type t)
Set the default way that 29th February annual recurrences should occur in non-leap years...
void setEndDateTime(const KDateTime &endDateTime)
Sets the date and time of the last recurrence.
bool set(const QString &icalRRULE)
Initialise the recurrence from an iCalendar RRULE string.
int durationTo(const KDateTime &dt) const
void addRRule(RecurrenceRule *rrule)
QDate endDate() const
Return the date of the last recurrence.
QList< int > yearDates() const
Returns the dates within a yearly recurrence.
void setFrequency(int freq)
Sets the frequency of recurrence, in terms of the recurrence time period type.
void setEndDate(const QDate &endDate)
Sets the date of the last recurrence.
KDateTime startDt() const
bool recurReadOnly() const
Returns true if the recurrence is read-only, or false if it can be changed.
KDateTime startDateTime() const
Return the start date/time of the recurrence (Time for all-day recurrences will be 0:00)...
KDateTime getNextDateTime(const KDateTime &preDateTime) const
bool init(KCalCore::RecurrenceRule::PeriodType t, int freq, int count, const KDateTime &start, const KDateTime &end)
Set up a KARecurrence from recurrence parameters.
bool recursOn(const QDate &, const KDateTime::Spec &) const
Return whether the event will recur on the specified date.
void clear()
Removes all recurrence and exception rules and dates.
KDateTime getPreviousDateTime(const KDateTime &afterDateTime) const
void addMonthlyPos(short pos, const QBitArray &days)
Adds a position (e.g.
KCalCore::TimeList recurTimesOn(const QDate &date, const KDateTime::Spec &timeSpec) const
Returns a list of the times on the specified date at which the recurrence will occur.
void addYearlyPos(short pos, const QBitArray &days)
Adds position within month/year within a yearly recurrence.
void addYearlyDay(int day)
Adds day number of year within a yearly recurrence.
QList< int > yearMonths() const
Returns the months within a yearly recurrence.
void addYearlyDate(int date)
Adds date within a yearly recurrence.
bool allDay() const
Set whether the recurrence has no time, just a date.
Feb29Type feb29Type() const
Return when 29th February annual recurrences should occur in non-leap years.
static Feb29Type defaultFeb29Type()
Return the default way that 29th February annual recurrences should occur in non-leap years...
void setStartDt(const KDateTime &start)
KARecurrence::Type recurType() const
Return the recurrence period type for the event.
bool operator==(const KARecurrence &r) const
Comparison operator for equality.
void setStartDateTime(const KDateTime &dt, bool dateOnly)
Set the recurrence start date/time, and optionally set it to all-day.
void shiftTimes(const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec)
Shift the times of the recurrence so that they appear at the same clock time as before but in a new t...
static bool dailyType(const KCalCore::RecurrenceRule *)
Check if the recurrence rule is a daily rule with or without BYDAYS specified.
KCalCore::Duration regularInterval() const
Return the interval between recurrences, if the interval between successive occurrences does not vary...