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)
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