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
48class Recurrence_p :
public Recurrence
51 using Recurrence::setNewRecurrenceType;
54 Recurrence_p(
const Recurrence_p& r) :
Recurrence(r) {}
57class KARecurrence::Private
62 explicit Private(
const Recurrence& r)
63 : mRecurrence(r), mFeb29Type(
Feb29_None), mCachedType(-1) {}
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;
75 int combineDurations(
const RecurrenceRule*,
const RecurrenceRule*, QDate& end)
const;
78 Recurrence_p mRecurrence;
80 mutable int mCachedType;
101KARecurrence::KARecurrence()
105KARecurrence::KARecurrence(
const Recurrence& r)
111KARecurrence::KARecurrence(
const KARecurrence& r)
112 : d(new Private(*r.d))
115KARecurrence::~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);
164bool 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)))
308void 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:
354 || rrule->
frequency() != rrules[0]->frequency()
355 || rrule->
startDt() != rrules[0]->startDt())
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)
379 || rrule->
frequency() != rrules[0]->frequency()
380 || rrule->
startDt() != rrules[0]->startDt())
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());
489 int freq = mRecurrence.frequency();
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();
626KDateTime 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);
697 return end.isValid() ? end.date() : QDate();
702 d->mRecurrence.setEndDate(
endDate);
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);
820 return d->mRecurrence.getNextDateTime(preDateTime);
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;
966 KDateTime next1(rr.getNextDate(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();
1002 const QList<RecurrenceRule::WDayPos>
days = d->mRecurrence.defaultRRuleConst()->byDays();
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;
1040 if (ds[d->mRecurrence.startDate().dayOfWeek() - 1])
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;
1074 if (7 - span > maxgap)
1087 const QList<int> months = d->mRecurrence.yearMonths();
1088 if (months.isEmpty())
1090 if (months.count() == 1)
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));
1110 if (365 - span > maxgap)
1127 int freq = d->mRecurrence.frequency();
1134 const QList<RecurrenceRule::WDayPos>
days = d->mRecurrence.defaultRRuleConst()->byDays();
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])
1153 for (
int i = 0; i < 7; ++i)
1164 const QList<RecurrenceRule::WDayPos>
days = d->mRecurrence.defaultRRuleConst()->byDays();
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)
1196 return d->mRecurrence.exDateTimes();
1199DateList KARecurrence::exDates()
const
1201 return d->mRecurrence.exDates();
1204void KARecurrence::setExDateTimes(
const DateTimeList& exdates)
1206 d->mRecurrence.setExDateTimes(exdates);
1209void KARecurrence::setExDates(
const DateList& exdates)
1211 d->mRecurrence.setExDates(exdates);
1214void KARecurrence::addExDateTime(
const KDateTime& exdate)
1216 d->mRecurrence.addExDateTime(exdate);
1219void 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);
1251 case Recurrence::rMinutely:
return MINUTELY;
1252 case Recurrence::rDaily:
return DAILY;
1253 case Recurrence::rWeekly:
return WEEKLY;
1256 case Recurrence::rYearlyMonth:
return ANNUAL_DATE;
1257 case Recurrence::rYearlyPos:
return ANNUAL_POS;
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)
Represents recurrences for KAlarm.
void addMonthlyPos(short pos, const QBitArray &days)
Adds a position (e.g.
QList< int > yearDates() const
Returns the dates within a yearly recurrence.
void addYearlyDay(int day)
Adds day number of year within a yearly recurrence.
void clear()
Removes all recurrence and exception rules and dates.
static void setDefaultFeb29Type(Feb29Type t)
Set the default way that 29th February annual recurrences should occur in non-leap years.
static bool dailyType(const KCalCore::RecurrenceRule *)
Check if the recurrence rule is a daily rule with or without BYDAYS specified.
bool recursAt(const KDateTime &dt) const
Returns true if the date/time specified is one at which the event will recur.
KDateTime startDateTime() const
Return the start date/time of the recurrence (Time for all-day recurrences will be 0:00).
QDate endDate() const
Return the date of the last recurrence.
bool set(const QString &icalRRULE)
Initialise the recurrence from an iCalendar RRULE string.
bool recurs() const
Returns whether the event recurs at all.
void setFrequency(int freq)
Sets the frequency of recurrence, in terms of the recurrence time period type.
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 setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
KDateTime endDateTime() const
Return the date/time of the last recurrence.
void addYearlyDate(int date)
Adds date within a yearly recurrence.
bool recurReadOnly() const
Returns true if the recurrence is read-only, or false if it can be changed.
int durationTo(const KDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
QList< int > monthDays() const
Returns list of day numbers of a month.
Type
The recurrence's period type.
@ MINUTELY
at an hours/minutes interval
@ ANNUAL_DATE
yearly, on a specified date in each of the specified months
@ ANNUAL_POS
yearly, on specified weekdays in the specified weeks of the specified months
@ WEEKLY
weekly, on specified weekdays
@ MONTHLY_DAY
monthly, on a specified day of the month
@ MONTHLY_POS
monthly, on specified weekdays in a specified week of the month
void setStartDateTime(const KDateTime &dt, bool dateOnly)
Set the recurrence start date/time, and optionally set it to all-day.
QList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
void addWeeklyDays(const QBitArray &days)
Adds days to the weekly day recurrence list.
Feb29Type
When annual February 29th recurrences should occur in non-leap years.
@ Feb29_None
does not occur in non-leap years
QBitArray days() const
Returns week day mask (bit 0 = Monday).
KDateTime getPreviousDateTime(const KDateTime &afterDateTime) const
Get the previous time the recurrence occurred, strictly before a specified time.
bool recursOn(const QDate &, const KDateTime::Spec &) const
Return whether the event will recur on the specified date.
KARecurrence & operator=(const KARecurrence &r)
Assignment operator.
static Feb29Type defaultFeb29Type()
Return the default way that 29th February annual recurrences should occur in non-leap years.
void fix()
Convert the recurrence to KARecurrence types.
bool allDay() const
Set whether the recurrence has no time, just a date.
KDateTime getNextDateTime(const KDateTime &preDateTime) const
Get the next time the recurrence occurs, strictly after a specified time.
Type type() const
Return the recurrence's period type.
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
void writeRecurrence(KCalCore::Recurrence &) const
Initialise a KCalCore::Recurrence to be the same as this instance.
Feb29Type feb29Type() const
Return when 29th February annual recurrences should occur in non-leap years.
void addMonthlyDate(short day)
Adds a date (e.g.
bool init(KCalCore::RecurrenceRule::PeriodType t, int freq, int count, const KDateTime &start, const KDateTime &end)
Set up a KARecurrence from recurrence parameters.
KCalCore::Duration longestInterval() const
Return the longest interval between recurrences.
QList< KCalCore::RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
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...
void setEndDate(const QDate &endDate)
Sets the date of the last recurrence.
QList< KCalCore::RecurrenceRule::WDayPos > yearPositions() const
Returns the positions within a yearly recurrence.
void addYearlyMonth(short month)
Adds month in yearly recurrence.
bool operator==(const KARecurrence &r) const
Comparison operator for equality.
QList< int > yearMonths() const
Returns the months within a yearly recurrence.
void setEndDateTime(const KDateTime &endDateTime)
Sets the date and time of the last recurrence.
KCalCore::Duration regularInterval() const
Return the interval between recurrences, if the interval between successive occurrences does not vary...
void setRecurReadOnly(bool readOnly)
Set if recurrence is read-only or can be changed.
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.
QDate startDate() const
Return the start date/time of the recurrence.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
void setAllDay(bool allDay)
void setDuration(int duration)
void setFrequency(int freq)
int durationTo(const KDateTime &dt) const
KDateTime startDt() const
void setEndDt(const KDateTime &endDateTime)
bool recursOn(const QDate &date, const KDateTime::Spec &timeSpec) const
KDateTime getPreviousDate(const KDateTime &afterDateTime) const
KDateTime endDt(bool *result=0) const
void setStartDt(const KDateTime &start)
ushort recurrenceType() const
void setEndDateTime(const KDateTime &endDateTime)
void setStartDateTime(const KDateTime &start)
KDateTime getPreviousDateTime(const KDateTime &afterDateTime) const
void addRRule(RecurrenceRule *rrule)
KDateTime getNextDateTime(const KDateTime &preDateTime) const
void setDuration(int duration)
SortableList< KDateTime > DateTimeList
SortableList< QDate > DateList