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>
40 #ifndef KALARMCAL_USE_KRESOURCES
41 using namespace KCalCore;
52 using Recurrence::setNewRecurrenceType;
55 Recurrence_p(
const Recurrence_p& r) :
Recurrence(r) {}
58 class KARecurrence::Private
62 : mFeb29Type(Feb29_None), mCachedType(-1) {}
64 : mRecurrence(r), mFeb29Type(Feb29_None), mCachedType(-1) {}
68 mFeb29Type = Feb29_None;
71 bool set(Type,
int freq,
int count,
int f29,
const KDateTime& start,
const KDateTime& end);
74 void writeRecurrence(
const KARecurrence* q,
Recurrence& recur)
const;
75 KDateTime endDateTime()
const;
78 static Feb29Type mDefaultFeb29;
79 Recurrence_p mRecurrence;
81 mutable int mCachedType;
100 KARecurrence::Feb29Type KARecurrence::Private::mDefaultFeb29 = KARecurrence::Feb29_None;
102 KARecurrence::KARecurrence()
106 KARecurrence::KARecurrence(
const Recurrence& r)
112 KARecurrence::KARecurrence(
const KARecurrence& r)
113 : d(new Private(*r.d))
116 KARecurrence::~KARecurrence()
130 return d->mRecurrence == r.d->mRecurrence
131 && d->mFeb29Type == r.d->mFeb29Type;
136 return d->mFeb29Type;
141 return Private::mDefaultFeb29;
146 Private::mDefaultFeb29 = t;
157 return d->set(t, freq, count, -1, start, end);
162 return d->set(t, freq, count, f29, start, end);
165 bool KARecurrence::Private::set(Type
recurType,
int freq,
int count,
int f29,
const KDateTime& start,
const KDateTime& end)
171 case MINUTELY: rrtype = RecurrenceRule::rMinutely;
break;
172 case DAILY: rrtype = RecurrenceRule::rDaily;
break;
173 case WEEKLY: rrtype = RecurrenceRule::rWeekly;
break;
174 case MONTHLY_DAY: rrtype = RecurrenceRule::rMonthly;
break;
175 case ANNUAL_DATE: rrtype = RecurrenceRule::rYearly;
break;
176 case NO_RECUR: rrtype = RecurrenceRule::rNone;
break;
180 if (!init(rrtype, freq, count, f29, start, end))
187 days.setBit(start.date().dayOfWeek() - 1);
188 mRecurrence.addWeeklyDays(days);
192 mRecurrence.addMonthlyDate(start.date().day());
195 mRecurrence.addYearlyDate(start.date().day());
196 mRecurrence.addYearlyMonth(start.date().month());
210 return d->init(t, freq, count, -1, start, end);
215 return d->init(t, freq, count, f29, start, end);
219 const KDateTime& end)
222 Feb29Type feb29Type = (f29 == -1) ? mDefaultFeb29 : static_cast<Feb29Type>(f29);
225 bool dateOnly = start.isDateOnly();
226 if (!count && ((!dateOnly && !end.isValid())
227 || (dateOnly && !end.date().isValid())))
231 case RecurrenceRule::rMinutely:
232 case RecurrenceRule::rDaily:
233 case RecurrenceRule::rWeekly:
234 case RecurrenceRule::rMonthly:
235 case RecurrenceRule::rYearly:
237 case RecurrenceRule::rNone:
242 mRecurrence.setNewRecurrenceType(recurType, freq);
244 mRecurrence.setDuration(count);
246 mRecurrence.setEndDate(end.date());
248 mRecurrence.setEndDateTime(end);
249 KDateTime startdt = start;
250 if (recurType == RecurrenceRule::rYearly
251 && (feb29Type == Feb29_Feb28 || feb29Type == Feb29_Mar1))
253 int year = startdt.date().year();
254 if (!QDate::isLeapYear(year)
255 && startdt.date().dayOfYear() == (feb29Type == Feb29_Mar1 ? 60 : 59))
264 while (!QDate::isLeapYear(--year)) ;
265 startdt.setDate(QDate(year, 2, 29));
267 mFeb29Type = feb29Type;
269 mRecurrence.setStartDateTime(startdt);
278 static QString RRULE = QLatin1String(
"RRULE:");
280 if (icalRRULE.isEmpty())
283 if (!format.
fromString(d->mRecurrence.defaultRRule(
true),
284 (icalRRULE.startsWith(RRULE) ? icalRRULE.mid(RRULE.length()) : icalRRULE)))
309 void KARecurrence::Private::fix()
312 mFeb29Type = Feb29_None;
314 int days[2] = { 0, 0 };
316 const RecurrenceRule::List rrulelist = mRecurrence.rRules();
318 int rrend = rrulelist.count();
319 for (
int i = 0; i < 2 && rri < rrend; ++i, ++rri)
324 int rtype = mRecurrence.recurrenceType(rrule);
327 case Recurrence::rHourly:
329 rrule->setRecurrenceType(RecurrenceRule::rMinutely);
332 case Recurrence::rMinutely:
333 case Recurrence::rDaily:
334 case Recurrence::rWeekly:
335 case Recurrence::rMonthlyDay:
336 case Recurrence::rMonthlyPos:
337 case Recurrence::rYearlyPos:
341 case Recurrence::rOther:
342 if (dailyType(rrule))
348 case Recurrence::rYearlyDay:
360 QList<int> ds = rrule->byYearDays();
361 if (!ds.isEmpty() && ds.first() == 60)
370 case Recurrence::rYearlyMonth:
372 QList<int> ds = rrule->byMonthDays();
375 int day = ds.first();
380 if (day == days[0] || (day == -1 && days[0] == 60)
389 rrule->setByMonthDays(ds);
394 QList<int> months = rrule->byMonths();
395 if (months.count() != 1 || months.first() != 2)
398 if (day == 29 || day == -1)
418 for ( ; rri < rrend; ++rri)
419 mRecurrence.deleteRRule(rrulelist[rri]);
433 rrules[0] = rrules[1];
440 months = rrules[0]->byMonths();
441 if (months.removeAll(2))
442 rrules[0]->setByMonths(months);
444 count = combineDurations(rrules[0], rrules[1], end);
445 mFeb29Type = (days[1] == 60) ? Feb29_Mar1 : Feb29_Feb28;
447 else if (convert == 1 && days[0] == 60)
451 count = mRecurrence.duration();
453 end = mRecurrence.endDate();
454 mFeb29Type = Feb29_Mar1;
460 mRecurrence.setNewRecurrenceType(RecurrenceRule::rYearly, mRecurrence.frequency());
463 rrule->setByMonths(months);
466 rrule->setByMonthDays(ds);
470 mRecurrence.setEndDate(end);
479 d->writeRecurrence(
this, recur);
486 recur.setExDates(mRecurrence.exDates());
487 recur.setExDateTimes(mRecurrence.exDateTimes());
492 int count = mRecurrence.duration();
493 static_cast<Recurrence_p*
>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq);
501 if (rrule->byDays().isEmpty())
506 recur.defaultRRule(
true)->setByDays(rrule->byDays());
509 recur.defaultRRule(
true)->setByMonthDays(rrule->byMonthDays());
512 recur.defaultRRule(
true)->setByMonths(rrule->byMonths());
513 recur.defaultRRule()->setByDays(rrule->byDays());
517 QList<int> months = rrule->byMonths();
518 QList<int> days = mRecurrence.monthDays();
519 bool special = (mFeb29Type != Feb29_None && !days.isEmpty()
520 && days.first() == 29 && months.removeAll(2));
522 rrule1->setByMonths(months);
523 rrule1->setByMonthDays(days);
530 rrule2->setRecurrenceType(RecurrenceRule::rYearly);
532 rrule2->
setStartDt(mRecurrence.startDateTime());
536 if (mFeb29Type == Feb29_Mar1)
540 rrule2->setByYearDays(ds);
546 rrule2->setByMonthDays(ds);
549 rrule2->setByMonths(ms);
552 if (months.isEmpty())
579 KDateTime end = endDateTime();
581 - (rrule1->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
585 rrule1->
setEndDt(mRecurrence.startDateTime());
587 - (rrule2->
recursOn(mRecurrence.startDate(), mRecurrence.startDateTime().timeSpec()) ? 0 : 1);
591 rrule2->
setEndDt(mRecurrence.startDateTime());
605 return d->mRecurrence.startDateTime();
610 return d->mRecurrence.startDate();
615 d->mRecurrence.setStartDateTime(dt);
617 d->mRecurrence.setAllDay(
true);
625 return d->endDateTime();
628 KDateTime KARecurrence::Private::endDateTime()
const
630 if (mFeb29Type == Feb29_None || mRecurrence.duration() <= 1)
637 return mRecurrence.endDateTime();
646 rrule->setRecurrenceType(RecurrenceRule::rYearly);
647 KDateTime dt = mRecurrence.startDateTime();
648 QDate da = dt.date();
654 da.setYMD(da.year(), da.month(), 28);
657 if (da.month() != 2 || mFeb29Type != Feb29_Feb28 || QDate::isLeapYear(da.year()))
660 da.setYMD(da.year(), da.month(), 27);
664 if (da.month() == 3 && mFeb29Type == Feb29_Mar1 && !QDate::isLeapYear(da.year()))
668 da.setYMD(da.year(), 2, 28);
681 rrule->setByMonthDays(ds);
682 rrule->setByMonths(mRecurrence.defaultRRuleConst()->byMonths());
688 if (mFeb29Type == Feb29_Feb28 && dt.date().month() == 2 && !QDate::isLeapYear(dt.date().year()))
690 return dt.addDays(1);
698 KDateTime end = endDateTime();
699 return end.isValid() ? end.date() : QDate();
704 d->mRecurrence.setEndDate(endDate);
709 d->mRecurrence.setEndDateTime(endDateTime);
714 return d->mRecurrence.allDay();
719 d->mRecurrence.setRecurReadOnly(readOnly);
724 return d->mRecurrence.recurReadOnly();
729 return d->mRecurrence.recurs();
734 return d->mRecurrence.days();
739 return d->mRecurrence.monthPositions();
744 return d->mRecurrence.monthDays();
749 return d->mRecurrence.yearDays();
754 return d->mRecurrence.yearDates();
759 return d->mRecurrence.yearMonths();
764 return d->mRecurrence.yearPositions();
769 d->mRecurrence.addWeeklyDays(days);
774 d->mRecurrence.addYearlyDay(day);
779 d->mRecurrence.addYearlyDate(date);
784 d->mRecurrence.addYearlyMonth(month);
789 d->mRecurrence.addYearlyPos(pos, days);
794 d->mRecurrence.addMonthlyPos(pos, days);
799 d->mRecurrence.addMonthlyPos(pos, day);
804 d->mRecurrence.addMonthlyDate(day);
818 writeRecurrence(recur);
822 return d->mRecurrence.getNextDateTime(preDateTime);
837 writeRecurrence(recur);
841 return d->mRecurrence.getPreviousDateTime(afterDateTime);
851 if (!d->mRecurrence.recursOn(dt, timeSpec))
853 if (dt != d->mRecurrence.startDate())
857 if (d->mRecurrence.rDates().contains(dt))
859 const RecurrenceRule::List rulelist = d->mRecurrence.rRules();
860 for (
int rri = 0, rrend = rulelist.count(); rri < rrend; ++rri)
861 if (rulelist[rri]->recursOn(dt, timeSpec))
863 const DateTimeList dtlist = d->mRecurrence.rDateTimes();
864 for (
int dti = 0, dtend = dtlist.count(); dti < dtend; ++dti)
865 if (dtlist[dti].date() == dt)
872 return d->mRecurrence.recursAt(dt);
877 return d->mRecurrence.recurTimesOn(date, timeSpec);
882 return d->mRecurrence.timesInInterval(start, end);
887 return d->mRecurrence.frequency();
892 d->mRecurrence.setFrequency(freq);
897 return d->mRecurrence.duration();
902 d->mRecurrence.setDuration(duration);
907 return d->mRecurrence.durationTo(dt);
912 return d->mRecurrence.durationTo(date);
923 if (count1 == -1 && count2 == -1)
928 if (count1 && !count2 && rrule2->
endDt().date() == mRecurrence.startDateTime().date())
930 if (count2 && !count1 && rrule1->
endDt().date() == mRecurrence.startDateTime().date())
937 if (!count1 || !count2)
940 KDateTime end1 = rrule1->
endDt();
941 KDateTime end2 = rrule2->
endDt();
942 if (end1.date() == end2.date())
945 return count1 + count2;
950 && (!end1.isValid() || end1.date() > end2.date()))
968 KDateTime next1(rr.getNextDate(end1));
969 next1.setDateOnly(
true);
970 if (!next1.isValid())
974 if (end2.isValid() && next1 > end2)
980 return count1 + count2;
983 end = (prev2 > end1.date()) ? prev2 : end1.date();
987 return count1 + count2;
996 int freq = d->mRecurrence.frequency();
1000 return Duration(freq * 60, Duration::Seconds);
1004 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1006 return Duration(freq, Duration::Days);
1011 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1012 for (
int i = 0, end = days.count(); i < end; ++i)
1013 if (days[i].pos() == 0)
1014 ds[days[i].day() - 1] =
true;
1022 for (
int i = 0; i < freq*7; i += freq)
1028 else if (i - last > maxgap)
1033 int wrap = freq*7 - last + first;
1036 return Duration(maxgap, Duration::Days);
1042 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1043 return Duration(freq, Duration::Days);
1051 QBitArray ds = d->mRecurrence.days();
1057 int weekStart = KGlobal::locale()->weekStartDay() - 1;
1058 for (
int i = 0; i < 7; ++i)
1062 if (ds.testBit((i + weekStart) % 7))
1066 else if (i - last > maxgap)
1073 int span = last - first;
1075 return Duration(freq*7 - span, Duration::Days);
1076 if (7 - span > maxgap)
1077 return Duration(7 - span, Duration::Days);
1078 return Duration(maxgap, Duration::Days);
1082 return Duration(freq * 31, Duration::Days);
1089 const QList<int> months = d->mRecurrence.yearMonths();
1090 if (months.isEmpty())
1092 if (months.count() == 1)
1093 return Duration(freq * 365, Duration::Days);
1097 for (
int i = 0, end = months.count(); i < end; ++i)
1103 int span = QDate(2001, last, 1).daysTo(QDate(2001, months[i], 1));
1109 int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1));
1111 return Duration(freq*365 - span, Duration::Days);
1112 if (365 - span > maxgap)
1113 return Duration(365 - span, Duration::Days);
1114 return Duration(maxgap, Duration::Days);
1129 int freq = d->mRecurrence.frequency();
1133 return Duration(freq * 60, Duration::Seconds);
1136 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1138 return Duration(freq, Duration::Days);
1142 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1143 for (
int i = 0, end = days.count(); i < end; ++i)
1144 if (days[i].pos() == 0)
1145 ds[days[i].day() - 1] =
true;
1150 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1151 return Duration(freq, Duration::Days);
1155 for (
int i = 0; i < 7; ++i)
1159 return Duration(freq, Duration::Days);
1161 return Duration(freq * 7, Duration::Days);
1166 const QList<RecurrenceRule::WDayPos> days = d->mRecurrence.defaultRRuleConst()->byDays();
1168 return Duration(freq * 7, Duration::Days);
1172 bool ds[7] = {
false,
false,
false,
false,
false,
false,
false };
1173 for (
int i = 0, end = days.count(); i < end; ++i)
1174 if (days[i].pos() == 0)
1175 ds[days[i].day() - 1] =
true;
1177 for (
int i = 0; i < 7; ++i)
1183 return Duration(freq, Duration::Days);
1187 return Duration(freq * 7, Duration::Days);
1198 return d->mRecurrence.exDateTimes();
1201 DateList KARecurrence::exDates()
const
1203 return d->mRecurrence.exDates();
1206 void KARecurrence::setExDateTimes(
const DateTimeList& exdates)
1208 d->mRecurrence.setExDateTimes(exdates);
1211 void KARecurrence::setExDates(
const DateList& exdates)
1213 d->mRecurrence.setExDates(exdates);
1216 void KARecurrence::addExDateTime(
const KDateTime& exdate)
1218 d->mRecurrence.addExDateTime(exdate);
1221 void KARecurrence::addExDate(
const QDate& exdate)
1223 d->mRecurrence.addExDate(exdate);
1228 d->mRecurrence.shiftTimes(oldSpec, newSpec);
1233 return d->mRecurrence.defaultRRuleConst();
1241 if (d->mCachedType == -1)
1242 d->mCachedType = type(d->mRecurrence.defaultRRuleConst());
1243 return static_cast<Type>(d->mCachedType);
1251 switch (Recurrence::recurrenceType(rrule))
1253 case Recurrence::rMinutely:
return MINUTELY;
1254 case Recurrence::rDaily:
return DAILY;
1255 case Recurrence::rWeekly:
return WEEKLY;
1256 case Recurrence::rMonthlyDay:
return MONTHLY_DAY;
1257 case Recurrence::rMonthlyPos:
return MONTHLY_POS;
1258 case Recurrence::rYearlyMonth:
return ANNUAL_DATE;
1259 case Recurrence::rYearlyPos:
return ANNUAL_POS;
1261 if (dailyType(rrule))
1272 if (rrule->recurrenceType() != RecurrenceRule::rDaily
1273 || !rrule->bySeconds().isEmpty()
1274 || !rrule->byMinutes().isEmpty()
1275 || !rrule->byHours().isEmpty()
1276 || !rrule->byWeekNumbers().isEmpty()
1277 || !rrule->byMonthDays().isEmpty()
1278 || !rrule->byMonths().isEmpty()
1279 || !rrule->bySetPos().isEmpty()
1280 || !rrule->byYearDays().isEmpty())
1282 const QList<RecurrenceRule::WDayPos> days = rrule->byDays();
1287 for (
int i = 0, end = days.count(); i < end; ++i)
1289 if (days[i].pos() != 0)
QList< KCalCore::RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
void setFrequency(int freq)
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)
KARecurrence & operator=(const KARecurrence &r)
Assignment operator.
void setDuration(int duration)
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 endDateTime() const
Return the date/time of the last recurrence.
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.
void setEndDt(const KDateTime &endDateTime)
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.
KDateTime endDt(bool *result=0) const
KDateTime startDt() const
bool recursOn(const QDate &date, const KDateTime::Spec &timeSpec) const
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.
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.
int durationTo(const KDateTime &dt) 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.
void setAllDay(bool allDay)
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 setStartDt(const KDateTime &start)
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...
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...
KDateTime getPreviousDate(const KDateTime &afterDateTime) const