9 #include "recurrencerule.h" 10 #include "kcalendarcore_debug.h" 11 #include "recurrencehelper_p.h" 14 #include <QDataStream> 15 #include <QStringList> 23 const int LOOP_LIMIT = 10000;
49 static QString dayName(
short day);
51 static QDate getNthWeek(
int year,
int weeknumber,
short weekstart = 1);
52 static int weekNumbersInYear(
int year,
short weekstart = 1);
53 static int getWeekNumber(
const QDate &date,
short weekstart,
int *year =
nullptr);
54 static int getWeekNumberNeg(
const QDate &date,
short weekstart,
int *year =
nullptr);
57 static QDate getDate(
int year,
int month,
int day)
60 return QDate(year, month, day);
74 QString DateHelper::dayName(
short day)
78 return QStringLiteral(
"MO");
80 return QStringLiteral(
"TU");
82 return QStringLiteral(
"WE");
84 return QStringLiteral(
"TH");
86 return QStringLiteral(
"FR");
88 return QStringLiteral(
"SA");
90 return QStringLiteral(
"SU");
92 return QStringLiteral(
"??");
97 QDate DateHelper::getNthWeek(
int year,
int weeknumber,
short weekstart)
99 if (weeknumber == 0) {
104 QDate dt(year, 1, 4);
105 int adjust = -(7 + dt.dayOfWeek() - weekstart) % 7;
106 if (weeknumber > 0) {
107 dt = dt.addDays(7 * (weeknumber - 1) + adjust);
108 }
else if (weeknumber < 0) {
110 dt = dt.addDays(7 * weeknumber + adjust);
115 int DateHelper::getWeekNumber(
const QDate &date,
short weekstart,
int *year)
119 dt = dt.addDays(-(7 + dt.dayOfWeek() - weekstart) % 7);
121 qint64 daysto = dt.daysTo(date);
126 dt = dt.addDays(-(7 + dt.dayOfWeek() - weekstart) % 7);
127 daysto = dt.daysTo(date);
128 }
else if (daysto > 355) {
130 QDate dtn(y + 1, 1, 4);
131 dtn = dtn.addDays(-(7 + dtn.dayOfWeek() - weekstart) % 7);
132 qint64 dayston = dtn.daysTo(date);
142 return daysto / 7 + 1;
145 int DateHelper::weekNumbersInYear(
int year,
short weekstart)
147 QDate dt(year, 1, weekstart);
148 QDate dt1(year + 1, 1, weekstart);
149 return dt.daysTo(dt1) / 7;
153 int DateHelper::getWeekNumberNeg(
const QDate &date,
short weekstart,
int *year)
155 int weekpos = getWeekNumber(date, weekstart, year);
156 return weekNumbersInYear(*year, weekstart) - weekpos - 1;
166 return mDay == pos2.mDay && mPos == pos2.mPos;
186 explicit Constraint(
const QTimeZone &,
int wkst = 1);
209 void setMinute(
int n)
214 void setSecond(
int n)
219 void setWeekday(
int n)
224 void setWeekdaynr(
int n)
229 void setWeeknumber(
int n)
234 void setYearday(
int n)
239 void setWeekstart(
int n)
261 bool merge(
const Constraint &interval);
270 mutable bool useCachedDt;
274 Constraint::Constraint(
const QTimeZone &timeZone,
int wkst)
283 , timeZone(dt.timeZone())
286 readDateTime(dt, type);
289 void Constraint::clear()
309 if (weeknumber == 0) {
310 if (year > 0 && year != dt.
year()) {
315 if (weeknumber > 0 && weeknumber != DateHelper::getWeekNumber(dt, weekstart, &y)) {
318 if (weeknumber < 0 && weeknumber != DateHelper::getWeekNumberNeg(dt, weekstart, &y)) {
321 if (year > 0 && year != y) {
326 if (month > 0 && month != dt.
month()) {
329 if (day > 0 && day != dt.
day()) {
339 if (weekdaynr != 0) {
342 if ((type == RecurrenceRule::rMonthly) || (type == RecurrenceRule::rYearly && month > 0)) {
344 if (weekdaynr > 0 && weekdaynr != (dt.
day() - 1) / 7 + 1) {
347 if (weekdaynr < 0 && weekdaynr != -((dt.
daysInMonth() - dt.
day()) / 7 + 1)) {
352 if (weekdaynr > 0 && weekdaynr != (dt.
dayOfYear() - 1) / 7 + 1) {
361 if (yearday > 0 && yearday != dt.
dayOfYear()) {
375 if ((hour >= 0 && hour != dt.
time().
hour()) || (minute >= 0 && minute != dt.
time().
minute()) || (second >= 0 && second != dt.
time().
second())
376 || !matches(dt.
date(), type)) {
397 bool subdaily =
true;
399 case RecurrenceRule::rSecondly:
400 t.setHMS(hour, minute, second);
402 case RecurrenceRule::rMinutely:
403 t.setHMS(hour, minute, 0);
405 case RecurrenceRule::rHourly:
406 t.setHMS(hour, 0, 0);
408 case RecurrenceRule::rDaily:
410 case RecurrenceRule::rWeekly:
411 d = DateHelper::getNthWeek(year, weeknumber, weekstart);
414 case RecurrenceRule::rMonthly:
418 case RecurrenceRule::rYearly:
426 d = DateHelper::getDate(year, (month > 0) ? month : 1, day ? day : 1);
433 bool Constraint::merge(
const Constraint &interval)
436 #define mergeConstraint( name, cmparison ) \ 437 if ( interval.name cmparison ) { \ 438 if ( !( name cmparison ) ) { \ 439 name = interval.name; \ 440 } else if ( name != interval.name ) { \ 448 mergeConstraint(year, > 0);
449 mergeConstraint(month, > 0);
450 mergeConstraint(day, != 0);
451 mergeConstraint(hour, >= 0);
452 mergeConstraint(minute, >= 0);
453 mergeConstraint(second, >= 0);
455 mergeConstraint(weekday, != 0);
456 mergeConstraint(weekdaynr, != 0);
457 mergeConstraint(weeknumber, != 0);
458 mergeConstraint(yearday, != 0);
460 #undef mergeConstraint 484 if (!isConsistent(type)) {
489 QTime tm(hour, minute, second);
492 if (day && month > 0) {
493 appendDateTime(DateHelper::getDate(year, month, day), tm, result);
497 if (!done && weekday == 0 && weeknumber == 0 && yearday == 0) {
499 uint mstart = (month > 0) ? month : 1;
500 uint mend = (month <= 0) ? 12 : month;
501 for (uint m = mstart; m <= mend; ++m) {
505 }
else if (day < 0) {
506 QDate date(year, month, 1);
509 QDate date(year, month, 1);
515 appendDateTime(dt, tm, result);
526 if (!done && yearday != 0) {
528 QDate d(year + ((yearday > 0) ? 0 : 1), 1, 1);
529 d = d.addDays(yearday - ((yearday > 0) ? 1 : 0));
530 appendDateTime(d, tm, result);
535 if (!done && weeknumber != 0) {
536 QDate wst(DateHelper::getNthWeek(year, weeknumber, weekstart));
538 wst = wst.addDays((7 + weekday - weekstart) % 7);
539 appendDateTime(wst, tm, result);
541 for (
int i = 0; i < 7; ++i) {
542 appendDateTime(wst, tm, result);
543 wst = wst.addDays(1);
550 if (!done && weekday != 0) {
551 QDate dt(year, 1, 1);
555 bool inMonth = (type == RecurrenceRule::rMonthly) || (type == RecurrenceRule::rYearly && month > 0);
556 if (inMonth && month > 0) {
557 dt =
QDate(year, month, 1);
568 int adj = (7 + weekday - dt.dayOfWeek()) % 7;
572 dt = dt.
addDays((weekdaynr - 1) * 7);
573 appendDateTime(dt, tm, result);
574 }
else if (weekdaynr < 0) {
575 dt = dt.
addDays(weekdaynr * 7);
576 appendDateTime(dt, tm, result);
579 for (
int i = 0; i < maxloop; ++i) {
580 appendDateTime(dt, tm, result);
588 for (
int i = 0, iend = result.
count(); i < iend; ++i) {
589 if (matches(result[i], type)) {
609 intervalDateTime(type);
613 case RecurrenceRule::rSecondly:
614 cachedDt = cachedDt.addSecs(freq);
616 case RecurrenceRule::rMinutely:
617 cachedDt = cachedDt.addSecs(60 * freq);
619 case RecurrenceRule::rHourly:
620 cachedDt = cachedDt.addSecs(3600 * freq);
622 case RecurrenceRule::rDaily:
623 cachedDt = cachedDt.addDays(freq);
625 case RecurrenceRule::rWeekly:
626 cachedDt = cachedDt.addDays(7 * freq);
628 case RecurrenceRule::rMonthly:
629 cachedDt = cachedDt.addMonths(freq);
631 case RecurrenceRule::rYearly:
632 cachedDt = cachedDt.addYears(freq);
638 readDateTime(cachedDt, type);
649 case RecurrenceRule::rSecondly:
652 case RecurrenceRule::rMinutely:
655 case RecurrenceRule::rHourly:
658 case RecurrenceRule::rDaily:
661 case RecurrenceRule::rMonthly:
664 case RecurrenceRule::rYearly:
667 case RecurrenceRule::rWeekly:
669 weeknumber = DateHelper::getWeekNumber(dt.
date(), weekstart, &year);
701 Private &
operator=(
const Private &other);
705 void buildConstraints();
706 bool buildCache()
const;
707 Constraint getNextValidDateInterval(
const QDateTime &preDate, PeriodType type)
const;
708 Constraint getPreviousValidDateInterval(
const QDateTime &afterDate, PeriodType type)
const;
709 QList<QDateTime> datesForInterval(
const Constraint &interval, PeriodType type)
const;
736 Constraint::List mConstraints;
743 mutable bool mCached;
748 uint mTimedRepetition;
751 RecurrenceRule::Private::Private(
RecurrenceRule *parent,
const Private &p)
755 , mDateStart(p.mDateStart)
756 , mFrequency(p.mFrequency)
757 , mDuration(p.mDuration)
758 , mDateEnd(p.mDateEnd)
761 mBySeconds(p.mBySeconds)
762 , mByMinutes(p.mByMinutes)
763 , mByHours(p.mByHours)
765 , mByMonthDays(p.mByMonthDays)
766 , mByYearDays(p.mByYearDays)
767 , mByWeekNumbers(p.mByWeekNumbers)
768 , mByMonths(p.mByMonths)
769 , mBySetPos(p.mBySetPos)
770 , mWeekStart(p.mWeekStart)
773 mIsReadOnly(p.mIsReadOnly)
775 , mNoByRules(p.mNoByRules)
780 RecurrenceRule::Private &RecurrenceRule::Private::operator=(
const Private &p)
789 mDateStart = p.mDateStart;
790 mFrequency = p.mFrequency;
791 mDuration = p.mDuration;
792 mDateEnd = p.mDateEnd;
794 mBySeconds = p.mBySeconds;
795 mByMinutes = p.mByMinutes;
796 mByHours = p.mByHours;
798 mByMonthDays = p.mByMonthDays;
799 mByYearDays = p.mByYearDays;
800 mByWeekNumbers = p.mByWeekNumbers;
801 mByMonths = p.mByMonths;
802 mBySetPos = p.mBySetPos;
803 mWeekStart = p.mWeekStart;
805 mIsReadOnly = p.mIsReadOnly;
807 mNoByRules = p.mNoByRules;
814 bool RecurrenceRule::Private::operator==(
const Private &r)
const 816 return mPeriod == r.mPeriod && ((mDateStart == r.mDateStart) || (!mDateStart.isValid() && !r.mDateStart.isValid())) && mDuration == r.mDuration
817 && ((mDateEnd == r.mDateEnd) || (!mDateEnd.isValid() && !r.mDateEnd.isValid())) && mFrequency == r.mFrequency && mIsReadOnly == r.mIsReadOnly
818 && mAllDay == r.mAllDay && mBySeconds == r.mBySeconds && mByMinutes == r.mByMinutes && mByHours == r.mByHours && mByDays == r.mByDays
819 && mByMonthDays == r.mByMonthDays && mByYearDays == r.mByYearDays && mByWeekNumbers == r.mByWeekNumbers && mByMonths == r.mByMonths
820 && mBySetPos == r.mBySetPos && mWeekStart == r.mWeekStart && mNoByRules == r.mNoByRules;
823 void RecurrenceRule::Private::clear()
833 mByMonthDays.clear();
835 mByWeekNumbers.clear();
844 void RecurrenceRule::Private::setDirty()
848 mCachedDates.clear();
849 for (
int i = 0, iend = mObservers.count(); i < iend; ++i) {
851 mObservers[i]->recurrenceChanged(mParent);
862 : d(new Private(this))
867 : d(
new Private(
this, *r.d))
871 RecurrenceRule::~RecurrenceRule()
895 if (!d->mObservers.contains(observer)) {
896 d->mObservers.append(observer);
902 d->mObservers.removeAll(observer);
905 void RecurrenceRule::setRecurrenceType(
PeriodType period)
919 if (d->mPeriod == rNone) {
922 if (d->mDuration < 0) {
925 if (d->mDuration == 0) {
935 if (!d->buildCache()) {
942 return d->mCachedDateEnd;
950 d->mDateEnd = dateTime;
951 if (d->mDateEnd.isValid()) {
980 void RecurrenceRule::setDirty()
990 d->mDateStart = start;
999 d->mFrequency = freq;
1003 void RecurrenceRule::setBySeconds(
const QList<int> &bySeconds)
1008 d->mBySeconds = bySeconds;
1012 void RecurrenceRule::setByMinutes(
const QList<int> &byMinutes)
1017 d->mByMinutes = byMinutes;
1021 void RecurrenceRule::setByHours(
const QList<int> &byHours)
1026 d->mByHours = byHours;
1035 d->mByDays = byDays;
1039 void RecurrenceRule::setByMonthDays(
const QList<int> &byMonthDays)
1044 d->mByMonthDays = byMonthDays;
1048 void RecurrenceRule::setByYearDays(
const QList<int> &byYearDays)
1053 d->mByYearDays = byYearDays;
1057 void RecurrenceRule::setByWeekNumbers(
const QList<int> &byWeekNumbers)
1062 d->mByWeekNumbers = byWeekNumbers;
1066 void RecurrenceRule::setByMonths(
const QList<int> &byMonths)
1071 d->mByMonths = byMonths;
1075 void RecurrenceRule::setBySetPos(
const QList<int> &bySetPos)
1080 d->mBySetPos = bySetPos;
1084 void RecurrenceRule::setWeekStart(
short weekStart)
1089 d->mWeekStart = weekStart;
1095 d->mDateStart = d->mDateStart.toTimeZone(oldTz);
1096 d->mDateStart.setTimeZone(newTz);
1097 if (d->mDuration == 0) {
1098 d->mDateEnd = d->mDateEnd.toTimeZone(oldTz);
1099 d->mDateEnd.setTimeZone(newTz);
1161 void RecurrenceRule::Private::buildConstraints()
1163 mTimedRepetition = 0;
1164 mNoByRules = mBySetPos.isEmpty();
1165 mConstraints.clear();
1166 Constraint con(mDateStart.timeZone());
1167 if (mWeekStart > 0) {
1168 con.setWeekstart(mWeekStart);
1170 mConstraints.append(con);
1174 Constraint::List tmp;
1177 #define intConstraint( list, setElement ) \ 1178 if ( !list.isEmpty() ) { \ 1179 mNoByRules = false; \ 1180 iend = list.count(); \ 1181 if ( iend == 1 ) { \ 1182 for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \ 1183 mConstraints[c].setElement( list[0] ); \ 1186 tmp.reserve(mConstraints.count() * iend); \ 1187 for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \ 1188 for ( i = 0; i < iend; ++i ) { \ 1189 con = mConstraints[c]; \ 1190 con.setElement( list[i] ); \ 1191 tmp.append( con ); \ 1194 mConstraints = tmp; \ 1200 intConstraint(mBySeconds, setSecond);
1201 intConstraint(mByMinutes, setMinute);
1202 intConstraint(mByHours, setHour);
1203 intConstraint(mByMonthDays, setDay);
1204 intConstraint(mByMonths, setMonth);
1205 intConstraint(mByYearDays, setYearday);
1206 intConstraint(mByWeekNumbers, setWeeknumber);
1207 #undef intConstraint 1209 if (!mByDays.isEmpty()) {
1211 tmp.reserve(mConstraints.count() * mByDays.count());
1212 for (c = 0, cend = mConstraints.count(); c < cend; ++c) {
1213 for (i = 0, iend = mByDays.count(); i < iend; ++i) {
1214 con = mConstraints[c];
1215 con.setWeekday(mByDays[i].day());
1216 con.setWeekdaynr(mByDays[i].pos());
1225 #define fixConstraint( setElement, value ) \ 1227 for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \ 1228 mConstraints[c].setElement( value ); \ 1236 if (mPeriod == rWeekly && mByDays.isEmpty()) {
1237 fixConstraint(setWeekday, mDateStart.date().dayOfWeek());
1244 if (mByDays.isEmpty() && mByWeekNumbers.isEmpty() && mByYearDays.isEmpty() && mByMonths.isEmpty()) {
1245 fixConstraint(setMonth, mDateStart.date().month());
1249 if (mByDays.isEmpty() && mByWeekNumbers.isEmpty() && mByYearDays.isEmpty() && mByMonthDays.isEmpty()) {
1250 fixConstraint(setDay, mDateStart.date().day());
1255 if (mByHours.isEmpty()) {
1256 fixConstraint(setHour, mDateStart.time().hour());
1260 if (mByMinutes.isEmpty()) {
1261 fixConstraint(setMinute, mDateStart.time().minute());
1265 if (mBySeconds.isEmpty()) {
1266 fixConstraint(setSecond, mDateStart.time().second());
1273 #undef fixConstraint 1278 mTimedRepetition = mFrequency * 3600;
1281 mTimedRepetition = mFrequency * 60;
1284 mTimedRepetition = mFrequency;
1290 for (c = 0, cend = mConstraints.count(); c < cend;) {
1291 if (mConstraints[c].isConsistent(mPeriod)) {
1294 mConstraints.removeAt(c);
1303 bool RecurrenceRule::Private::buildCache()
const 1305 Q_ASSERT(mDuration > 0);
1308 Constraint interval(getNextValidDateInterval(mDateStart, mPeriod));
1310 auto dts = datesForInterval(interval, mPeriod);
1313 const auto it = strictLowerBound(dts.begin(), dts.end(), mDateStart);
1314 if (it != dts.end()) {
1315 dts.erase(dts.begin(), it + 1);
1320 for (
int loopnr = 0; loopnr < LOOP_LIMIT && dts.count() < mDuration; ++loopnr) {
1321 interval.increase(mPeriod, mFrequency);
1323 dts += datesForInterval(interval, mPeriod);
1325 if (dts.count() > mDuration) {
1327 dts.erase(dts.begin() + mDuration, dts.end());
1337 if (
int(dts.count()) == mDuration) {
1338 mCachedDateEnd = dts.last();
1343 mCachedLastDate = interval.intervalDateTime(mPeriod);
1352 for (
int i = 0, iend = d->mConstraints.count(); i < iend; ++i) {
1353 if (d->mConstraints[i].matches(dt, recurrenceType())) {
1364 if (!qd.
isValid() || !d->mDateStart.isValid()) {
1372 if (qd < d->mDateStart.date()) {
1377 if (d->mDuration >= 0) {
1387 for (i = 0, iend = d->mConstraints.count(); i < iend && !match; ++i) {
1388 match = d->mConstraints[i].matches(qd, recurrenceType());
1395 Constraint interval(d->getNextValidDateInterval(start, recurrenceType()));
1398 if (!interval.matches(qd, recurrenceType())) {
1406 auto dts = d->datesForInterval(interval, recurrenceType());
1407 for (i = 0, iend = dts.count(); i < iend; ++i) {
1408 if (dts[i].date() >= qd) {
1409 return dts[i].
date() == qd;
1412 interval.increase(recurrenceType(),
frequency());
1413 }
while (interval.intervalDateTime(recurrenceType()) < end);
1420 start = start.
toTimeZone(d->mDateStart.timeZone());
1421 if (end < d->mDateStart) {
1424 if (start < d->mDateStart) {
1425 start = d->mDateStart;
1429 if (d->mDuration >= 0) {
1432 if (start > endRecur) {
1435 if (end > endRecur) {
1441 if (d->mTimedRepetition) {
1443 int n =
static_cast<int>((d->mDateStart.secsTo(start) - 1) % d->mTimedRepetition);
1444 return start.
addSecs(d->mTimedRepetition - n) < end;
1450 int dayCount = startDay.
daysTo(endDay) + 1;
1455 for (i = 0, iend = d->mConstraints.count(); i < iend && !match; ++i) {
1456 match = d->mConstraints[i].matches(startDay, recurrenceType());
1457 for (
int day = 1; day < dayCount && !match; ++day) {
1458 match = d->mConstraints[i].matches(startDay.
addDays(day), recurrenceType());
1465 Constraint interval(d->getNextValidDateInterval(start, recurrenceType()));
1468 Constraint intervalm = interval;
1470 match = intervalm.matches(startDay, recurrenceType());
1471 for (
int day = 1; day < dayCount && !match; ++day) {
1472 match = intervalm.matches(startDay.
addDays(day), recurrenceType());
1477 intervalm.increase(recurrenceType(),
frequency());
1478 }
while (intervalm.intervalDateTime(recurrenceType()).isValid() && intervalm.intervalDateTime(recurrenceType()) < end);
1487 auto dts = d->datesForInterval(interval, recurrenceType());
1488 const auto it = std::lower_bound(dts.constBegin(), dts.constEnd(), start);
1489 if (it != dts.constEnd()) {
1492 interval.increase(recurrenceType(),
frequency());
1493 }
while (interval.intervalDateTime(recurrenceType()).isValid() && interval.intervalDateTime(recurrenceType()) < end);
1506 if (dt < d->mDateStart) {
1510 if (d->mDuration >= 0 && dt >
endDt()) {
1514 if (d->mTimedRepetition) {
1516 return !(d->mDateStart.secsTo(dt) % d->mTimedRepetition);
1526 Constraint interval(d->getNextValidDateInterval(dt, recurrenceType()));
1528 if (interval.matches(dt, recurrenceType())) {
1543 for (
int i = 0, iend = dts.count(); i < iend; ++i) {
1544 lst += dts[i].toTimeZone(timeZone).time();
1556 if (toDate < d->mDateStart) {
1560 if (d->mDuration > 0 && toDate >=
endDt()) {
1561 return d->mDuration;
1564 if (d->mTimedRepetition) {
1566 return static_cast<int>(d->mDateStart.secsTo(toDate) / d->mTimedRepetition);
1583 if (!toDate.
isValid() || toDate < d->mDateStart) {
1587 if (d->mTimedRepetition) {
1590 if (d->mDuration >= 0 &&
endDt().isValid() && toDate >
endDt()) {
1593 int n =
static_cast<int>((d->mDateStart.secsTo(prev) - 1) % d->mTimedRepetition);
1598 return prev >= d->mDateStart ? prev :
QDateTime();
1602 if (d->mDuration > 0) {
1606 const auto it = strictLowerBound(d->mCachedDates.constBegin(), d->mCachedDates.constEnd(), toDate);
1607 if (it != d->mCachedDates.constEnd()) {
1614 if (d->mDuration >= 0 &&
endDt().isValid() && toDate >
endDt()) {
1618 Constraint interval(d->getPreviousValidDateInterval(prev, recurrenceType()));
1619 const auto dts = d->datesForInterval(interval, recurrenceType());
1620 const auto it = strictLowerBound(dts.begin(), dts.end(), prev);
1621 if (it != dts.end()) {
1622 return ((*it) >= d->mDateStart) ? (*it) :
QDateTime();
1626 while (interval.intervalDateTime(recurrenceType()) > d->mDateStart) {
1627 interval.increase(recurrenceType(), -
int(
frequency()));
1629 auto dts = d->datesForInterval(interval, recurrenceType());
1631 if (!dts.isEmpty()) {
1633 if (prev.
isValid() && prev >= d->mDateStart) {
1648 if (d->mDuration >= 0 &&
endDt().isValid() && fromDate >=
endDt()) {
1653 if (fromDate < d->mDateStart) {
1654 fromDate = d->mDateStart.
addSecs(-1);
1657 if (d->mTimedRepetition) {
1659 int n =
static_cast<int>((d->mDateStart.secsTo(fromDate) + 1) % d->mTimedRepetition);
1664 if (d->mDuration > 0) {
1668 const auto it = std::upper_bound(d->mCachedDates.constBegin(), d->mCachedDates.constEnd(), fromDate);
1669 if (it != d->mCachedDates.constEnd()) {
1675 Constraint interval(d->getNextValidDateInterval(fromDate, recurrenceType()));
1676 const auto dts = d->datesForInterval(interval, recurrenceType());
1677 const auto it = std::upper_bound(dts.begin(), dts.end(), fromDate);
1678 if (it != dts.end()) {
1679 return (d->mDuration < 0 || *it <= end) ? *it :
QDateTime();
1681 interval.increase(recurrenceType(),
frequency());
1682 if (d->mDuration >= 0 && interval.intervalDateTime(recurrenceType()) > end) {
1691 auto dts = d->datesForInterval(interval, recurrenceType());
1692 if (!dts.isEmpty()) {
1694 if (d->mDuration >= 0 && ret > end) {
1700 interval.increase(recurrenceType(),
frequency());
1701 }
while (++loop < LOOP_LIMIT && (d->mDuration < 0 || interval.intervalDateTime(recurrenceType()) < end));
1710 if (end < d->mDateStart) {
1714 if (d->mDuration >= 0) {
1717 if (start > endRecur) {
1720 if (end >= endRecur) {
1726 if (d->mTimedRepetition) {
1730 qint64 offsetFromNextOccurrence;
1731 if (d->mDateStart < start) {
1732 offsetFromNextOccurrence = d->mTimedRepetition - (d->mDateStart.secsTo(start) % d->mTimedRepetition);
1734 offsetFromNextOccurrence = -(d->mDateStart.secsTo(start) % d->mTimedRepetition);
1738 int numberOfOccurrencesWithinInterval =
static_cast<int>(dt.
secsTo(enddt) / d->mTimedRepetition) + 1;
1740 numberOfOccurrencesWithinInterval = qMin(numberOfOccurrencesWithinInterval, LOOP_LIMIT);
1741 for (
int i = 0; i < numberOfOccurrencesWithinInterval; dt = dt.
addSecs(d->mTimedRepetition), ++i) {
1748 QDateTime st = start < d->mDateStart ? d->mDateStart : start;
1750 if (d->mDuration > 0) {
1754 if (d->mCachedDateEnd.isValid() && start > d->mCachedDateEnd) {
1757 const auto it = std::lower_bound(d->mCachedDates.constBegin(), d->mCachedDates.constEnd(), start);
1758 if (it != d->mCachedDates.constEnd()) {
1759 const auto itEnd = std::upper_bound(it, d->mCachedDates.constEnd(), enddt);
1760 if (itEnd != d->mCachedDates.constEnd()) {
1763 std::copy(it, itEnd, std::back_inserter(result));
1765 if (d->mCachedDateEnd.isValid()) {
1767 }
else if (!result.
isEmpty()) {
1775 st = d->mCachedLastDate.
addSecs(1);
1778 Constraint interval(d->getNextValidDateInterval(st, recurrenceType()));
1781 auto dts = d->datesForInterval(interval, recurrenceType());
1782 auto it = dts.begin();
1783 auto itEnd = dts.end();
1785 it = std::lower_bound(dts.begin(), dts.end(), st);
1787 itEnd = std::upper_bound(it, dts.end(), enddt);
1788 if (itEnd != dts.end()) {
1791 std::copy(it, itEnd, std::back_inserter(result));
1793 interval.increase(recurrenceType(),
frequency());
1794 }
while (++loop < LOOP_LIMIT && interval.intervalDateTime(recurrenceType()) < end);
1803 Constraint RecurrenceRule::Private::getPreviousValidDateInterval(
const QDateTime &dt,
PeriodType type)
const 1824 periods =
static_cast<int>(start.
secsTo(toDate) / modifier);
1826 if (mFrequency > 0) {
1827 periods = (periods / mFrequency) * mFrequency;
1829 nextValid = start.
addSecs(modifier * periods);
1837 periods = start.
daysTo(toDate) / modifier;
1839 if (mFrequency > 0) {
1840 periods = (periods / mFrequency) * mFrequency;
1842 nextValid = start.
addDays(modifier * periods);
1847 if (mFrequency > 0) {
1848 periods = (periods / mFrequency) * mFrequency;
1859 if (mFrequency > 0) {
1860 periods = (periods / mFrequency) * mFrequency;
1868 return Constraint(nextValid, type, mWeekStart);
1875 Constraint RecurrenceRule::Private::getNextValidDateInterval(
const QDateTime &dt,
PeriodType type)
const 1897 periods =
static_cast<int>(start.
secsTo(toDate) / modifier);
1898 periods = qMax(0L, periods);
1899 if (periods > 0 && mFrequency > 0) {
1900 periods += (mFrequency - 1 - ((periods - 1) % mFrequency));
1902 nextValid = start.
addSecs(modifier * periods);
1911 periods = start.
daysTo(toDate) / modifier;
1912 periods = qMax(0L, periods);
1913 if (periods > 0 && mFrequency > 0) {
1914 periods += (mFrequency - 1 - ((periods - 1) % mFrequency));
1916 nextValid = start.
addDays(modifier * periods);
1920 periods = qMax(0L, periods);
1921 if (periods > 0 && mFrequency > 0) {
1922 periods += (mFrequency - 1 - ((periods - 1) % mFrequency));
1932 periods = qMax(0L, periods);
1933 if (periods > 0 && mFrequency > 0) {
1934 periods += (mFrequency - 1 - ((periods - 1) % mFrequency));
1942 return Constraint(nextValid, type, mWeekStart);
1954 for (
int i = 0, iend = mConstraints.count(); i < iend; ++i) {
1955 Constraint merged(interval);
1956 if (merged.merge(mConstraints[i])) {
1958 if (merged.year > 0 && merged.hour >= 0 && merged.minute >= 0 && merged.second >= 0) {
1967 sortAndRemoveDuplicates(lst);
1978 if (!mBySetPos.isEmpty()) {
1981 for (
int i = 0, iend = mBySetPos.count(); i < iend; ++i) {
1982 int pos = mBySetPos[i];
1987 pos += tmplst.count();
1989 if (pos >= 0 && pos < tmplst.count()) {
1993 sortAndRemoveDuplicates(lst);
2003 if (!d->mRRule.isEmpty()) {
2004 qCDebug(KCALCORE_LOG) <<
" RRULE=" << d->mRRule;
2006 qCDebug(KCALCORE_LOG) <<
" Read-Only:" <<
isReadOnly();
2008 qCDebug(KCALCORE_LOG) <<
" Period type:" << int(recurrenceType()) <<
", frequency:" <<
frequency();
2009 qCDebug(KCALCORE_LOG) <<
" #occurrences:" <<
duration();
2010 qCDebug(KCALCORE_LOG) <<
" start date:" << dumpTime(
startDt(),
allDay()) <<
", end date:" << dumpTime(
endDt(),
allDay());
2012 #define dumpByIntList(list,label) \ 2013 if ( !list.isEmpty() ) {\ 2015 for ( int i = 0, iend = list.count(); i < iend; ++i ) {\ 2016 lst.append( QString::number( list[i] ) );\ 2018 qCDebug(KCALCORE_LOG) << " " << label << lst.join(QLatin1String(", ") );\ 2021 dumpByIntList(d->mBySeconds, QStringLiteral(
"BySeconds: "));
2022 dumpByIntList(d->mByMinutes, QStringLiteral(
"ByMinutes: "));
2023 dumpByIntList(d->mByHours, QStringLiteral(
"ByHours: "));
2024 if (!d->mByDays.isEmpty()) {
2026 for (
int i = 0, iend = d->mByDays.count(); i < iend; ++i) {
2031 dumpByIntList(d->mByMonthDays, QStringLiteral(
"ByMonthDays:"));
2032 dumpByIntList(d->mByYearDays, QStringLiteral(
"ByYearDays: "));
2033 dumpByIntList(d->mByWeekNumbers, QStringLiteral(
"ByWeekNr: "));
2034 dumpByIntList(d->mByMonths, QStringLiteral(
"ByMonths: "));
2035 dumpByIntList(d->mBySetPos, QStringLiteral(
"BySetPos: "));
2036 #undef dumpByIntList 2038 qCDebug(KCALCORE_LOG) <<
" Week start:" << DateHelper::dayName(d->mWeekStart);
2040 qCDebug(KCALCORE_LOG) <<
" Constraints:";
2042 for (
int i = 0, iend = d->mConstraints.count(); i < iend; ++i) {
2043 d->mConstraints[i].dump();
2049 void Constraint::dump()
const 2051 qCDebug(KCALCORE_LOG) <<
" ~> Y=" << year <<
", M=" << month <<
", D=" << day <<
", H=" << hour <<
", m=" << minute <<
", S=" << second
2052 <<
", wd=" << weekday <<
",#wd=" << weekdaynr <<
", #w=" << weeknumber <<
", yd=" << yearday;
2064 result = dt.
toString(QStringLiteral(
"ddd yyyy-MM-dd t"));
2066 result = dt.
toString(QStringLiteral(
"ddd yyyy-MM-dd hh:mm:ss t"));
2078 return d->mDateStart;
2088 return d->mFrequency;
2093 return d->mDuration;
2096 QString RecurrenceRule::rrule()
const 2108 return d->mIsReadOnly;
2113 d->mIsReadOnly = readOnly;
2118 return d->mPeriod != rNone;
2126 const QList<int> &RecurrenceRule::bySeconds()
const 2128 return d->mBySeconds;
2131 const QList<int> &RecurrenceRule::byMinutes()
const 2133 return d->mByMinutes;
2136 const QList<int> &RecurrenceRule::byHours()
const 2146 const QList<int> &RecurrenceRule::byMonthDays()
const 2148 return d->mByMonthDays;
2151 const QList<int> &RecurrenceRule::byYearDays()
const 2153 return d->mByYearDays;
2156 const QList<int> &RecurrenceRule::byWeekNumbers()
const 2158 return d->mByWeekNumbers;
2161 const QList<int> &RecurrenceRule::byMonths()
const 2163 return d->mByMonths;
2166 const QList<int> &RecurrenceRule::bySetPos()
const 2168 return d->mBySetPos;
2171 short RecurrenceRule::weekStart()
const 2173 return d->mWeekStart;
2176 RecurrenceRule::RuleObserver::~RuleObserver()
2180 RecurrenceRule::WDayPos::WDayPos(
int ps,
short dy)
2186 void RecurrenceRule::WDayPos::setDay(
short dy)
2191 short RecurrenceRule::WDayPos::day()
const 2196 void RecurrenceRule::WDayPos::setPos(
int ps)
2201 int RecurrenceRule::WDayPos::pos()
const 2208 out << c.year << c.month << c.day << c.hour << c.minute << c.second << c.weekday << c.weekdaynr << c.weeknumber << c.yearday << c.weekstart;
2209 serializeQTimeZoneAsSpec(out, c.timeZone);
2217 bool secondOccurrence;
2218 in >> c.year >> c.month >> c.day >> c.hour >> c.minute >> c.second >> c.weekday >> c.weekdaynr >> c.weeknumber >> c.yearday >> c.weekstart;
2219 deserializeSpecAsQTimeZone(in, c.timeZone);
2220 in >> secondOccurrence;
2226 out << w.mDay << w.mPos;
2232 in >> w.mDay >> w.mPos;
2242 RecurrenceRule::Private *d = r->d;
2243 out << d->mRRule <<
static_cast<quint32
>(d->mPeriod);
2244 serializeQDateTimeAsKDateTime(out, d->mDateStart);
2245 out << d->mFrequency << d->mDuration;
2246 serializeQDateTimeAsKDateTime(out, d->mDateEnd);
2247 out << d->mBySeconds << d->mByMinutes << d->mByHours << d->mByDays << d->mByMonthDays << d->mByYearDays << d->mByWeekNumbers << d->mByMonths << d->mBySetPos
2248 << d->mWeekStart << d->mConstraints << d->mAllDay << d->mNoByRules << d->mTimedRepetition << d->mIsReadOnly;
2259 RecurrenceRule::Private *d = r->d;
2261 in >> d->mRRule >> period;
2262 deserializeKDateTimeAsQDateTime(in, d->mDateStart);
2263 in >> d->mFrequency >> d->mDuration;
2264 deserializeKDateTimeAsQDateTime(in, d->mDateEnd);
2265 in >> d->mBySeconds >> d->mByMinutes >> d->mByHours >> d->mByDays >> d->mByMonthDays >> d->mByYearDays >> d->mByWeekNumbers >> d->mByMonths >> d->mBySetPos
2266 >> d->mWeekStart >> d->mConstraints >> d->mAllDay >> d->mNoByRules >> d->mTimedRepetition >> d->mIsReadOnly;
qint64 daysTo(const QDate &d) const const
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
Alarm serializer.
QString toString(Qt::DateFormat format) const const
QDateTime addMonths(int nmonths) const const
QDate addMonths(int nmonths) const const
bool dateMatchesRules(const QDateTime &dt) const
Returns true if the date matches the rules.
bool isReadOnly() const
Returns true if the recurrence is read-only; false if it can be changed.
RecurrenceRule()
/************************************************************************** RecurrenceRule * ...
QDateTime getPreviousDate(const QDateTime &afterDateTime) const
Returns the date and time of the last previous recurrence, before the specified date/time.
QDateTime addYears(int nyears) const const
bool recurs() const
Returns the event's recurrence status.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last...
QDateTime startDt() const
Returns the recurrence start date/time.
void setStartDt(const QDateTime &start)
Sets the recurrence start date/time.
qint64 daysTo(const QDateTime &other) const const
void addObserver(RuleObserver *observer)
Installs an observer.
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
Alarm deserializer.
int daysInYear() const const
QDateTime endDt(bool *result=nullptr) const
Returns the date and time of the last recurrence.
void removeObserver(RuleObserver *observer)
Removes an observer that was added with addObserver.
QString join(const QString &separator) const const
void clear()
Turns off recurrence for the event.
uint frequency() const
Returns the recurrence frequency, in terms of the recurrence time period type.
QDateTime toTimeZone(const QTimeZone &timeZone) const const
bool operator==(const Qt3DRender::QGraphicsApiFilter &reference, const Qt3DRender::QGraphicsApiFilter &sample)
structure for describing the n-th weekday of the month/year.
bool recursAt(const QDateTime &dt) const
Returns true if the date/time specified is one at which the event will recur.
bool recursOn(const QDate &date, const QTimeZone &timeZone) const
Returns true if the date specified is one on which the event will recur.
void setFrequency(int freq)
Sets the recurrence frequency, in terms of the recurrence time period type.
TimeList recurTimesOn(const QDate &date, const QTimeZone &timeZone) const
Returns a list of the times on the specified date at which the recurrence will occur.
int dayOfWeek() const const
QString number(int n, int base)
int count(const T &value) const const
void setAllDay(bool allDay)
Sets whether the dtstart is all-day (i.e.
void append(const T &value)
void clear()
Removes all recurrence and exception rules and dates.
Recurrence & operator=(const Recurrence &r)
Assignment operator.
bool isEmpty() const const
void shiftTimes(const QTimeZone &oldTz, const QTimeZone &newTz)
Shift the times of the rule so that they appear at the same clock time as before but in a new time zo...
PeriodType
enum for describing the frequency how an event recurs, if at all.
bool isValid() const const
int daysInMonth() const const
void setDate(const QDate &date)
friend KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::RecurrenceRule *)
RecurrenceRule serializer and deserializer.
QDate addYears(int nyears) const const
bool operator==(const Recurrence &r) const
Comparison operator for equality.
void setReadOnly(bool readOnly)
Set if recurrence is read-only or can be changed.
bool isValid() const const
bool setDate(int year, int month, int day)
qint64 secsTo(const QDateTime &other) const const
void setRRule(const QString &rrule)
Set the RRULE string for the rule.
int durationTo(const QDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
int dayOfYear() const const
void setEndDt(const QDateTime &endDateTime)
Sets the date and time of the last recurrence.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
QDate addDays(qint64 ndays) const const
QDateTime getNextDate(const QDateTime &preDateTime) const
Returns the date and time of the next recurrence, after the specified date/time.
QList< QDateTime > timesInInterval(const QDateTime &start, const QDateTime &end) const
Returns a list of all the times at which the recurrence will occur between two specified times...
This class represents a recurrence rule for a calendar incidence.
QDateTime addSecs(qint64 s) const const
void dump() const
Debug output.
QDateTime addDays(qint64 ndays) const const
QVariant merge(const QVariant &lhs, const QVariant &rhs)
bool allDay() const
Returns whether the start date has no time associated.
Namespace for all KCalendarCore types.
QTimeZone timeZone() const const