22 #include "recurrencerule.h"
26 #include <QtCore/QStringList>
27 #include <QtCore/QTime>
29 using namespace KCalCore;
32 const int LOOP_LIMIT = 10000;
34 static QString dumpTime(
const KDateTime &dt);
56 static QString dayName(
short day);
58 static QDate getNthWeek(
int year,
int weeknumber,
short weekstart = 1);
59 static int weekNumbersInYear(
int year,
short weekstart = 1);
60 static int getWeekNumber(
const QDate &date,
short weekstart,
int *year = 0);
61 static int getWeekNumberNeg(
const QDate &date,
short weekstart,
int *year = 0);
64 static QDate getDate(
int year,
int month,
int day)
67 return QDate(year, month, day);
81 QString DateHelper::dayName(
short day)
104 QDate DateHelper::getNthWeek(
int year,
int weeknumber,
short weekstart)
106 if (weeknumber == 0) {
111 QDate dt(year, 1, 4);
112 int adjust = -(7 + dt.dayOfWeek() - weekstart) % 7;
113 if (weeknumber > 0) {
114 dt = dt.addDays(7 * (weeknumber-1) + adjust);
115 }
else if (weeknumber < 0) {
117 dt = dt.addDays(7 * weeknumber + adjust);
122 int DateHelper::getWeekNumber(
const QDate &date,
short weekstart,
int *year)
126 dt = dt.addDays(-(7 + dt.dayOfWeek() - weekstart) % 7);
128 int daysto = dt.daysTo(date);
133 dt = dt.addDays(-(7 + dt.dayOfWeek() - weekstart) % 7);
134 daysto = dt.daysTo(date);
135 }
else if (daysto > 355) {
137 QDate dtn(y+1, 1, 4);
138 dtn = dtn.addDays(-(7 + dtn.dayOfWeek() - weekstart) % 7);
139 int dayston = dtn.daysTo(date);
149 return daysto / 7 + 1;
152 int DateHelper::weekNumbersInYear(
int year,
short weekstart)
154 QDate dt(year, 1, weekstart);
155 QDate dt1(year + 1, 1, weekstart);
156 return dt.daysTo(dt1) / 7;
160 int DateHelper::getWeekNumberNeg(
const QDate &date,
short weekstart,
int *year)
162 int weekpos = getWeekNumber(date, weekstart, year);
163 return weekNumbersInYear(*year, weekstart) - weekpos - 1;
173 return mDay == pos2.mDay && mPos == pos2.mPos;
178 return !operator==(pos2);
191 explicit Constraint(KDateTime::Spec,
int wkst = 1);
214 void setMinute(
int n)
219 void setSecond(
int n)
224 void setWeekday(
int n)
229 void setWeekdaynr(
int n)
234 void setWeeknumber(
int n)
239 void setYearday(
int n)
244 void setWeekstart(
int n)
249 void setSecondOccurrence(
int n)
251 secondOccurrence = n;
266 KDateTime::Spec timespec;
267 bool secondOccurrence;
272 bool merge(
const Constraint &interval);
273 bool isConsistent()
const;
282 mutable bool useCachedDt;
283 mutable KDateTime cachedDt;
286 Constraint::Constraint(KDateTime::Spec spec,
int wkst)
295 timespec(dt.timeSpec())
298 readDateTime(dt, type);
301 void Constraint::clear()
313 secondOccurrence =
false;
322 if (weeknumber == 0) {
323 if (year > 0 && year != dt.
year()) {
328 if (weeknumber > 0 &&
329 weeknumber != DateHelper::getWeekNumber(dt, weekstart, &y)) {
332 if (weeknumber < 0 &&
333 weeknumber != DateHelper::getWeekNumberNeg(dt, weekstart, &y)) {
336 if (year > 0 && year != y) {
341 if (month > 0 && month != dt.
month()) {
344 if (day > 0 && day != dt.
day()) {
354 if (weekdaynr != 0) {
357 if ((type == RecurrenceRule::rMonthly) ||
358 (type == RecurrenceRule::rYearly && month > 0)) {
361 weekdaynr != (dt.
day() - 1) / 7 + 1) {
371 weekdaynr != (dt.
dayOfYear() - 1) / 7 + 1) {
381 if (yearday > 0 && yearday != dt.
dayOfYear()) {
395 if ((hour >= 0 && (hour != dt.time().hour() ||
396 secondOccurrence != dt.isSecondOccurrence())) ||
397 (minute >= 0 && minute != dt.time().minute()) ||
398 (second >= 0 && second != dt.time().second()) ||
399 !matches(dt.date(), type)) {
420 bool subdaily =
true;
422 case RecurrenceRule::rSecondly:
423 t.setHMS(hour, minute, second);
425 case RecurrenceRule::rMinutely:
426 t.setHMS(hour, minute, 0);
428 case RecurrenceRule::rHourly:
429 t.setHMS(hour, 0, 0);
431 case RecurrenceRule::rDaily:
433 case RecurrenceRule::rWeekly:
434 d = DateHelper::getNthWeek(year, weeknumber, weekstart);
437 case RecurrenceRule::rMonthly:
441 case RecurrenceRule::rYearly:
449 d = DateHelper::getDate(year, (month>0)?month:1, day?day:1);
451 cachedDt = KDateTime(d, t, timespec);
452 if (secondOccurrence) {
453 cachedDt.setSecondOccurrence(
true);
459 bool Constraint::merge(
const Constraint &interval)
461 #define mergeConstraint( name, cmparison ) \
462 if ( interval.name cmparison ) { \
463 if ( !( name cmparison ) ) { \
464 name = interval.name; \
465 } else if ( name != interval.name ) { \
472 mergeConstraint(year, > 0);
473 mergeConstraint(month, > 0);
474 mergeConstraint(day, != 0);
475 mergeConstraint(hour, >= 0);
476 mergeConstraint(minute, >= 0);
477 mergeConstraint(second, >= 0);
479 mergeConstraint(weekday, != 0);
480 mergeConstraint(weekdaynr, != 0);
481 mergeConstraint(weeknumber, != 0);
482 mergeConstraint(yearday, != 0);
484 #undef mergeConstraint
509 if (!isConsistent(type)) {
514 QTime tm(hour, minute, second);
516 if (!done && day && month > 0) {
517 appendDateTime(DateHelper::getDate(year, month, day), tm, result);
521 if (!done && weekday == 0 && weeknumber == 0 && yearday == 0) {
523 uint mstart = (month > 0) ? month : 1;
524 uint mend = (month <= 0) ? 12 : month;
525 for (uint m = mstart; m <= mend; ++m) {
529 }
else if (day < 0) {
530 QDate date(year, month, 1);
533 QDate date(year, month, 1);
539 appendDateTime(dt, tm, result);
550 if (!done && yearday != 0) {
552 QDate d(year + ((yearday > 0) ? 0 : 1), 1, 1);
553 d = d.addDays(yearday - ((yearday > 0) ? 1 : 0));
554 appendDateTime(d, tm, result);
559 if (!done && weeknumber != 0) {
560 QDate wst(DateHelper::getNthWeek(year, weeknumber, weekstart));
562 wst = wst.addDays((7 + weekday - weekstart) % 7);
563 appendDateTime(wst, tm, result);
565 for (
int i = 0; i < 7; ++i) {
566 appendDateTime(wst, tm, result);
567 wst = wst.addDays(1);
574 if (!done && weekday != 0) {
575 QDate dt(year, 1, 1);
579 bool inMonth = (type == RecurrenceRule::rMonthly) ||
580 (type == RecurrenceRule::rYearly && month > 0);
581 if (inMonth && month > 0) {
582 dt =
QDate(year, month, 1);
588 dt = dt.addMonths(1);
593 int adj = (7 + weekday - dt.dayOfWeek()) % 7;
594 dt = dt.addDays(adj);
597 dt = dt.addDays((weekdaynr - 1) * 7);
598 appendDateTime(dt, tm, result);
599 }
else if (weekdaynr < 0) {
600 dt = dt.addDays(weekdaynr * 7);
601 appendDateTime(dt, tm, result);
604 for (
int i = 0; i < maxloop; ++i) {
605 appendDateTime(dt, tm, result);
613 for (
int i = 0, iend = result.
count(); i < iend; ++i) {
614 if (matches(result[i], type)) {
623 void Constraint::appendDateTime(
const QDate &date,
const QTime &time,
626 KDateTime dt(date, time, timespec);
628 if (secondOccurrence) {
629 dt.setSecondOccurrence(
true);
638 intervalDateTime(type);
642 case RecurrenceRule::rSecondly:
643 cachedDt = cachedDt.addSecs(freq);
645 case RecurrenceRule::rMinutely:
646 cachedDt = cachedDt.addSecs(60 * freq);
648 case RecurrenceRule::rHourly:
649 cachedDt = cachedDt.addSecs(3600 * freq);
651 case RecurrenceRule::rDaily:
652 cachedDt = cachedDt.addDays(freq);
654 case RecurrenceRule::rWeekly:
655 cachedDt = cachedDt.addDays(7 * freq);
657 case RecurrenceRule::rMonthly:
658 cachedDt = cachedDt.addMonths(freq);
660 case RecurrenceRule::rYearly:
661 cachedDt = cachedDt.addYears(freq);
667 readDateTime(cachedDt, type);
678 case RecurrenceRule::rSecondly:
679 second = dt.time().second();
680 case RecurrenceRule::rMinutely:
681 minute = dt.time().minute();
682 case RecurrenceRule::rHourly:
683 hour = dt.time().hour();
684 secondOccurrence = dt.isSecondOccurrence();
685 case RecurrenceRule::rDaily:
686 day = dt.date().day();
687 case RecurrenceRule::rMonthly:
688 month = dt.date().month();
689 case RecurrenceRule::rYearly:
690 year = dt.date().year();
692 case RecurrenceRule::rWeekly:
694 weeknumber = DateHelper::getWeekNumber(dt.date(), weekstart, &year);
709 class KCalCore::RecurrenceRule::Private
726 Private &operator=(
const Private &other);
727 bool operator==(
const Private &other)
const;
730 void buildConstraints();
731 bool buildCache()
const;
732 Constraint getNextValidDateInterval(
const KDateTime &preDate,
PeriodType type)
const;
733 Constraint getPreviousValidDateInterval(
const KDateTime &afterDate,
PeriodType type)
const;
739 KDateTime mDateStart;
761 Constraint::List mConstraints;
766 mutable KDateTime mCachedDateEnd;
767 mutable KDateTime mCachedLastDate;
768 mutable bool mCached;
773 uint mTimedRepetition;
776 RecurrenceRule::Private::Private(
RecurrenceRule *parent,
const Private &p)
780 mDateStart(p.mDateStart),
781 mFrequency(p.mFrequency),
782 mDuration(p.mDuration),
783 mDateEnd(p.mDateEnd),
785 mBySeconds(p.mBySeconds),
786 mByMinutes(p.mByMinutes),
787 mByHours(p.mByHours),
789 mByMonthDays(p.mByMonthDays),
790 mByYearDays(p.mByYearDays),
791 mByWeekNumbers(p.mByWeekNumbers),
792 mByMonths(p.mByMonths),
793 mBySetPos(p.mBySetPos),
794 mWeekStart(p.mWeekStart),
796 mIsReadOnly(p.mIsReadOnly),
798 mNoByRules(p.mNoByRules)
803 RecurrenceRule::Private &RecurrenceRule::Private::operator=(
const Private &p)
812 mDateStart = p.mDateStart;
813 mFrequency = p.mFrequency;
814 mDuration = p.mDuration;
815 mDateEnd = p.mDateEnd;
817 mBySeconds = p.mBySeconds;
818 mByMinutes = p.mByMinutes;
819 mByHours = p.mByHours;
821 mByMonthDays = p.mByMonthDays;
822 mByYearDays = p.mByYearDays;
823 mByWeekNumbers = p.mByWeekNumbers;
824 mByMonths = p.mByMonths;
825 mBySetPos = p.mBySetPos;
826 mWeekStart = p.mWeekStart;
828 mIsReadOnly = p.mIsReadOnly;
830 mNoByRules = p.mNoByRules;
837 bool RecurrenceRule::Private::operator==(
const Private &r)
const
840 mPeriod == r.mPeriod &&
841 ((mDateStart == r.mDateStart) ||
842 (!mDateStart.isValid() && !r.mDateStart.isValid())) &&
843 mDuration == r.mDuration &&
844 ((mDateEnd == r.mDateEnd) ||
845 (!mDateEnd.isValid() && !r.mDateEnd.isValid())) &&
846 mFrequency == r.mFrequency &&
847 mIsReadOnly == r.mIsReadOnly &&
848 mAllDay == r.mAllDay &&
849 mBySeconds == r.mBySeconds &&
850 mByMinutes == r.mByMinutes &&
851 mByHours == r.mByHours &&
852 mByDays == r.mByDays &&
853 mByMonthDays == r.mByMonthDays &&
854 mByYearDays == r.mByYearDays &&
855 mByWeekNumbers == r.mByWeekNumbers &&
856 mByMonths == r.mByMonths &&
857 mBySetPos == r.mBySetPos &&
858 mWeekStart == r.mWeekStart &&
859 mNoByRules == r.mNoByRules;
862 void RecurrenceRule::Private::clear()
872 mByMonthDays.clear();
874 mByWeekNumbers.clear();
883 void RecurrenceRule::Private::setDirty()
887 mCachedDates.clear();
888 for (
int i = 0, iend = mObservers.count(); i < iend; ++i) {
890 mObservers[i]->recurrenceChanged(mParent);
901 : d(new Private(this))
906 : d(new Private(this, *r.d))
910 RecurrenceRule::~RecurrenceRule()
934 if (!d->mObservers.contains(observer)) {
935 d->mObservers.append(observer);
941 if (d->mObservers.contains(observer)) {
942 d->mObservers.removeAll(observer);
946 void 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;
1019 void RecurrenceRule::setDirty()
1029 d->mDateStart = start;
1038 d->mFrequency = freq;
1042 void RecurrenceRule::setBySeconds(
const QList<int> &bySeconds)
1047 d->mBySeconds = bySeconds;
1051 void RecurrenceRule::setByMinutes(
const QList<int> &byMinutes)
1056 d->mByMinutes = byMinutes;
1060 void RecurrenceRule::setByHours(
const QList<int> &byHours)
1065 d->mByHours = byHours;
1074 d->mByDays = byDays;
1078 void RecurrenceRule::setByMonthDays(
const QList<int> &byMonthDays)
1083 d->mByMonthDays = byMonthDays;
1087 void RecurrenceRule::setByYearDays(
const QList<int> &byYearDays)
1092 d->mByYearDays = byYearDays;
1096 void RecurrenceRule::setByWeekNumbers(
const QList<int> &byWeekNumbers)
1101 d->mByWeekNumbers = byWeekNumbers;
1105 void RecurrenceRule::setByMonths(
const QList<int> &byMonths)
1110 d->mByMonths = byMonths;
1114 void RecurrenceRule::setBySetPos(
const QList<int> &bySetPos)
1119 d->mBySetPos = bySetPos;
1123 void 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);
1138 d->mDateEnd.setTimeSpec(newSpec);
1200 void RecurrenceRule::Private::buildConstraints()
1202 mTimedRepetition = 0;
1203 mNoByRules = mBySetPos.isEmpty();
1204 mConstraints.clear();
1205 Constraint con(mDateStart.timeSpec());
1206 if (mWeekStart > 0) {
1207 con.setWeekstart(mWeekStart);
1209 mConstraints.append(con);
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; \
1236 intConstraint(mBySeconds, setSecond);
1237 intConstraint(mByMinutes, setMinute);
1238 intConstraint(mByHours, setHour);
1239 intConstraint(mByMonthDays, setDay);
1240 intConstraint(mByMonths, setMonth);
1241 intConstraint(mByYearDays, setYearday);
1242 intConstraint(mByWeekNumbers, setWeeknumber);
1243 #undef intConstraint
1245 if (!mByDays.isEmpty()) {
1247 for (c = 0, cend = mConstraints.count(); c < cend; ++c) {
1248 for (i = 0, iend = mByDays.count(); i < iend; ++i) {
1249 con = mConstraints[c];
1250 con.setWeekday(mByDays[i].day());
1251 con.setWeekdaynr(mByDays[i].pos());
1259 #define fixConstraint( setElement, value ) \
1261 for ( c = 0, cend = mConstraints.count(); c < cend; ++c ) { \
1262 mConstraints[c].setElement( value ); \
1269 if (mPeriod == rWeekly && mByDays.isEmpty()) {
1270 fixConstraint(setWeekday, mDateStart.date().dayOfWeek());
1277 if (mByDays.isEmpty() && mByWeekNumbers.isEmpty() &&
1278 mByYearDays.isEmpty() && mByMonths.isEmpty()) {
1279 fixConstraint(setMonth, mDateStart.date().month());
1282 if (mByDays.isEmpty() && mByWeekNumbers.isEmpty() &&
1283 mByYearDays.isEmpty() && mByMonthDays.isEmpty()) {
1284 fixConstraint(setDay, mDateStart.date().day());
1288 if (mByHours.isEmpty()) {
1289 fixConstraint(setHour, mDateStart.time().hour());
1292 if (mByMinutes.isEmpty()) {
1293 fixConstraint(setMinute, mDateStart.time().minute());
1296 if (mBySeconds.isEmpty()) {
1297 fixConstraint(setSecond, mDateStart.time().second());
1303 #undef fixConstraint
1308 mTimedRepetition = mFrequency * 3600;
1311 mTimedRepetition = mFrequency * 60;
1314 mTimedRepetition = mFrequency;
1320 for (c = 0, cend = mConstraints.count(); c < cend;) {
1321 if (mConstraints[c].isConsistent(mPeriod)) {
1324 mConstraints.removeAt(c);
1333 bool RecurrenceRule::Private::buildCache()
const
1335 Q_ASSERT(mDuration > 0);
1338 Constraint interval(getNextValidDateInterval(mDateStart, mPeriod));
1341 DateTimeList dts = datesForInterval(interval, mPeriod);
1344 int i = dts.
findLT(mDateStart);
1351 for (
int loopnr = 0; loopnr < LOOP_LIMIT && dts.
count() < mDuration; ++loopnr) {
1352 interval.increase(mPeriod, mFrequency);
1354 dts += datesForInterval(interval, mPeriod);
1356 if (dts.
count() > mDuration) {
1368 if (
int(dts.
count()) == mDuration) {
1369 mCachedDateEnd = dts.
last();
1373 mCachedDateEnd = KDateTime();
1374 mCachedLastDate = interval.intervalDateTime(mPeriod);
1382 KDateTime dt = kdt.toTimeSpec(d->mDateStart.timeSpec());
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()) {
1403 if (qd < d->mDateStart.date()) {
1408 if (d->mDuration >= 0) {
1409 endDate =
endDt().date();
1418 for (i = 0, iend = d->mConstraints.count(); i < iend && !match; ++i) {
1419 match = d->mConstraints[i].matches(qd, recurrenceType());
1425 KDateTime start(qd,
QTime(0, 0, 0), d->mDateStart.timeSpec());
1426 Constraint interval(d->getNextValidDateInterval(start, recurrenceType()));
1429 if (!interval.matches(qd, recurrenceType())) {
1435 KDateTime end = start.addDays(1);
1437 DateTimeList dts = d->datesForInterval(interval, recurrenceType());
1438 for (i = 0, iend = dts.
count(); i < iend; ++i) {
1439 if (dts[i].date() >= qd) {
1440 return dts[i].date() == qd;
1443 interval.increase(recurrenceType(),
frequency());
1444 }
while (interval.intervalDateTime(recurrenceType()) < end);
1449 KDateTime start(qd,
QTime(0, 0, 0), timeSpec);
1450 KDateTime end = start.addDays(1).toTimeSpec(d->mDateStart.timeSpec());
1451 start = start.toTimeSpec(d->mDateStart.timeSpec());
1452 if (end < d->mDateStart) {
1455 if (start < d->mDateStart) {
1456 start = d->mDateStart;
1460 if (d->mDuration >= 0) {
1461 KDateTime endRecur =
endDt();
1462 if (endRecur.isValid()) {
1463 if (start > endRecur) {
1466 if (end > endRecur) {
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;
1479 QDate startDay = start.date();
1480 QDate endDay = end.addSecs(-1).date();
1481 int dayCount = startDay.
daysTo(endDay) + 1;
1486 for (i = 0, iend = d->mConstraints.count(); i < iend && !match; ++i) {
1487 match = d->mConstraints[i].matches(startDay, recurrenceType());
1488 for (
int day = 1; day < dayCount && !match; ++day) {
1489 match = d->mConstraints[i].matches(startDay.
addDays(day), recurrenceType());
1496 Constraint interval(d->getNextValidDateInterval(start, recurrenceType()));
1500 Constraint intervalm = interval;
1502 match = intervalm.matches(startDay, recurrenceType());
1503 for (
int day = 1; day < dayCount && !match; ++day) {
1504 match = intervalm.matches(startDay.
addDays(day), recurrenceType());
1509 intervalm.increase(recurrenceType(),
frequency());
1510 }
while (intervalm.intervalDateTime(recurrenceType()) < end);
1519 DateTimeList dts = d->datesForInterval(interval, recurrenceType());
1520 int i = dts.
findGE(start);
1522 return dts[i] <= end;
1524 interval.increase(recurrenceType(),
frequency());
1525 }
while (interval.intervalDateTime(recurrenceType()) < end);
1533 KDateTime dt(kdt.toTimeSpec(d->mDateStart.timeSpec()));
1536 return recursOn(dt.date(), dt.timeSpec());
1538 if (dt < d->mDateStart) {
1542 if (d->mDuration >= 0 && dt >
endDt()) {
1546 if (d->mTimedRepetition) {
1548 return !(d->mDateStart.secsTo_long(dt) % d->mTimedRepetition);
1558 Constraint interval(d->getNextValidDateInterval(dt, recurrenceType()));
1560 if (interval.matches(dt, recurrenceType())) {
1572 KDateTime start(date,
QTime(0, 0, 0), timeSpec);
1573 KDateTime end = start.addDays(1).addSecs(-1);
1575 for (
int i = 0, iend = dts.
count(); i < iend; ++i) {
1576 lst += dts[i].toTimeSpec(timeSpec).time();
1585 KDateTime toDate(dt.toTimeSpec(d->mDateStart.timeSpec()));
1588 if (toDate < d->mDateStart) {
1592 if (d->mDuration > 0 && toDate >=
endDt()) {
1593 return d->mDuration;
1596 if (d->mTimedRepetition) {
1598 return static_cast<int>(d->mDateStart.secsTo_long(toDate) / d->mTimedRepetition);
1606 return durationTo(KDateTime(date,
QTime(23, 59, 59), d->mDateStart.timeSpec()));
1612 KDateTime toDate(afterDate.toTimeSpec(d->mDateStart.timeSpec()));
1615 if (!toDate.isValid() || toDate < d->mDateStart) {
1619 if (d->mTimedRepetition) {
1621 KDateTime prev = toDate;
1622 if (d->mDuration >= 0 &&
endDt().isValid() && toDate >
endDt()) {
1623 prev =
endDt().addSecs(1).toTimeSpec(d->mDateStart.timeSpec());
1625 int n =
static_cast<int>((d->mDateStart.secsTo_long(prev) - 1) % d->mTimedRepetition);
1629 prev = prev.addSecs(-n - 1);
1630 return prev >= d->mDateStart ? prev : KDateTime();
1634 if (d->mDuration > 0) {
1638 int i = d->mCachedDates.findLT(toDate);
1640 return d->mCachedDates[i];
1645 KDateTime prev = toDate;
1646 if (d->mDuration >= 0 &&
endDt().isValid() && toDate >
endDt()) {
1647 prev =
endDt().addSecs(1).toTimeSpec(d->mDateStart.timeSpec());
1650 Constraint interval(d->getPreviousValidDateInterval(prev, recurrenceType()));
1651 DateTimeList dts = d->datesForInterval(interval, recurrenceType());
1652 int i = dts.
findLT(prev);
1654 return (dts[i] >= d->mDateStart) ? dts[i] : KDateTime();
1658 while (interval.intervalDateTime(recurrenceType()) > d->mDateStart) {
1659 interval.increase(recurrenceType(), -
int(
frequency()));
1661 DateTimeList dts = d->datesForInterval(interval, recurrenceType());
1665 if (prev.isValid() && prev >= d->mDateStart) {
1678 KDateTime fromDate(preDate.toTimeSpec(d->mDateStart.timeSpec()));
1680 if (d->mDuration >= 0 &&
endDt().isValid() && fromDate >=
endDt()) {
1685 if (fromDate < d->mDateStart) {
1686 fromDate = d->mDateStart.addSecs(-1);
1689 if (d->mTimedRepetition) {
1691 int n =
static_cast<int>((d->mDateStart.secsTo_long(fromDate) + 1) % d->mTimedRepetition);
1692 KDateTime next = fromDate.
addSecs(d->mTimedRepetition - n + 1);
1693 return d->mDuration < 0 || !
endDt().isValid() || next <=
endDt() ? next : KDateTime();
1696 if (d->mDuration > 0) {
1700 int i = d->mCachedDates.findGT(fromDate);
1702 return d->mCachedDates[i];
1706 KDateTime end =
endDt();
1707 Constraint interval(d->getNextValidDateInterval(fromDate, recurrenceType()));
1708 DateTimeList dts = d->datesForInterval(interval, recurrenceType());
1709 int i = dts.
findGT(fromDate);
1711 return (d->mDuration < 0 || dts[i] <= end) ? dts[i] : KDateTime();
1713 interval.increase(recurrenceType(),
frequency());
1714 if (d->mDuration >= 0 && interval.intervalDateTime(recurrenceType()) > end) {
1723 DateTimeList dts = d->datesForInterval(interval, recurrenceType());
1724 if (dts.
count() > 0) {
1725 KDateTime ret(dts[0]);
1726 if (d->mDuration >= 0 && ret > end) {
1732 interval.increase(recurrenceType(),
frequency());
1733 }
while (++loop < LOOP_LIMIT &&
1734 (d->mDuration < 0 || interval.intervalDateTime(recurrenceType()) < end));
1739 const KDateTime &dtEnd)
const
1741 const KDateTime start = dtStart.toTimeSpec(d->mDateStart.timeSpec());
1742 const KDateTime end = dtEnd.toTimeSpec(d->mDateStart.timeSpec());
1744 if (end < d->mDateStart) {
1747 KDateTime enddt = end;
1748 if (d->mDuration >= 0) {
1749 const KDateTime endRecur =
endDt();
1750 if (endRecur.isValid()) {
1751 if (start > endRecur) {
1754 if (end >= endRecur) {
1760 if (d->mTimedRepetition) {
1764 qint64 offsetFromNextOccurrence;
1765 if (d->mDateStart < start) {
1766 offsetFromNextOccurrence =
1767 d->mTimedRepetition - (d->mDateStart.secsTo_long(start) % d->mTimedRepetition);
1769 offsetFromNextOccurrence = -(d->mDateStart.secsTo_long(start) % d->mTimedRepetition);
1771 KDateTime dt = start.addSecs(offsetFromNextOccurrence);
1773 int numberOfOccurrencesWithinInterval =
1774 static_cast<int>(dt.secsTo_long(enddt) / d->mTimedRepetition) + 1;
1776 numberOfOccurrencesWithinInterval = qMin(numberOfOccurrencesWithinInterval, LOOP_LIMIT);
1778 i < numberOfOccurrencesWithinInterval;
1779 dt = dt.addSecs(d->mTimedRepetition), ++i) {
1786 KDateTime st = start;
1788 if (d->mDuration > 0) {
1792 if (d->mCachedDateEnd.isValid() && start > d->mCachedDateEnd) {
1795 int i = d->mCachedDates.
findGE(start);
1797 int iend = d->mCachedDates.findGT(enddt, i);
1799 iend = d->mCachedDates.count();
1804 result += d->mCachedDates[i++];
1807 if (d->mCachedDateEnd.isValid()) {
1809 }
else if (!result.
isEmpty()) {
1810 result += KDateTime();
1817 st = d->mCachedLastDate.addSecs(1);
1820 Constraint interval(d->getNextValidDateInterval(st, recurrenceType()));
1823 DateTimeList dts = d->datesForInterval(interval, recurrenceType());
1825 int iend = dts.
count();
1832 int j = dts.
findGT(enddt, i);
1841 interval.increase(recurrenceType(),
frequency());
1842 }
while (++loop < LOOP_LIMIT &&
1843 interval.intervalDateTime(recurrenceType()) < end);
1852 Constraint RecurrenceRule::Private::getPreviousValidDateInterval(
const KDateTime &dt,
1853 PeriodType type)
const
1856 KDateTime start = mDateStart;
1857 KDateTime nextValid(start);
1859 KDateTime toDate(dt.toTimeSpec(start.timeSpec()));
1872 periods =
static_cast<int>(start.secsTo_long(toDate) / modifier);
1874 if (mFrequency > 0) {
1875 periods = (periods / mFrequency) * mFrequency;
1877 nextValid = start.addSecs(modifier * periods);
1880 toDate = toDate.addDays(-(7 + toDate.date().dayOfWeek() - mWeekStart) % 7);
1881 start = start.addDays(-(7 + start.date().dayOfWeek() - mWeekStart) % 7);
1884 periods = start.daysTo(toDate) / modifier;
1886 if (mFrequency > 0) {
1887 periods = (periods / mFrequency) * mFrequency;
1889 nextValid = start.addDays(modifier * periods);
1893 periods = 12 * (toDate.date().year() - start.date().year()) +
1894 (toDate.date().month() - start.date().month());
1896 if (mFrequency > 0) {
1897 periods = (periods / mFrequency) * mFrequency;
1901 start.setDate(
QDate(start.date().year(), start.date().month(), 1));
1902 nextValid.setDate(start.date().addMonths(periods));
1906 periods = (toDate.date().year() - start.date().year());
1908 if (mFrequency > 0) {
1909 periods = (periods / mFrequency) * mFrequency;
1911 nextValid.setDate(start.date().addYears(periods));
1917 return Constraint(nextValid, type, mWeekStart);
1924 Constraint RecurrenceRule::Private::getNextValidDateInterval(
const KDateTime &dt,
1925 PeriodType type)
const
1929 KDateTime start = mDateStart;
1930 KDateTime nextValid(start);
1932 KDateTime toDate(dt.toTimeSpec(start.timeSpec()));
1945 periods =
static_cast<int>(start.secsTo_long(toDate) / modifier);
1946 periods = qMax(0L, periods);
1947 if (periods > 0 && mFrequency > 0) {
1948 periods += (mFrequency - 1 - ((periods - 1) % mFrequency));
1950 nextValid = start.addSecs(modifier * periods);
1954 toDate = toDate.addDays(-(7 + toDate.date().dayOfWeek() - mWeekStart) % 7);
1955 start = start.addDays(-(7 + start.date().dayOfWeek() - mWeekStart) % 7);
1958 periods = start.daysTo(toDate) / modifier;
1959 periods = qMax(0L, periods);
1960 if (periods > 0 && mFrequency > 0) {
1961 periods += (mFrequency - 1 - ((periods - 1) % mFrequency));
1963 nextValid = start.addDays(modifier * periods);
1967 periods = 12 * (toDate.date().year() - start.date().year()) +
1968 (toDate.date().month() - start.date().month());
1969 periods = qMax(0L, periods);
1970 if (periods > 0 && mFrequency > 0) {
1971 periods += (mFrequency - 1 - ((periods - 1) % mFrequency));
1975 start.setDate(
QDate(start.date().year(), start.date().month(), 1));
1976 nextValid.setDate(start.date().addMonths(periods));
1980 periods = (toDate.date().year() - start.date().year());
1981 periods = qMax(0L, periods);
1982 if (periods > 0 && mFrequency > 0) {
1983 periods += (mFrequency - 1 - ((periods - 1) % mFrequency));
1985 nextValid.setDate(start.date().addYears(periods));
1991 return Constraint(nextValid, type, mWeekStart);
1994 DateTimeList RecurrenceRule::Private::datesForInterval(
const Constraint &interval,
1995 PeriodType type)
const
2004 for (
int i = 0, iend = mConstraints.count(); i < iend; ++i) {
2005 Constraint merged(interval);
2006 if (merged.merge(mConstraints[i])) {
2008 if (merged.year > 0 && merged.hour >= 0 && merged.minute >= 0 && merged.second >= 0) {
2028 if (!mBySetPos.isEmpty()) {
2031 for (
int i = 0, iend = mBySetPos.count(); i < iend; ++i) {
2032 int pos = mBySetPos[i];
2037 pos += tmplst.
count();
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();
2060 kDebug() <<
" #occurrences:" <<
duration();
2061 kDebug() <<
" start date:" << dumpTime(
startDt())
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()));
2084 dumpByIntList(d->mByMonthDays,
QLatin1String(
"ByMonthDays:"));
2085 dumpByIntList(d->mByYearDays,
QLatin1String(
"ByYearDays: "));
2086 dumpByIntList(d->mByWeekNumbers,
QLatin1String(
"ByWeekNr: "));
2089 #undef dumpByIntList
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();
2102 void Constraint::dump()
const
2104 kDebug() <<
" ~> Y=" << year
2110 <<
", wd=" << weekday
2111 <<
",#wd=" << weekdaynr
2112 <<
", #w=" << weeknumber
2113 <<
", yd=" << yearday;
2117 QString dumpTime(
const KDateTime &dt)
2120 if (!dt.isValid()) {
2124 if (dt.isDateOnly()) {
2127 result = dt.toString(
QLatin1String(
"%a %Y-%m-%d %H:%M:%S %:Z"));
2128 if (dt.isSecondOccurrence()) {
2132 if (dt.timeSpec() == KDateTime::Spec::ClockTime()) {
2144 return d->mDateStart;
2154 return d->mFrequency;
2159 return d->mDuration;
2162 QString RecurrenceRule::rrule()
const
2174 return d->mIsReadOnly;
2179 d->mIsReadOnly = readOnly;
2184 return d->mPeriod != rNone;
2192 const QList<int> &RecurrenceRule::bySeconds()
const
2194 return d->mBySeconds;
2197 const QList<int> &RecurrenceRule::byMinutes()
const
2199 return d->mByMinutes;
2202 const QList<int> &RecurrenceRule::byHours()
const
2212 const QList<int> &RecurrenceRule::byMonthDays()
const
2214 return d->mByMonthDays;
2217 const QList<int> &RecurrenceRule::byYearDays()
const
2219 return d->mByYearDays;
2222 const QList<int> &RecurrenceRule::byWeekNumbers()
const
2224 return d->mByWeekNumbers;
2227 const QList<int> &RecurrenceRule::byMonths()
const
2229 return d->mByMonths;
2232 const QList<int> &RecurrenceRule::bySetPos()
const
2234 return d->mBySetPos;
2237 short RecurrenceRule::weekStart()
const
2239 return d->mWeekStart;
2242 RecurrenceRule::RuleObserver::~RuleObserver()
2246 RecurrenceRule::WDayPos::WDayPos(
int ps,
short dy)
2247 : mDay(dy), mPos(ps)
2251 void RecurrenceRule::WDayPos::setDay(
short dy)
2256 short RecurrenceRule::WDayPos::day()
const
2261 void RecurrenceRule::WDayPos::setPos(
int ps)
2266 int 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
int daysTo(const QDate &d) const
void setFrequency(int freq)
Sets the recurrence frequency, in terms of the recurrence time period type.
structure for describing the n-th weekday of the month/year.
void dump() const
Debug output.
bool dateMatchesRules(const KDateTime &dt) const
Returns true if the date matches the rules.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last...
void sortUnique()
Sort the list.
bool isReadOnly() const
Returns true if the recurrence is read-only; false if it can be changed.
iterator erase(iterator pos)
RecurrenceRule()
/************************************************************************** RecurrenceRule * ...
QString join(const QString &separator) const
void setRRule(const QString &rrule)
Set the RRULE string for the rule.
void setReadOnly(bool readOnly)
Set if recurrence is read-only or can be changed.
void clear()
Turns off recurrence for the event.
QString number(int n, int base)
int count(const T &value) const
void append(const T &value)
void setEndDt(const KDateTime &endDateTime)
Sets the date and time of the last recurrence.
bool allDay() const
Returns whether the start date has no time associated.
bool recursAt(const KDateTime &dt) const
Returns true if the date/time specified is one at which the event will recur.
bool setYMD(int y, int m, int d)
A QList which can be sorted.
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...
uint frequency() const
Returns the recurrence frequency, in terms of the recurrence time period type.
KDateTime endDt(bool *result=0) const
Returns the date and time of the last recurrence.
friend KCALCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalCore::RecurrenceRule *)
RecurrenceRule serializer and deserializer.
KDateTime startDt() const
Returns the recurrence start date/time.
void clear()
Removes all recurrence and exception rules and dates.
bool recursOn(const QDate &date, const KDateTime::Spec &timeSpec) const
Returns true if the date specified is one on which the event will recur.
KCALCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalCore::Alarm::Ptr &)
Alarm deserializer.
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 getNextDate(const KDateTime &preDateTime) const
Returns the date and time of the next recurrence, after the specified date/time.
bool recurs() const
Returns the event's recurrence status.
void removeObserver(RuleObserver *observer)
Removes an observer that was added with addObserver.
int findGE(const T &value, int start=0) const
Search the list for the first item >= value.
PeriodType
enum for describing the frequency how an event recurs, if at all.
void addObserver(RuleObserver *observer)
Installs an observer.
int durationTo(const KDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
int findGT(const T &value, int start=0) const
Search the list for the first item > value.
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)
Sets whether the dtstart is all-day (i.e.
QDate addDays(int ndays) const
int findLT(const T &value, int start=0) const
Search the list for the last item < value.
void setStartDt(const KDateTime &start)
Sets the recurrence start date/time.
QDateTime addSecs(int s) const
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...
KCALCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalCore::Alarm::Ptr &)
Alarm serializer.
This class represents a recurrence rule for a calendar incidence.
KDateTime getPreviousDate(const KDateTime &afterDateTime) const
Returns the date and time of the last previous recurrence, before the specified date/time.