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) {}
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 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);
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)))
308 void 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:
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)
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());
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());
603 return d->mRecurrence.startDateTime();
608 return d->mRecurrence.startDate();
613 d->mRecurrence.setStartDateTime(dt);
615 d->mRecurrence.setAllDay(
true);
623 return d->endDateTime();
626 KDateTime 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);
696 KDateTime end = endDateTime();
697 return end.isValid() ? end.date() : QDate();
702 d->mRecurrence.setEndDate(endDate);
707 d->mRecurrence.setEndDateTime(endDateTime);
712 return d->mRecurrence.allDay();
717 d->mRecurrence.setRecurReadOnly(readOnly);
722 return d->mRecurrence.recurReadOnly();
727 return d->mRecurrence.recurs();
732 return d->mRecurrence.days();
737 return d->mRecurrence.monthPositions();
742 return d->mRecurrence.monthDays();
747 return d->mRecurrence.yearDays();
752 return d->mRecurrence.yearDates();
757 return d->mRecurrence.yearMonths();
762 return d->mRecurrence.yearPositions();
767 d->mRecurrence.addWeeklyDays(days);
772 d->mRecurrence.addYearlyDay(day);
777 d->mRecurrence.addYearlyDate(date);
782 d->mRecurrence.addYearlyMonth(month);
787 d->mRecurrence.addYearlyPos(pos, days);
792 d->mRecurrence.addMonthlyPos(pos, days);
797 d->mRecurrence.addMonthlyPos(pos, day);
802 d->mRecurrence.addMonthlyDate(day);
816 writeRecurrence(recur);
820 return d->mRecurrence.getNextDateTime(preDateTime);
835 writeRecurrence(recur);
839 return d->mRecurrence.getPreviousDateTime(afterDateTime);
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)
870 return d->mRecurrence.recursAt(dt);
875 return d->mRecurrence.recurTimesOn(date, timeSpec);
880 return d->mRecurrence.timesInInterval(start, end);
885 return d->mRecurrence.frequency();
890 d->mRecurrence.setFrequency(freq);
895 return d->mRecurrence.duration();
900 d->mRecurrence.setDuration(duration);
905 return d->mRecurrence.durationTo(dt);
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;
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();
998 return Duration(freq * 60, Duration::Seconds);
1002 const QList<RecurrenceRule::WDayPos>
days = d->mRecurrence.defaultRRuleConst()->byDays();
1004 return Duration(freq, Duration::Days);
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;
1034 return Duration(maxgap, Duration::Days);
1040 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
1041 return Duration(freq, Duration::Days);
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;
1073 return Duration(freq*7 - span, Duration::Days);
1074 if (7 - span > maxgap)
1075 return Duration(7 - span, Duration::Days);
1076 return Duration(maxgap, Duration::Days);
1080 return Duration(freq * 31, Duration::Days);
1087 const QList<int> months = d->mRecurrence.yearMonths();
1088 if (months.isEmpty())
1090 if (months.count() == 1)
1091 return Duration(freq * 365, Duration::Days);
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));
1109 return Duration(freq*365 - span, Duration::Days);
1110 if (365 - span > maxgap)
1111 return Duration(365 - span, Duration::Days);
1112 return Duration(maxgap, Duration::Days);
1127 int freq = d->mRecurrence.frequency();
1131 return Duration(freq * 60, Duration::Seconds);
1134 const QList<RecurrenceRule::WDayPos>
days = d->mRecurrence.defaultRRuleConst()->byDays();
1136 return Duration(freq, Duration::Days);
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])
1149 return Duration(freq, Duration::Days);
1153 for (
int i = 0; i < 7; ++i)
1157 return Duration(freq, Duration::Days);
1159 return Duration(freq * 7, Duration::Days);
1164 const QList<RecurrenceRule::WDayPos>
days = d->mRecurrence.defaultRRuleConst()->byDays();
1166 return Duration(freq * 7, Duration::Days);
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)
1181 return Duration(freq, Duration::Days);
1185 return Duration(freq * 7, Duration::Days);
1196 return d->mRecurrence.exDateTimes();
1199 DateList KARecurrence::exDates()
const 1201 return d->mRecurrence.exDates();
1204 void KARecurrence::setExDateTimes(
const DateTimeList& exdates)
1206 d->mRecurrence.setExDateTimes(exdates);
1209 void KARecurrence::setExDates(
const DateList& exdates)
1211 d->mRecurrence.setExDates(exdates);
1214 void KARecurrence::addExDateTime(
const KDateTime& exdate)
1216 d->mRecurrence.addExDateTime(exdate);
1219 void KARecurrence::addExDate(
const QDate& exdate)
1221 d->mRecurrence.addExDate(exdate);
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);
1249 switch (Recurrence::recurrenceType(rrule))
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)
QBitArray days
Days in week, element 0 = Monday.
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.
Type
The category of an event, indicated by the middle part of its UID.
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.
KDateTime getNextDate(const KDateTime &preDateTime) const
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