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