22#include "recurrencerule.h"
26#include <QtCore/QStringList>
27#include <QtCore/QTime>
32const int LOOP_LIMIT = 10000;
56 static QString
dayName(
short day);
81QString DateHelper::dayName(
short day)
135 }
else if (
daysto > 355) {
156 return dt.daysTo(
dt1) / 7;
173 return mDay ==
pos2.mDay && mPos ==
pos2.mPos;
178 return !operator==(
pos2);
188 typedef QList<Constraint> List;
286Constraint::Constraint(KDateTime::Spec
spec,
int wkst)
301void Constraint::clear()
344 if (day > 0 && day !=
dt.day()) {
347 if (day < 0 &&
dt.day() != (
dt.daysInMonth() + day + 1)) {
357 if ((type == RecurrenceRule::rMonthly) ||
358 (type == RecurrenceRule::rYearly &&
month > 0)) {
375 weekdaynr != -((
dt.daysInYear() -
dt.dayOfYear()) / 7 + 1)) {
395 if ((
hour >= 0 && (
hour !=
dt.time().hour() ||
422 case RecurrenceRule::rSecondly:
425 case RecurrenceRule::rMinutely:
428 case RecurrenceRule::rHourly:
429 t.setHMS(
hour, 0, 0);
431 case RecurrenceRule::rDaily:
433 case RecurrenceRule::rWeekly:
437 case RecurrenceRule::rMonthly:
441 case RecurrenceRule::rYearly:
442 d.setYMD(
year, 1, 1);
461#define mergeConstraint( name, cmparison ) \
462 if ( interval.name cmparison ) { \
463 if ( !( name cmparison ) ) { \
464 name = interval.name; \
465 } else if ( name != interval.name ) { \
484#undef mergeConstraint
529 }
else if (day < 0) {
565 for (
int i = 0;
i < 7; ++
i) {
579 bool inMonth = (type == RecurrenceRule::rMonthly) ||
580 (type == RecurrenceRule::rYearly &&
month > 0);
588 dt =
dt.addMonths(1);
612 QList<KDateTime>
valid;
623void Constraint::appendDateTime(
const QDate &
date,
const QTime &time,
624 QList<KDateTime> &
list)
const
629 dt.setSecondOccurrence(
true);
642 case RecurrenceRule::rSecondly:
645 case RecurrenceRule::rMinutely:
648 case RecurrenceRule::rHourly:
651 case RecurrenceRule::rDaily:
654 case RecurrenceRule::rWeekly:
657 case RecurrenceRule::rMonthly:
660 case RecurrenceRule::rYearly:
678 case RecurrenceRule::rSecondly:
680 case RecurrenceRule::rMinutely:
682 case RecurrenceRule::rHourly:
685 case RecurrenceRule::rDaily:
686 day =
dt.date().day();
687 case RecurrenceRule::rMonthly:
689 case RecurrenceRule::rYearly:
692 case RecurrenceRule::rWeekly:
709class KCalCore::RecurrenceRule::Private
726 Private &operator=(
const Private &
other);
727 bool operator==(
const Private &
other)
const;
803RecurrenceRule::Private &RecurrenceRule::Private::operator=(
const Private &
p)
837bool RecurrenceRule::Private::operator==(
const Private &
r)
const
842 (!
mDateStart.isValid() && !
r.mDateStart.isValid())) &&
845 (!
mDateEnd.isValid() && !
r.mDateEnd.isValid())) &&
862void RecurrenceRule::Private::clear()
883void RecurrenceRule::Private::setDirty()
910RecurrenceRule::~RecurrenceRule()
946void RecurrenceRule::setRecurrenceType(PeriodType
period)
960 if (
d->mPeriod == rNone) {
963 if (
d->mDuration < 0) {
966 if (
d->mDuration == 0) {
976 if (!
d->buildCache()) {
983 return d->mCachedDateEnd;
991 d->mDateEnd = dateTime;
1019void RecurrenceRule::setDirty()
1029 d->mDateStart = start;
1038 d->mFrequency =
freq;
1042void RecurrenceRule::setBySeconds(
const QList<int> &bySeconds)
1047 d->mBySeconds = bySeconds;
1051void RecurrenceRule::setByMinutes(
const QList<int> &byMinutes)
1056 d->mByMinutes = byMinutes;
1060void RecurrenceRule::setByHours(
const QList<int> &byHours)
1065 d->mByHours = byHours;
1069void RecurrenceRule::setByDays(
const QList<WDayPos> &byDays)
1074 d->mByDays = byDays;
1078void RecurrenceRule::setByMonthDays(
const QList<int> &byMonthDays)
1083 d->mByMonthDays = byMonthDays;
1087void RecurrenceRule::setByYearDays(
const QList<int> &byYearDays)
1092 d->mByYearDays = byYearDays;
1096void RecurrenceRule::setByWeekNumbers(
const QList<int> &byWeekNumbers)
1101 d->mByWeekNumbers = byWeekNumbers;
1105void RecurrenceRule::setByMonths(
const QList<int> &byMonths)
1110 d->mByMonths = byMonths;
1114void RecurrenceRule::setBySetPos(
const QList<int> &bySetPos)
1119 d->mBySetPos = bySetPos;
1123void RecurrenceRule::setWeekStart(
short weekStart)
1128 d->mWeekStart = weekStart;
1134 d->mDateStart =
d->mDateStart.toTimeSpec(
oldSpec);
1135 d->mDateStart.setTimeSpec(
newSpec);
1136 if (
d->mDuration == 0) {
1137 d->mDateEnd =
d->mDateEnd.toTimeSpec(
oldSpec);
1200void RecurrenceRule::Private::buildConstraints()
1213 Constraint::List
tmp;
1215#define intConstraint( list, setElement ) \
1216 if ( !list.isEmpty() ) { \
1217 mNoByRules = false; \
1218 iend = list.count(); \
1219 if ( iend == 1 ) { \
1220 for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \
1221 mConstraints[c].setElement( list[0] ); \
1224 for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \
1225 for ( i = 0; i < iend; ++i ) { \
1226 con = mConstraints[c]; \
1227 con.setElement( list[i] ); \
1228 tmp.append( con ); \
1231 mConstraints = tmp; \
1259#define fixConstraint( setElement, value ) \
1261 for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \
1262 mConstraints[c].setElement( value ); \
1333bool RecurrenceRule::Private::buildCache()
const
1383 for (
int i = 0,
iend =
d->mConstraints.count();
i <
iend; ++
i) {
1384 if (
d->mConstraints[
i].matches(
dt, recurrenceType())) {
1395 if (!
qd.isValid() || !
d->mDateStart.isValid()) {
1408 if (
d->mDuration >= 0) {
1409 endDate =
endDt().date();
1419 match =
d->mConstraints[
i].matches(
qd, recurrenceType());
1429 if (!
interval.matches(
qd, recurrenceType())) {
1440 return dts[
i].date() ==
qd;
1444 }
while (
interval.intervalDateTime(recurrenceType()) < end);
1450 KDateTime end = start.addDays(1).toTimeSpec(
d->mDateStart.timeSpec());
1451 start = start.toTimeSpec(
d->mDateStart.timeSpec());
1456 start =
d->mDateStart;
1460 if (
d->mDuration >= 0) {
1472 if (
d->mTimedRepetition) {
1474 int n =
static_cast<int>((
d->mDateStart.secsTo_long(start) - 1) %
d->mTimedRepetition);
1475 return start.addSecs(
d->mTimedRepetition -
n) < end;
1489 match =
d->mConstraints[
i].matches(
startDay.addDays(day), recurrenceType());
1510 }
while (
intervalm.intervalDateTime(recurrenceType()) < end);
1522 return dts[
i] <= end;
1525 }
while (
interval.intervalDateTime(recurrenceType()) < end);
1542 if (
d->mDuration >= 0 &&
dt >
endDt()) {
1546 if (
d->mTimedRepetition) {
1548 return !(
d->mDateStart.secsTo_long(
dt) %
d->mTimedRepetition);
1560 if (
interval.matches(
dt, recurrenceType())) {
1573 KDateTime end = start.addDays(1).addSecs(-1);
1576 lst +=
dts[
i].toTimeSpec(timeSpec).time();
1593 return d->mDuration;
1596 if (
d->mTimedRepetition) {
1598 return static_cast<int>(
d->mDateStart.secsTo_long(
toDate) /
d->mTimedRepetition);
1619 if (
d->mTimedRepetition) {
1623 prev =
endDt().addSecs(1).toTimeSpec(
d->mDateStart.timeSpec());
1625 int n =
static_cast<int>((
d->mDateStart.secsTo_long(
prev) - 1) %
d->mTimedRepetition);
1634 if (
d->mDuration > 0) {
1638 int i =
d->mCachedDates.findLT(
toDate);
1640 return d->mCachedDates[
i];
1647 prev =
endDt().addSecs(1).toTimeSpec(
d->mDateStart.timeSpec());
1658 while (
interval.intervalDateTime(recurrenceType()) >
d->mDateStart) {
1663 if (!
dts.isEmpty()) {
1665 if (
prev.isValid() &&
prev >=
d->mDateStart) {
1689 if (
d->mTimedRepetition) {
1691 int n =
static_cast<int>((
d->mDateStart.secsTo_long(
fromDate) + 1) %
d->mTimedRepetition);
1696 if (
d->mDuration > 0) {
1702 return d->mCachedDates[
i];
1714 if (
d->mDuration >= 0 &&
interval.intervalDateTime(recurrenceType()) > end) {
1724 if (
dts.count() > 0) {
1726 if (
d->mDuration >= 0 &&
ret > end) {
1733 }
while (++
loop < LOOP_LIMIT &&
1734 (
d->mDuration < 0 ||
interval.intervalDateTime(recurrenceType()) < end));
1741 const KDateTime start = dtStart.toTimeSpec(
d->mDateStart.timeSpec());
1742 const KDateTime end = dtEnd.toTimeSpec(
d->mDateStart.timeSpec());
1748 if (
d->mDuration >= 0) {
1760 if (
d->mTimedRepetition) {
1765 if (
d->mDateStart < start) {
1767 d->mTimedRepetition - (
d->mDateStart.secsTo_long(start) %
d->mTimedRepetition);
1774 static_cast<int>(
dt.secsTo_long(
enddt) /
d->mTimedRepetition) + 1;
1779 dt =
dt.addSecs(
d->mTimedRepetition), ++
i) {
1788 if (
d->mDuration > 0) {
1792 if (
d->mCachedDateEnd.isValid() && start >
d->mCachedDateEnd) {
1795 int i =
d->mCachedDates.findGE(start);
1799 iend =
d->mCachedDates.count();
1807 if (
d->mCachedDateEnd.isValid()) {
1809 }
else if (!
result.isEmpty()) {
1817 st =
d->mCachedLastDate.addSecs(1);
1842 }
while (++
loop < LOOP_LIMIT &&
1843 interval.intervalDateTime(recurrenceType()) < end);
1853 PeriodType type)
const
1881 start = start.addDays(-(7 + start.date().dayOfWeek() -
mWeekStart) % 7);
1893 periods = 12 * (
toDate.date().year() - start.date().year()) +
1894 (
toDate.date().month() - start.date().month());
1901 start.setDate(
QDate(start.date().year(), start.date().month(), 1));
1925 PeriodType type)
const
1955 start = start.addDays(-(7 + start.date().dayOfWeek() -
mWeekStart) % 7);
1967 periods = 12 * (
toDate.date().year() - start.date().year()) +
1968 (
toDate.date().month() - start.date().month());
1975 start.setDate(
QDate(start.date().year(), start.date().month(), 1));
1995 PeriodType type)
const
2039 if (pos >= 0 && pos <
tmplst.count()) {
2054 if (!
d->mRRule.isEmpty()) {
2055 kDebug() <<
" RRULE=" <<
d->mRRule;
2059 kDebug() <<
" Period type:" << int(recurrenceType()) <<
", frequency:" <<
frequency();
2062 <<
", end date:" << dumpTime(
endDt());
2064#define dumpByIntList(list,label) \
2065 if ( !list.isEmpty() ) {\
2067 for ( int i = 0, iend = list.count(); i < iend; ++i ) {\
2068 lst.append( QString::number( list[i] ) );\
2070 kDebug() << " " << label << lst.join( QLatin1String(", ") );\
2075 if (!
d->mByDays.isEmpty()) {
2077 for (
int i = 0,
iend =
d->mByDays.count();
i <
iend; ++
i) {
2080 DateHelper::dayName(
d->mByDays[
i].day()));
2091 kDebug() <<
" Week start:" << DateHelper::dayName(
d->mWeekStart);
2093 kDebug() <<
" Constraints:";
2095 for (
int i = 0,
iend =
d->mConstraints.count();
i <
iend; ++
i) {
2096 d->mConstraints[
i].dump();
2102void Constraint::dump()
const
2120 if (!
dt.isValid()) {
2124 if (
dt.isDateOnly()) {
2128 if (
dt.isSecondOccurrence()) {
2132 if (
dt.timeSpec() == KDateTime::Spec::ClockTime()) {
2144 return d->mDateStart;
2154 return d->mFrequency;
2159 return d->mDuration;
2162QString RecurrenceRule::rrule()
const
2174 return d->mIsReadOnly;
2184 return d->mPeriod != rNone;
2192const QList<int> &RecurrenceRule::bySeconds()
const
2194 return d->mBySeconds;
2197const QList<int> &RecurrenceRule::byMinutes()
const
2199 return d->mByMinutes;
2202const QList<int> &RecurrenceRule::byHours()
const
2207const QList<RecurrenceRule::WDayPos> &RecurrenceRule::byDays()
const
2212const QList<int> &RecurrenceRule::byMonthDays()
const
2214 return d->mByMonthDays;
2217const QList<int> &RecurrenceRule::byYearDays()
const
2219 return d->mByYearDays;
2222const QList<int> &RecurrenceRule::byWeekNumbers()
const
2224 return d->mByWeekNumbers;
2227const QList<int> &RecurrenceRule::byMonths()
const
2229 return d->mByMonths;
2232const QList<int> &RecurrenceRule::bySetPos()
const
2234 return d->mBySetPos;
2237short RecurrenceRule::weekStart()
const
2239 return d->mWeekStart;
2242RecurrenceRule::RuleObserver::~RuleObserver()
2246RecurrenceRule::WDayPos::WDayPos(
int ps,
short dy)
2247 : mDay(
dy), mPos(
ps)
2251void RecurrenceRule::WDayPos::setDay(
short dy)
2256short RecurrenceRule::WDayPos::day()
const
2261void RecurrenceRule::WDayPos::setPos(
int ps)
2266int RecurrenceRule::WDayPos::pos()
const
2274 out <<
c.year <<
c.month <<
c.day <<
c.hour <<
c.minute <<
c.second
2275 <<
c.weekday <<
c.weekdaynr <<
c.weeknumber <<
c.yearday <<
c.weekstart
2276 <<
c.timespec <<
c.secondOccurrence;
2283 in >>
c.year >>
c.month >>
c.day >>
c.hour >>
c.minute >>
c.second
2284 >>
c.weekday >>
c.weekdaynr >>
c.weeknumber >>
c.yearday >>
c.weekstart
2285 >>
c.timespec >>
c.secondOccurrence;
2291 out <<
w.mDay <<
w.mPos;
2297 in >>
w.mDay >>
w.mPos;
2306 RecurrenceRule::Private *
d =
r->d;
2307 out <<
d->mRRule <<
static_cast<quint32>(
d->mPeriod) <<
d->mDateStart <<
d->mFrequency <<
d->mDuration <<
d->mDateEnd
2308 <<
d->mBySeconds <<
d->mByMinutes <<
d->mByHours <<
d->mByDays <<
d->mByMonthDays
2309 <<
d->mByYearDays <<
d->mByWeekNumbers <<
d->mByMonths <<
d->mBySetPos
2310 <<
d->mWeekStart <<
d->mConstraints <<
d->mAllDay <<
d->mNoByRules <<
d->mTimedRepetition
2322 RecurrenceRule::Private *
d =
r->d;
2324 in >>
d->mRRule >>
period >>
d->mDateStart >>
d->mFrequency >>
d->mDuration >>
d->mDateEnd
2325 >>
d->mBySeconds >>
d->mByMinutes >>
d->mByHours >>
d->mByDays >>
d->mByMonthDays
2326 >>
d->mByYearDays >>
d->mByWeekNumbers >>
d->mByMonths >>
d->mBySetPos
2327 >>
d->mWeekStart >>
d->mConstraints >>
d->mAllDay >>
d->mNoByRules >>
d->mTimedRepetition
structure for describing the n-th weekday of the month/year.
This class represents a recurrence rule for a calendar incidence.
void setAllDay(bool allDay)
Sets whether the dtstart is all-day (i.e.
void setReadOnly(bool readOnly)
Set if recurrence is read-only or can be changed.
void clear()
Turns off recurrence for the event.
PeriodType
enum for describing the frequency how an event recurs, if at all.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
bool allDay() const
Returns whether the start date has no time associated.
void dump() const
Debug output.
bool recurs() const
Returns the event's recurrence status.
void setFrequency(int freq)
Sets the recurrence frequency, in terms of the recurrence time period type.
RecurrenceRule()
/************************************************************************** RecurrenceRule *
uint frequency() const
Returns the recurrence frequency, in terms of the recurrence time period type.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
friend KCALCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalCore::RecurrenceRule *)
RecurrenceRule serializer and deserializer.
int durationTo(const KDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
KDateTime startDt() const
Returns the recurrence start date/time.
void setRRule(const QString &rrule)
Set the RRULE string for the rule.
void shiftTimes(const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec)
Shift the times of the rule so that they appear at the same clock time as before but in a new time zo...
void setEndDt(const KDateTime &endDateTime)
Sets the date and time of the last recurrence.
bool recursOn(const QDate &date, const KDateTime::Spec &timeSpec) const
Returns true if the date specified is one on which the event will recur.
KDateTime getNextDate(const KDateTime &preDateTime) const
Returns the date and time of the next recurrence, after the specified date/time.
bool recursAt(const KDateTime &dt) const
Returns true if the date/time specified is one at which the event will recur.
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.
KDateTime getPreviousDate(const KDateTime &afterDateTime) const
Returns the date and time of the last previous recurrence, before the specified date/time.
KDateTime endDt(bool *result=0) const
Returns the date and time of the last recurrence.
bool isReadOnly() const
Returns true if the recurrence is read-only; false if it can be changed.
void addObserver(RuleObserver *observer)
Installs an observer.
bool dateMatchesRules(const KDateTime &dt) const
Returns true if the date matches the rules.
void setStartDt(const KDateTime &start)
Sets the recurrence start date/time.
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 removeObserver(RuleObserver *observer)
Removes an observer that was added with addObserver.
A QList which can be sorted.
int findGE(const T &value, int start=0) const
Search the list for the first item >= value.
int findLT(const T &value, int start=0) const
Search the list for the last item < value.
int findGT(const T &value, int start=0) const
Search the list for the first item > value.
KCALCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalCore::Alarm::Ptr &)
Alarm deserializer.
KCALCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalCore::Alarm::Ptr &)
Alarm serializer.
static uint qHash(const KDateTime &dt)
Private class that helps to provide binary compatibility between releases.