KCalendarCore

recurrence.cpp
1/*
2 This file is part of kcalcore library.
3
4 SPDX-FileCopyrightText: 1998 Preston Brown <pbrown@kde.org>
5 SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
6 SPDX-FileCopyrightText: 2002, 2006 David Jarvie <djarvie@kde.org>
7 SPDX-FileCopyrightText: 2005 Reinhold Kainhofer <kainhofer@kde.org>
8
9 SPDX-License-Identifier: LGPL-2.0-or-later
10*/
11
12#include "incidencebase.h"
13#include "recurrence.h"
14#include "recurrencehelper_p.h"
15#include "utils_p.h"
16
17#include "kcalendarcore_debug.h"
18
19#include <QBitArray>
20#include <QDataStream>
21#include <QTime>
22#include <QTimeZone>
23#include <QHash>
24
25using namespace KCalendarCore;
26
27//@cond PRIVATE
28class Q_DECL_HIDDEN KCalendarCore::Recurrence::Private
29{
30public:
31 Private()
32 : mCachedType(rMax)
33 , mAllDay(false)
34 , mRecurReadOnly(false)
35 {
36 }
37
38 Private(const Private &p)
39 : mRDateTimes(p.mRDateTimes)
40 , mRDateTimePeriods(p.mRDateTimePeriods)
41 , mRDates(p.mRDates)
42 , mExDateTimes(p.mExDateTimes)
43 , mExDates(p.mExDates)
44 , mStartDateTime(p.mStartDateTime)
45 , mCachedType(p.mCachedType)
46 , mAllDay(p.mAllDay)
47 , mRecurReadOnly(p.mRecurReadOnly)
48 {
49 }
50
51 bool operator==(const Private &p) const;
52
53 RecurrenceRule::List mExRules;
54 RecurrenceRule::List mRRules;
55 QList<QDateTime> mRDateTimes;
56 QHash<QDateTime, Period> mRDateTimePeriods; // Map RDate starts with periods if any
57 DateList mRDates;
58 QList<QDateTime> mExDateTimes;
59 DateList mExDates;
60 QDateTime mStartDateTime; // date/time of first recurrence
61 QList<RecurrenceObserver *> mObservers;
62
63 // Cache the type of the recurrence with the old system (e.g. MonthlyPos)
64 mutable ushort mCachedType;
65
66 bool mAllDay = false; // the recurrence has no time, just a date
67 bool mRecurReadOnly = false;
68};
69
70bool Recurrence::Private::operator==(const Recurrence::Private &p) const
71{
72 // qCDebug(KCALCORE_LOG) << mStartDateTime << p.mStartDateTime;
73 if (!identical(mStartDateTime, p.mStartDateTime) || mAllDay != p.mAllDay
74 || mRecurReadOnly != p.mRecurReadOnly || mExDates != p.mExDates || mExDateTimes != p.mExDateTimes || mRDates != p.mRDates
75 || mRDateTimes != p.mRDateTimes || mRDateTimePeriods != p.mRDateTimePeriods) {
76 return false;
77 }
78
79 // Compare the rrules, exrules! Assume they have the same order... This only
80 // matters if we have more than one rule (which shouldn't be the default anyway)
81 int i;
82 int end = mRRules.count();
83 if (end != p.mRRules.count()) {
84 return false;
85 }
86 for (i = 0; i < end; ++i) {
87 if (*mRRules[i] != *p.mRRules[i]) {
88 return false;
89 }
90 }
91 end = mExRules.count();
92 if (end != p.mExRules.count()) {
93 return false;
94 }
95 for (i = 0; i < end; ++i) {
96 if (*mExRules[i] != *p.mExRules[i]) {
97 return false;
98 }
99 }
100 return true;
101}
102//@endcond
103
105 : d(new KCalendarCore::Recurrence::Private())
106{
107}
108
110 : RecurrenceRule::RuleObserver()
111 , d(new KCalendarCore::Recurrence::Private(*r.d))
112{
113 int i;
114 int end;
115 d->mRRules.reserve(r.d->mRRules.count());
116 for (i = 0, end = r.d->mRRules.count(); i < end; ++i) {
117 RecurrenceRule *rule = new RecurrenceRule(*r.d->mRRules[i]);
118 d->mRRules.append(rule);
119 rule->addObserver(this);
120 }
121 d->mExRules.reserve(r.d->mExRules.count());
122 for (i = 0, end = r.d->mExRules.count(); i < end; ++i) {
123 RecurrenceRule *rule = new RecurrenceRule(*r.d->mExRules[i]);
124 d->mExRules.append(rule);
125 rule->addObserver(this);
126 }
127}
128
130{
131 qDeleteAll(d->mExRules);
132 qDeleteAll(d->mRRules);
133 delete d;
134}
135
136bool Recurrence::operator==(const Recurrence &recurrence) const
137{
138 return *d == *recurrence.d;
139}
140
141void Recurrence::addObserver(RecurrenceObserver *observer)
142{
143 if (!d->mObservers.contains(observer)) {
144 d->mObservers.append(observer);
145 }
146}
147
148void Recurrence::removeObserver(RecurrenceObserver *observer)
149{
150 d->mObservers.removeAll(observer);
151}
152
154{
155 return d->mStartDateTime;
156}
157
159{
160 return d->mAllDay;
161}
162
164{
165 if (d->mRecurReadOnly || allDay == d->mAllDay) {
166 return;
167 }
168
169 d->mAllDay = allDay;
170 for (int i = 0, end = d->mRRules.count(); i < end; ++i) {
171 d->mRRules[i]->setAllDay(allDay);
172 }
173 for (int i = 0, end = d->mExRules.count(); i < end; ++i) {
174 d->mExRules[i]->setAllDay(allDay);
175 }
176 updated();
177}
178
179RecurrenceRule *Recurrence::defaultRRule(bool create) const
180{
181 if (d->mRRules.isEmpty()) {
182 if (!create || d->mRecurReadOnly) {
183 return nullptr;
184 }
185 RecurrenceRule *rrule = new RecurrenceRule();
186 rrule->setStartDt(startDateTime());
187 const_cast<KCalendarCore::Recurrence *>(this)->addRRule(rrule);
188 return rrule;
189 } else {
190 return d->mRRules[0];
191 }
192}
193
194RecurrenceRule *Recurrence::defaultRRuleConst() const
195{
196 return d->mRRules.isEmpty() ? nullptr : d->mRRules[0];
197}
198
199void Recurrence::updated()
200{
201 // recurrenceType() re-calculates the type if it's rMax
202 d->mCachedType = rMax;
203 for (int i = 0, end = d->mObservers.count(); i < end; ++i) {
204 if (d->mObservers[i]) {
205 d->mObservers[i]->recurrenceUpdated(this);
206 }
207 }
208}
209
211{
212 return !d->mRRules.isEmpty() || !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty();
213}
214
216{
217 if (d->mCachedType == rMax) {
218 d->mCachedType = recurrenceType(defaultRRuleConst());
219 }
220 return d->mCachedType;
221}
222
224{
225 if (!rrule) {
226 return rNone;
227 }
228 RecurrenceRule::PeriodType type = rrule->recurrenceType();
229
230 // BYSETPOS, BYWEEKNUMBER and BYSECOND were not supported in old versions
231 if (!rrule->bySetPos().isEmpty() || !rrule->bySeconds().isEmpty() || !rrule->byWeekNumbers().isEmpty()) {
232 return rOther;
233 }
234
235 // It wasn't possible to set BYMINUTES, BYHOUR etc. by the old code. So if
236 // it's set, it's none of the old types
237 if (!rrule->byMinutes().isEmpty() || !rrule->byHours().isEmpty()) {
238 return rOther;
239 }
240
241 // Possible combinations were:
242 // BYDAY: with WEEKLY, MONTHLY, YEARLY
243 // BYMONTHDAY: with MONTHLY, YEARLY
244 // BYMONTH: with YEARLY
245 // BYYEARDAY: with YEARLY
246 if ((!rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly) || (!rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly)) {
247 return rOther;
248 }
249 if (!rrule->byDays().isEmpty()) {
250 if (type != RecurrenceRule::rYearly && type != RecurrenceRule::rMonthly && type != RecurrenceRule::rWeekly) {
251 return rOther;
252 }
253 }
254
255 switch (type) {
256 case RecurrenceRule::rNone:
257 return rNone;
258 case RecurrenceRule::rMinutely:
259 return rMinutely;
260 case RecurrenceRule::rHourly:
261 return rHourly;
262 case RecurrenceRule::rDaily:
263 return rDaily;
264 case RecurrenceRule::rWeekly:
265 return rWeekly;
266 case RecurrenceRule::rMonthly: {
267 if (rrule->byDays().isEmpty()) {
268 return rMonthlyDay;
269 } else if (rrule->byMonthDays().isEmpty()) {
270 return rMonthlyPos;
271 } else {
272 return rOther; // both position and date specified
273 }
274 }
275 case RecurrenceRule::rYearly: {
276 // Possible combinations:
277 // rYearlyMonth: [BYMONTH &] BYMONTHDAY
278 // rYearlyDay: BYYEARDAY
279 // rYearlyPos: [BYMONTH &] BYDAY
280 if (!rrule->byDays().isEmpty()) {
281 // can only by rYearlyPos
282 if (rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty()) {
283 return rYearlyPos;
284 } else {
285 return rOther;
286 }
287 } else if (!rrule->byYearDays().isEmpty()) {
288 // Can only be rYearlyDay
289 if (rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty()) {
290 return rYearlyDay;
291 } else {
292 return rOther;
293 }
294 } else {
295 return rYearlyMonth;
296 }
297 }
298 default:
299 return rOther;
300 }
301}
302
303bool Recurrence::recursOn(const QDate &qd, const QTimeZone &timeZone) const
304{
305 // Don't waste time if date is before the start of the recurrence
306 if (QDateTime(qd, QTime(23, 59, 59), timeZone) < d->mStartDateTime) {
307 return false;
308 }
309
310 // First handle dates. Exrules override
311 if (std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), qd)) {
312 return false;
313 }
314
315 int i;
316 int end;
317 // For all-day events a matching exrule excludes the whole day
318 // since exclusions take precedence over inclusions, we know it can't occur on that day.
319 if (allDay()) {
320 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
321 if (d->mExRules[i]->recursOn(qd, timeZone)) {
322 return false;
323 }
324 }
325 }
326
327 if (std::binary_search(d->mRDates.constBegin(), d->mRDates.constEnd(), qd)) {
328 return true;
329 }
330
331 // Check if it might recur today at all.
332 bool recurs = (startDate() == qd);
333 for (i = 0, end = d->mRDateTimes.count(); i < end && !recurs; ++i) {
334 recurs = (d->mRDateTimes[i].toTimeZone(timeZone).date() == qd);
335 }
336 for (i = 0, end = d->mRRules.count(); i < end && !recurs; ++i) {
337 recurs = d->mRRules[i]->recursOn(qd, timeZone);
338 }
339 // If the event wouldn't recur at all, simply return false, don't check ex*
340 if (!recurs) {
341 return false;
342 }
343
344 // Check if there are any times for this day excluded, either by exdate or exrule:
345 bool exon = false;
346 for (i = 0, end = d->mExDateTimes.count(); i < end && !exon; ++i) {
347 exon = (d->mExDateTimes[i].toTimeZone(timeZone).date() == qd);
348 }
349 if (!allDay()) { // we have already checked all-day times above
350 for (i = 0, end = d->mExRules.count(); i < end && !exon; ++i) {
351 exon = d->mExRules[i]->recursOn(qd, timeZone);
352 }
353 }
354
355 if (!exon) {
356 // Simple case, nothing on that day excluded, return the value from before
357 return recurs;
358 } else {
359 // Harder part: I don't think there is any way other than to calculate the
360 // whole list of items for that day.
361 // TODO: consider whether it would be more efficient to call
362 // Rule::recurTimesOn() instead of Rule::recursOn() from the start
363 TimeList timesForDay(recurTimesOn(qd, timeZone));
364 return !timesForDay.isEmpty();
365 }
366}
367
368bool Recurrence::recursAt(const QDateTime &dt) const
369{
370 // Convert to recurrence's time zone for date comparisons, and for more efficient time comparisons
371 const auto dtrecur = dt.toTimeZone(d->mStartDateTime.timeZone());
372
373 // if it's excluded anyway, don't bother to check if it recurs at all.
374 if (std::binary_search(d->mExDateTimes.constBegin(), d->mExDateTimes.constEnd(), dtrecur)
375 || std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), dtrecur.date())) {
376 return false;
377 }
378 int i;
379 int end;
380 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
381 if (d->mExRules[i]->recursAt(dtrecur)) {
382 return false;
383 }
384 }
385
386 // Check explicit recurrences, then rrules.
387 if (startDateTime() == dtrecur || std::binary_search(d->mRDateTimes.constBegin(), d->mRDateTimes.constEnd(), dtrecur)) {
388 return true;
389 }
390 for (i = 0, end = d->mRRules.count(); i < end; ++i) {
391 if (d->mRRules[i]->recursAt(dtrecur)) {
392 return true;
393 }
394 }
395
396 return false;
397}
398
399/** Calculates the cumulative end of the whole recurrence (rdates and rrules).
400 If any rrule is infinite, or the recurrence doesn't have any rrules or
401 rdates, an invalid date is returned. */
403{
405 dts << startDateTime();
406 if (!d->mRDates.isEmpty()) {
407 dts << QDateTime(d->mRDates.last(), QTime(0, 0, 0), d->mStartDateTime.timeZone());
408 }
409 if (!d->mRDateTimes.isEmpty()) {
410 dts << d->mRDateTimes.last();
411 }
412 for (int i = 0, end = d->mRRules.count(); i < end; ++i) {
413 auto rl = d->mRRules[i]->endDt();
414 // if any of the rules is infinite, the whole recurrence is
415 if (!rl.isValid()) {
416 return QDateTime();
417 }
418 dts << rl;
419 }
420 sortAndRemoveDuplicates(dts);
421 return dts.isEmpty() ? QDateTime() : dts.last();
422}
423
424/** Calculates the cumulative end of the whole recurrence (rdates and rrules).
425 If any rrule is infinite, or the recurrence doesn't have any rrules or
426 rdates, an invalid date is returned. */
428{
429 QDateTime end(endDateTime());
430 return end.isValid() ? end.date() : QDate();
431}
432
434{
435 QDateTime dt(date, d->mStartDateTime.time(), d->mStartDateTime.timeZone());
436 if (allDay()) {
437 dt.setTime(QTime(23, 59, 59));
438 }
439 setEndDateTime(dt);
440}
441
443{
444 if (d->mRecurReadOnly) {
445 return;
446 }
447 RecurrenceRule *rrule = defaultRRule(true);
448 if (!rrule) {
449 return;
450 }
451
452 // If the recurrence rule has a duration, and we're trying to set an invalid end date,
453 // we have to skip setting it to avoid setting the field dirty.
454 // The end date is already invalid since the duration is set and end date/duration
455 // are mutually exclusive.
456 // We can't use inequality check below, because endDt() also returns a valid date
457 // for a duration (it is calculated from the duration).
458 if (rrule->duration() > 0 && !dateTime.isValid()) {
459 return;
460 }
461
462 if (!identical(dateTime, rrule->endDt())) {
463 rrule->setEndDt(dateTime);
464 updated();
465 }
466}
467
469{
470 RecurrenceRule *rrule = defaultRRuleConst();
471 return rrule ? rrule->duration() : 0;
472}
473
474int Recurrence::durationTo(const QDateTime &datetime) const
475{
476 // Emulate old behavior: This is just an interface to the first rule!
477 RecurrenceRule *rrule = defaultRRuleConst();
478 return rrule ? rrule->durationTo(datetime) : 0;
479}
480
481int Recurrence::durationTo(const QDate &date) const
482{
483 return durationTo(QDateTime(date, QTime(23, 59, 59), d->mStartDateTime.timeZone()));
484}
485
487{
488 if (d->mRecurReadOnly) {
489 return;
490 }
491
492 RecurrenceRule *rrule = defaultRRule(true);
493 if (!rrule) {
494 return;
495 }
496
497 if (duration != rrule->duration()) {
498 rrule->setDuration(duration);
499 updated();
500 }
501}
502
503void Recurrence::shiftTimes(const QTimeZone &oldTz, const QTimeZone &newTz)
504{
505 if (d->mRecurReadOnly) {
506 return;
507 }
508
509 d->mStartDateTime = d->mStartDateTime.toTimeZone(oldTz);
510 d->mStartDateTime.setTimeZone(newTz);
511
512 QHash<QDateTime, Period> oldPeriods = d->mRDateTimePeriods;
513
514 for (auto &rDt : d->mRDateTimes) {
515 auto periodIt = oldPeriods.find(rDt);
516 if (periodIt != oldPeriods.end()) {
517 periodIt->shiftTimes(oldTz, newTz);
518 rDt = rDt.toTimeZone(oldTz);
519 rDt.setTimeZone(newTz);
520 // Now there are QDateTime objects in the hash? is this shifting times?
521 d->mRDateTimePeriods.insert(rDt, *periodIt);
522 }
523 }
524
525 for (auto &exDt : d->mExDateTimes) {
526 exDt = exDt.toTimeZone(oldTz);
527 exDt.setTimeZone(newTz);
528 }
529
530 for (auto &rr : d->mRRules) {
531 rr->shiftTimes(oldTz, newTz);
532 }
533
534 for (auto exR : d->mExRules) {
535 exR->shiftTimes(oldTz, newTz);
536 }
537}
538
540{
541 if (d->mRecurReadOnly) {
542 return;
543 }
544 qDeleteAll(d->mRRules);
545 d->mRRules.clear();
546 updated();
547}
548
550{
551 if (d->mRecurReadOnly) {
552 return;
553 }
554 qDeleteAll(d->mRRules);
555 d->mRRules.clear();
556 qDeleteAll(d->mExRules);
557 d->mExRules.clear();
558 d->mRDates.clear();
559 d->mRDateTimes.clear();
560 d->mRDateTimePeriods.clear();
561 d->mExDates.clear();
562 d->mExDateTimes.clear();
563 d->mCachedType = rMax;
564 updated();
565}
566
568{
569 d->mRecurReadOnly = readOnly;
570}
571
573{
574 return d->mRecurReadOnly;
575}
576
578{
579 return d->mStartDateTime.date();
580}
581
583{
584 if (d->mRecurReadOnly) {
585 return;
586 }
587 d->mStartDateTime = start;
588 setAllDay(isAllDay); // set all RRULEs and EXRULEs
589
590 int i;
591 int end;
592 for (i = 0, end = d->mRRules.count(); i < end; ++i) {
593 d->mRRules[i]->setStartDt(start);
594 }
595 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
596 d->mExRules[i]->setStartDt(start);
597 }
598 updated();
599}
600
602{
603 RecurrenceRule *rrule = defaultRRuleConst();
604 return rrule ? rrule->frequency() : 0;
605}
606
607// Emulate the old behaviour. Make this methods just an interface to the
608// first rrule
610{
611 if (d->mRecurReadOnly || freq <= 0) {
612 return;
613 }
614
615 RecurrenceRule *rrule = defaultRRule(true);
616 if (rrule) {
617 rrule->setFrequency(freq);
618 }
619 updated();
620}
621
622// WEEKLY
623
625{
626 RecurrenceRule *rrule = defaultRRuleConst();
627 return rrule ? rrule->weekStart() : 1;
628}
629
630// Emulate the old behavior
632{
633 QBitArray days(7);
634 days.fill(0);
635 RecurrenceRule *rrule = defaultRRuleConst();
636 if (rrule) {
637 const QList<RecurrenceRule::WDayPos> &bydays = rrule->byDays();
638 for (int i = 0; i < bydays.size(); ++i) {
639 if (bydays.at(i).pos() == 0) {
640 days.setBit(bydays.at(i).day() - 1);
641 }
642 }
643 }
644 return days;
645}
646
647// MONTHLY
648
649// Emulate the old behavior
651{
652 RecurrenceRule *rrule = defaultRRuleConst();
653 if (rrule) {
654 return rrule->byMonthDays();
655 } else {
656 return QList<int>();
657 }
658}
659
660// Emulate the old behavior
662{
663 RecurrenceRule *rrule = defaultRRuleConst();
664 return rrule ? rrule->byDays() : QList<RecurrenceRule::WDayPos>();
665}
666
667// YEARLY
668
670{
671 RecurrenceRule *rrule = defaultRRuleConst();
672 return rrule ? rrule->byYearDays() : QList<int>();
673}
674
676{
677 return monthDays();
678}
679
681{
682 RecurrenceRule *rrule = defaultRRuleConst();
683 return rrule ? rrule->byMonths() : QList<int>();
684}
685
690
691RecurrenceRule *Recurrence::setNewRecurrenceType(RecurrenceRule::PeriodType type, int freq)
692{
693 if (d->mRecurReadOnly || freq <= 0) {
694 return nullptr;
695 }
696
697 // Ignore the call if nothing has change
698 if (defaultRRuleConst() && defaultRRuleConst()->recurrenceType() == type && frequency() == freq) {
699 return nullptr;
700 }
701
702 qDeleteAll(d->mRRules);
703 d->mRRules.clear();
704 updated();
705 RecurrenceRule *rrule = defaultRRule(true);
706 if (!rrule) {
707 return nullptr;
708 }
709 rrule->setRecurrenceType(type);
710 rrule->setFrequency(freq);
711 rrule->setDuration(-1);
712 return rrule;
713}
714
716{
717 if (setNewRecurrenceType(RecurrenceRule::rMinutely, _rFreq)) {
718 updated();
719 }
720}
721
722void Recurrence::setHourly(int _rFreq)
723{
724 if (setNewRecurrenceType(RecurrenceRule::rHourly, _rFreq)) {
725 updated();
726 }
727}
728
729void Recurrence::setDaily(int _rFreq)
730{
731 if (setNewRecurrenceType(RecurrenceRule::rDaily, _rFreq)) {
732 updated();
733 }
734}
735
737{
738 RecurrenceRule *rrule = setNewRecurrenceType(RecurrenceRule::rWeekly, freq);
739 if (!rrule) {
740 return;
741 }
742 rrule->setWeekStart(weekStart);
743 updated();
744}
745
747{
748 setWeekly(freq, weekStart);
750}
751
756
758{
759 if (setNewRecurrenceType(RecurrenceRule::rMonthly, freq)) {
760 updated();
761 }
762}
763
765{
766 // Allow 53 for yearly!
767 if (d->mRecurReadOnly || pos > 53 || pos < -53) {
768 return;
769 }
770
771 RecurrenceRule *rrule = defaultRRule(false);
772 if (!rrule) {
773 return;
774 }
775 bool changed = false;
776 QList<RecurrenceRule::WDayPos> positions = rrule->byDays();
777
778 for (int i = 0; i < 7; ++i) {
779 if (days.testBit(i)) {
780 RecurrenceRule::WDayPos p(pos, i + 1);
781 if (!positions.contains(p)) {
782 changed = true;
783 positions.append(p);
784 }
785 }
786 }
787 if (changed) {
788 rrule->setByDays(positions);
789 updated();
790 }
791}
792
793void Recurrence::addMonthlyPos(short pos, ushort day)
794{
795 // Allow 53 for yearly!
796 if (d->mRecurReadOnly || pos > 53 || pos < -53) {
797 return;
798 }
799
800 RecurrenceRule *rrule = defaultRRule(false);
801 if (!rrule) {
802 return;
803 }
804 QList<RecurrenceRule::WDayPos> positions = rrule->byDays();
805
806 RecurrenceRule::WDayPos p(pos, day);
807 if (!positions.contains(p)) {
808 positions.append(p);
809 setMonthlyPos(positions);
810 }
811}
812
813void Recurrence::setMonthlyPos(const QList<RecurrenceRule::WDayPos> &monthlyDays)
814{
815 if (d->mRecurReadOnly) {
816 return;
817 }
818
819 RecurrenceRule *rrule = defaultRRule(true);
820 if (!rrule) {
821 return;
822 }
823
824 // TODO: sort lists
825 // the position inside the list has no meaning, so sort the list before testing if it changed
826
827 if (monthlyDays != rrule->byDays()) {
828 rrule->setByDays(monthlyDays);
829 updated();
830 }
831}
832
834{
835 if (d->mRecurReadOnly || day > 31 || day < -31) {
836 return;
837 }
838
839 RecurrenceRule *rrule = defaultRRule(true);
840 if (!rrule) {
841 return;
842 }
843
844 QList<int> monthDays = rrule->byMonthDays();
845 if (!monthDays.contains(day)) {
846 monthDays.append(day);
847 setMonthlyDate(monthDays);
848 }
849}
850
851void Recurrence::setMonthlyDate(const QList<int> &monthlyDays)
852{
853 if (d->mRecurReadOnly) {
854 return;
855 }
856
857 RecurrenceRule *rrule = defaultRRule(true);
858 if (!rrule) {
859 return;
860 }
861
862 QList<int> mD(monthlyDays);
863 QList<int> rbD(rrule->byMonthDays());
864
865 sortAndRemoveDuplicates(mD);
866 sortAndRemoveDuplicates(rbD);
867
868 if (mD != rbD) {
869 rrule->setByMonthDays(monthlyDays);
870 updated();
871 }
872}
873
875{
876 if (setNewRecurrenceType(RecurrenceRule::rYearly, freq)) {
877 updated();
878 }
879}
880
881// Daynumber within year
883{
884 RecurrenceRule *rrule = defaultRRule(false); // It must already exist!
885 if (!rrule) {
886 return;
887 }
888
889 QList<int> days = rrule->byYearDays();
890 if (!days.contains(day)) {
891 days << day;
892 setYearlyDay(days);
893 }
894}
895
896void Recurrence::setYearlyDay(const QList<int> &days)
897{
898 RecurrenceRule *rrule = defaultRRule(false); // It must already exist!
899 if (!rrule) {
900 return;
901 }
902
903 QList<int> d(days);
904 QList<int> bYD(rrule->byYearDays());
905
906 sortAndRemoveDuplicates(d);
907 sortAndRemoveDuplicates(bYD);
908
909 if (d != bYD) {
910 rrule->setByYearDays(days);
911 updated();
912 }
913}
914
915// day part of date within year
917{
918 addMonthlyDate(day);
919}
920
921void Recurrence::setYearlyDate(const QList<int> &dates)
922{
923 setMonthlyDate(dates);
924}
925
926// day part of date within year, given as position (n-th weekday)
928{
929 addMonthlyPos(pos, days);
930}
931
932void Recurrence::setYearlyPos(const QList<RecurrenceRule::WDayPos> &days)
933{
934 setMonthlyPos(days);
935}
936
937// month part of date within year
939{
940 if (d->mRecurReadOnly || month < 1 || month > 12) {
941 return;
942 }
943
944 RecurrenceRule *rrule = defaultRRule(false);
945 if (!rrule) {
946 return;
947 }
948
949 QList<int> months = rrule->byMonths();
950 if (!months.contains(month)) {
951 months << month;
952 setYearlyMonth(months);
953 }
954}
955
956void Recurrence::setYearlyMonth(const QList<int> &months)
957{
958 if (d->mRecurReadOnly) {
959 return;
960 }
961
962 RecurrenceRule *rrule = defaultRRule(false);
963 if (!rrule) {
964 return;
965 }
966
967 QList<int> m(months);
968 QList<int> bM(rrule->byMonths());
969
970 sortAndRemoveDuplicates(m);
971 sortAndRemoveDuplicates(bM);
972
973 if (m != bM) {
974 rrule->setByMonths(months);
975 updated();
976 }
977}
978
979TimeList Recurrence::recurTimesOn(const QDate &date, const QTimeZone &timeZone) const
980{
981 // qCDebug(KCALCORE_LOG) << "recurTimesOn(" << date << ")";
982 int i;
983 int end;
984 TimeList times;
985
986 // The whole day is excepted
987 if (std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), date)) {
988 return times;
989 }
990
991 // EXRULE takes precedence over RDATE entries, so for all-day events,
992 // a matching excule also excludes the whole day automatically
993 if (allDay()) {
994 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
995 if (d->mExRules[i]->recursOn(date, timeZone)) {
996 return times;
997 }
998 }
999 }
1000
1001 QDateTime dt = startDateTime().toTimeZone(timeZone);
1002 if (dt.date() == date) {
1003 times << dt.time();
1004 }
1005
1006 bool foundDate = false;
1007 for (i = 0, end = d->mRDateTimes.count(); i < end; ++i) {
1008 dt = d->mRDateTimes[i].toTimeZone(timeZone);
1009 if (dt.date() == date) {
1010 times << dt.time();
1011 foundDate = true;
1012 } else if (foundDate) {
1013 break; // <= Assume that the rdatetime list is sorted
1014 }
1015 }
1016 for (i = 0, end = d->mRRules.count(); i < end; ++i) {
1017 times += d->mRRules[i]->recurTimesOn(date, timeZone);
1018 }
1019 sortAndRemoveDuplicates(times);
1020
1021 foundDate = false;
1022 TimeList extimes;
1023 for (i = 0, end = d->mExDateTimes.count(); i < end; ++i) {
1024 dt = d->mExDateTimes[i].toTimeZone(timeZone);
1025 if (dt.date() == date) {
1026 extimes << dt.time();
1027 foundDate = true;
1028 } else if (foundDate) {
1029 break;
1030 }
1031 }
1032 if (!allDay()) { // we have already checked all-day times above
1033 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
1034 extimes += d->mExRules[i]->recurTimesOn(date, timeZone);
1035 }
1036 }
1037 sortAndRemoveDuplicates(extimes);
1038 inplaceSetDifference(times, extimes);
1039 return times;
1040}
1041
1043{
1044 int i;
1045 int count;
1046 QList<QDateTime> times;
1047 for (i = 0, count = d->mRRules.count(); i < count; ++i) {
1048 times += d->mRRules[i]->timesInInterval(start, end);
1049 }
1050
1051 // add rdatetimes that fit in the interval
1052 for (i = 0, count = d->mRDateTimes.count(); i < count; ++i) {
1053 if (d->mRDateTimes[i] >= start && d->mRDateTimes[i] <= end) {
1054 times += d->mRDateTimes[i];
1055 }
1056 }
1057
1058 // add rdates that fit in the interval
1059 QDateTime kdt = d->mStartDateTime;
1060 for (i = 0, count = d->mRDates.count(); i < count; ++i) {
1061 kdt.setDate(d->mRDates[i]);
1062 if (kdt >= start && kdt <= end) {
1063 times += kdt;
1064 }
1065 }
1066
1067 // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list
1068 // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include
1069 // mStartDateTime.
1070 // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly
1071 // add mStartDateTime to the list, otherwise we won't see the first occurrence.
1072 if ((!d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty()) && d->mRRules.isEmpty() && start <= d->mStartDateTime && end >= d->mStartDateTime) {
1073 times += d->mStartDateTime;
1074 }
1075
1076 sortAndRemoveDuplicates(times);
1077
1078 // Remove excluded times
1079 int idt = 0;
1080 int enddt = times.count();
1081 for (i = 0, count = d->mExDates.count(); i < count && idt < enddt; ++i) {
1082 while (idt < enddt && times[idt].date() < d->mExDates[i]) {
1083 ++idt;
1084 }
1085 while (idt < enddt && times[idt].date() == d->mExDates[i]) {
1086 times.removeAt(idt);
1087 --enddt;
1088 }
1089 }
1090 QList<QDateTime> extimes;
1091 for (i = 0, count = d->mExRules.count(); i < count; ++i) {
1092 extimes += d->mExRules[i]->timesInInterval(start, end);
1093 }
1094 extimes += d->mExDateTimes;
1095 sortAndRemoveDuplicates(extimes);
1096 inplaceSetDifference(times, extimes);
1097 return times;
1098}
1099
1101{
1102 QDateTime nextDT = preDateTime;
1103 // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
1104 // the exrule is identical to the rrule). If an occurrence is found, break
1105 // out of the loop by returning that QDateTime
1106 // TODO_Recurrence: Is a loop counter of 1000 really okay? I mean for secondly
1107 // recurrence, an exdate might exclude more than 1000 intervals!
1108 int loop = 0;
1109 while (loop < 1000) {
1110 // Outline of the algo:
1111 // 1) Find the next date/time after preDateTime when the event could recur
1112 // 1.0) Add the start date if it's after preDateTime
1113 // 1.1) Use the next occurrence from the explicit RDATE lists
1114 // 1.2) Add the next recurrence for each of the RRULEs
1115 // 2) Take the earliest recurrence of these = QDateTime nextDT
1116 // 3) If that date/time is not excluded, either explicitly by an EXDATE or
1117 // by an EXRULE, return nextDT as the next date/time of the recurrence
1118 // 4) If it's excluded, start all at 1), but starting at nextDT (instead
1119 // of preDateTime). Loop at most 1000 times.
1120 ++loop;
1121 // First, get the next recurrence from the RDate lists
1122 QList<QDateTime> dates;
1123 if (nextDT < startDateTime()) {
1124 dates << startDateTime();
1125 }
1126
1127 // Assume that the rdatetime list is sorted
1128 const auto it = std::upper_bound(d->mRDateTimes.constBegin(), d->mRDateTimes.constEnd(), nextDT);
1129 if (it != d->mRDateTimes.constEnd()) {
1130 dates << *it;
1131 }
1132
1133 QDateTime kdt(startDateTime());
1134 for (const auto &date : std::as_const(d->mRDates)) {
1135 kdt.setDate(date);
1136 if (kdt > nextDT) {
1137 dates << kdt;
1138 break;
1139 }
1140 }
1141
1142 // Add the next occurrences from all RRULEs.
1143 for (const auto &rule : std::as_const(d->mRRules)) {
1144 QDateTime dt = rule->getNextDate(nextDT);
1145 if (dt.isValid()) {
1146 dates << dt;
1147 }
1148 }
1149
1150 // Take the first of these (all others can't be used later on)
1151 sortAndRemoveDuplicates(dates);
1152 if (dates.isEmpty()) {
1153 return QDateTime();
1154 }
1155 nextDT = dates.first();
1156
1157 // Check if that date/time is excluded explicitly or by an exrule:
1158 if (!std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), nextDT.date())
1159 && !std::binary_search(d->mExDateTimes.constBegin(), d->mExDateTimes.constEnd(), nextDT)) {
1160 bool allowed = true;
1161 for (const auto &rule : std::as_const(d->mExRules)) {
1162 allowed = allowed && !rule->recursAt(nextDT);
1163 }
1164 if (allowed) {
1165 return nextDT;
1166 }
1167 }
1168 }
1169
1170 // Couldn't find a valid occurrences in 1000 loops, something is wrong!
1171 return QDateTime();
1172}
1173
1175{
1176 QDateTime prevDT = afterDateTime;
1177 // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
1178 // the exrule is identical to the rrule). If an occurrence is found, break
1179 // out of the loop by returning that QDateTime
1180 int loop = 0;
1181 while (loop < 1000) {
1182 // Outline of the algo:
1183 // 1) Find the next date/time after preDateTime when the event could recur
1184 // 1.1) Use the next occurrence from the explicit RDATE lists
1185 // 1.2) Add the next recurrence for each of the RRULEs
1186 // 2) Take the earliest recurrence of these = QDateTime nextDT
1187 // 3) If that date/time is not excluded, either explicitly by an EXDATE or
1188 // by an EXRULE, return nextDT as the next date/time of the recurrence
1189 // 4) If it's excluded, start all at 1), but starting at nextDT (instead
1190 // of preDateTime). Loop at most 1000 times.
1191 ++loop;
1192 // First, get the next recurrence from the RDate lists
1193 QList<QDateTime> dates;
1194 if (prevDT > startDateTime()) {
1195 dates << startDateTime();
1196 }
1197
1198 const auto it = strictLowerBound(d->mRDateTimes.constBegin(), d->mRDateTimes.constEnd(), prevDT);
1199 if (it != d->mRDateTimes.constEnd()) {
1200 dates << *it;
1201 }
1202
1203 QDateTime kdt(startDateTime());
1204 for (const auto &date : std::as_const(d->mRDates)) {
1205 kdt.setDate(date);
1206 if (kdt < prevDT) {
1207 dates << kdt;
1208 break;
1209 }
1210 }
1211
1212 // Add the previous occurrences from all RRULEs.
1213 for (const auto &rule : std::as_const(d->mRRules)) {
1214 QDateTime dt = rule->getPreviousDate(prevDT);
1215 if (dt.isValid()) {
1216 dates << dt;
1217 }
1218 }
1219
1220 // Take the last of these (all others can't be used later on)
1221 sortAndRemoveDuplicates(dates);
1222 if (dates.isEmpty()) {
1223 return QDateTime();
1224 }
1225 prevDT = dates.last();
1226
1227 // Check if that date/time is excluded explicitly or by an exrule:
1228 if (!std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), prevDT.date())
1229 && !std::binary_search(d->mExDateTimes.constBegin(), d->mExDateTimes.constEnd(), prevDT)) {
1230 bool allowed = true;
1231 for (const auto &rule : std::as_const(d->mExRules)) {
1232 allowed = allowed && !rule->recursAt(prevDT);
1233 }
1234 if (allowed) {
1235 return prevDT;
1236 }
1237 }
1238 }
1239
1240 // Couldn't find a valid occurrences in 1000 loops, something is wrong!
1241 return QDateTime();
1242}
1243
1244/***************************** PROTECTED FUNCTIONS ***************************/
1245
1246RecurrenceRule::List Recurrence::rRules() const
1247{
1248 return d->mRRules;
1249}
1250
1252{
1253 if (d->mRecurReadOnly || !rrule) {
1254 return;
1255 }
1256
1257 rrule->setAllDay(d->mAllDay);
1258 d->mRRules.append(rrule);
1259 rrule->addObserver(this);
1260 updated();
1261}
1262
1264{
1265 if (d->mRecurReadOnly) {
1266 return;
1267 }
1268
1269 d->mRRules.removeAll(rrule);
1270 rrule->removeObserver(this);
1271 updated();
1272}
1273
1275{
1276 if (d->mRecurReadOnly) {
1277 return;
1278 }
1279
1280 d->mRRules.removeAll(rrule);
1281 delete rrule;
1282 updated();
1283}
1284
1285RecurrenceRule::List Recurrence::exRules() const
1286{
1287 return d->mExRules;
1288}
1289
1291{
1292 if (d->mRecurReadOnly || !exrule) {
1293 return;
1294 }
1295
1296 exrule->setAllDay(d->mAllDay);
1297 d->mExRules.append(exrule);
1298 exrule->addObserver(this);
1299 updated();
1300}
1301
1303{
1304 if (d->mRecurReadOnly) {
1305 return;
1306 }
1307
1308 d->mExRules.removeAll(exrule);
1309 exrule->removeObserver(this);
1310 updated();
1311}
1312
1314{
1315 if (d->mRecurReadOnly) {
1316 return;
1317 }
1318
1319 d->mExRules.removeAll(exrule);
1320 delete exrule;
1321 updated();
1322}
1323
1324QList<QDateTime> Recurrence::rDateTimes() const
1325{
1326 return d->mRDateTimes;
1327}
1328
1329void Recurrence::setRDateTimes(const QList<QDateTime> &rdates)
1330{
1331 if (d->mRecurReadOnly) {
1332 return;
1333 }
1334
1335 d->mRDateTimes = rdates;
1336 sortAndRemoveDuplicates(d->mRDateTimes);
1337 d->mRDateTimePeriods.clear();
1338 updated();
1339}
1340
1341void Recurrence::addRDateTime(const QDateTime &rdate)
1342{
1343 if (d->mRecurReadOnly) {
1344 return;
1345 }
1346
1347 setInsert(d->mRDateTimes, rdate);
1348 updated();
1349}
1350
1352{
1353 if (d->mRecurReadOnly) {
1354 return;
1355 }
1356
1357 setInsert(d->mRDateTimes, period.start());
1358 d->mRDateTimePeriods.insert(period.start(), period);
1359 updated();
1360}
1361
1363{
1364 return d->mRDateTimePeriods.value(rdate);
1365}
1366
1367DateList Recurrence::rDates() const
1368{
1369 return d->mRDates;
1370}
1371
1372void Recurrence::setRDates(const DateList &rdates)
1373{
1374 if (d->mRecurReadOnly) {
1375 return;
1376 }
1377
1378 d->mRDates = rdates;
1379 sortAndRemoveDuplicates(d->mRDates);
1380 updated();
1381}
1382
1383void Recurrence::addRDate(const QDate &rdate)
1384{
1385 if (d->mRecurReadOnly) {
1386 return;
1387 }
1388
1389 setInsert(d->mRDates, rdate);
1390 updated();
1391}
1392
1393QList<QDateTime> Recurrence::exDateTimes() const
1394{
1395 return d->mExDateTimes;
1396}
1397
1398void Recurrence::setExDateTimes(const QList<QDateTime> &exdates)
1399{
1400 if (d->mRecurReadOnly) {
1401 return;
1402 }
1403
1404 d->mExDateTimes = exdates;
1405 sortAndRemoveDuplicates(d->mExDateTimes);
1406}
1407
1408void Recurrence::addExDateTime(const QDateTime &exdate)
1409{
1410 if (d->mRecurReadOnly) {
1411 return;
1412 }
1413
1414 setInsert(d->mExDateTimes, exdate);
1415 updated();
1416}
1417
1418DateList Recurrence::exDates() const
1419{
1420 return d->mExDates;
1421}
1422
1423void Recurrence::setExDates(const DateList &exdates)
1424{
1425 if (d->mRecurReadOnly) {
1426 return;
1427 }
1428
1429 DateList l = exdates;
1430 sortAndRemoveDuplicates(l);
1431
1432 if (d->mExDates != l) {
1433 d->mExDates = l;
1434 updated();
1435 }
1436}
1437
1438void Recurrence::addExDate(const QDate &exdate)
1439{
1440 if (d->mRecurReadOnly) {
1441 return;
1442 }
1443
1444 setInsert(d->mExDates, exdate);
1445 updated();
1446}
1447
1448void Recurrence::recurrenceChanged(RecurrenceRule *)
1449{
1450 updated();
1451}
1452
1453// %%%%%%%%%%%%%%%%%% end:Recurrencerule %%%%%%%%%%%%%%%%%%
1454
1456{
1457 int i;
1458 int count = d->mRRules.count();
1459 qCDebug(KCALCORE_LOG) << " -)" << count << "RRULEs:";
1460 for (i = 0; i < count; ++i) {
1461 qCDebug(KCALCORE_LOG) << " -) RecurrenceRule: ";
1462 d->mRRules[i]->dump();
1463 }
1464 count = d->mExRules.count();
1465 qCDebug(KCALCORE_LOG) << " -)" << count << "EXRULEs:";
1466 for (i = 0; i < count; ++i) {
1467 qCDebug(KCALCORE_LOG) << " -) ExceptionRule :";
1468 d->mExRules[i]->dump();
1469 }
1470
1471 count = d->mRDates.count();
1472 qCDebug(KCALCORE_LOG) << " -)" << count << "Recurrence Dates:";
1473 for (i = 0; i < count; ++i) {
1474 qCDebug(KCALCORE_LOG) << " " << d->mRDates[i];
1475 }
1476 count = d->mRDateTimes.count();
1477 qCDebug(KCALCORE_LOG) << " -)" << count << "Recurrence Date/Times:";
1478 for (i = 0; i < count; ++i) {
1479 qCDebug(KCALCORE_LOG) << " " << d->mRDateTimes[i];
1480 }
1481 count = d->mExDates.count();
1482 qCDebug(KCALCORE_LOG) << " -)" << count << "Exceptions Dates:";
1483 for (i = 0; i < count; ++i) {
1484 qCDebug(KCALCORE_LOG) << " " << d->mExDates[i];
1485 }
1486 count = d->mExDateTimes.count();
1487 qCDebug(KCALCORE_LOG) << " -)" << count << "Exception Date/Times:";
1488 for (i = 0; i < count; ++i) {
1489 qCDebug(KCALCORE_LOG) << " " << d->mExDateTimes[i];
1490 }
1491}
1492
1493Recurrence::RecurrenceObserver::~RecurrenceObserver()
1494{
1495}
1496
1498{
1499 if (!r) {
1500 return out;
1501 }
1502
1503 serializeQDateTimeList(out, r->d->mRDateTimes);
1504 out << (qint32)r->d->mRDateTimePeriods.size();
1505 for (auto it = r->d->mRDateTimePeriods.cbegin(); it != r->d->mRDateTimePeriods.cend(); ++it) {
1506 out << it.key() << it.value();
1507 }
1508 serializeQDateTimeList(out, r->d->mExDateTimes);
1509 out << r->d->mRDates;
1510 serializeQDateTimeAsKDateTime(out, r->d->mStartDateTime);
1511 out << r->d->mCachedType << r->d->mAllDay << r->d->mRecurReadOnly << r->d->mExDates << (qint32)r->d->mExRules.count() << (qint32)r->d->mRRules.count();
1512
1513 for (RecurrenceRule *rule : std::as_const(r->d->mExRules)) {
1514 out << rule;
1515 }
1516
1517 for (RecurrenceRule *rule : std::as_const(r->d->mRRules)) {
1518 out << rule;
1519 }
1520
1521 return out;
1522}
1523
1525{
1526 if (!r) {
1527 return in;
1528 }
1529
1530 int rruleCount;
1531 int exruleCount;
1532 int size;
1533
1534 deserializeQDateTimeList(in, r->d->mRDateTimes);
1535 in >> size;
1536 r->d->mRDateTimePeriods.clear();
1537 r->d->mRDateTimePeriods.reserve(size);
1538 for (int i = 0; i < size; ++i) {
1539 QDateTime start;
1540 Period period;
1541 in >> start >> period;
1542 r->d->mRDateTimes << start;
1543 r->d->mRDateTimePeriods.insert(start, period);
1544 }
1545 deserializeQDateTimeList(in, r->d->mExDateTimes);
1546 in >> r->d->mRDates;
1547 deserializeKDateTimeAsQDateTime(in, r->d->mStartDateTime);
1548 in >> r->d->mCachedType >> r->d->mAllDay >> r->d->mRecurReadOnly >> r->d->mExDates >> exruleCount >> rruleCount;
1549
1550 r->d->mExRules.clear();
1551 r->d->mRRules.clear();
1552
1553 for (int i = 0; i < exruleCount; ++i) {
1554 RecurrenceRule *rule = new RecurrenceRule();
1555 rule->addObserver(r);
1556 in >> rule;
1557 r->d->mExRules.append(rule);
1558 }
1559
1560 for (int i = 0; i < rruleCount; ++i) {
1561 RecurrenceRule *rule = new RecurrenceRule();
1562 rule->addObserver(r);
1563 in >> rule;
1564 r->d->mRRules.append(rule);
1565 }
1566
1567 return in;
1568}
The period can be defined by either a start time and an end time or by a start time and a duration.
Definition period.h:38
QDateTime start() const
Returns when this period starts.
Definition period.cpp:105
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.
QDateTime endDt(bool *result=nullptr) const
Returns the date and time of the last recurrence.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
void setFrequency(int freq)
Sets the recurrence frequency, in terms of the recurrence time period type.
void setEndDt(const QDateTime &endDateTime)
Sets the date and time of the last recurrence.
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...
PeriodType
enum for describing the frequency how an event recurs, if at all.
void setStartDt(const QDateTime &start)
Sets the recurrence start date/time.
int durationTo(const QDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
void addObserver(RuleObserver *observer)
Installs an observer.
void removeObserver(RuleObserver *observer)
Removes an observer that was added with addObserver.
This class represents a recurrence rule for a calendar incidence.
Definition recurrence.h:77
ushort recurrenceType() const
Returns the event's recurrence status.
void removeRRule(RecurrenceRule *rrule)
Remove a recurrence rule from the recurrence.
QList< RecurrenceRule::WDayPos > yearPositions() const
Returns the positions within a yearly recurrence.
void setRecurReadOnly(bool readOnly)
Set if recurrence is read-only or can be changed.
QList< int > yearDates() const
Returns the dates within a yearly recurrence.
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
void addYearlyDay(int day)
Adds day number of year within a yearly recurrence.
void setYearly(int freq)
Sets an event to recur yearly.
void setWeekly(int freq, int weekStart=1)
Sets an event to recur weekly.
Period rDateTimePeriod(const QDateTime &rdate) const
Inquire if the given RDATE is associated to a PERIOD.
bool recurs() const
Returns whether the event recurs at all.
void addRDateTimePeriod(const Period &period)
Add a RDATE defined as a PERIOD.
void setMinutely(int freq)
Sets an event to recur minutely.
void setMonthly(int freq)
Sets an event to recur monthly.
void setFrequency(int freq)
Sets the frequency of recurrence, in terms of the recurrence time period type.
QList< int > monthDays() const
Returns list of day numbers of a month.
void setStartDateTime(const QDateTime &start, bool isAllDay)
Set start of recurrence.
QDateTime getNextDateTime(const QDateTime &preDateTime) const
Returns the start date/time of the earliest recurrence with a start date/time after the specified dat...
void deleteExRule(RecurrenceRule *exrule)
Remove an exception rule from the recurrence and delete it.
void addYearlyMonth(short _rNum)
Adds month in yearly recurrence.
void deleteRRule(RecurrenceRule *rrule)
Remove a recurrence rule from the recurrence and delete it.
bool recurReadOnly() const
Returns true if the recurrence is read-only, or false if it can be changed.
void removeObserver(RecurrenceObserver *observer)
Removes an observer that was added with addObserver.
bool allDay() const
Set whether the recurrence has no time, just a date.
QDate startDate() const
Return the start date/time of the recurrence.
int durationTo(const QDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
void addMonthlyPos(short pos, const QBitArray &days)
Adds a position (e.g.
void addObserver(RecurrenceObserver *observer)
Installs an observer.
QDateTime endDateTime() const
Returns the date/time of the last recurrence.
void addYearlyDate(int date)
Adds date within a yearly recurrence.
void setEndDateTime(const QDateTime &endDateTime)
Sets the date and time of the last recurrence.
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.
QBitArray days() const
Returns week day mask (bit 0 = Monday).
void removeExRule(RecurrenceRule *exrule)
Remove an exception rule from the recurrence.
void addYearlyPos(short pos, const QBitArray &days)
Adds position within month/year within a yearly recurrence.
void setAllDay(bool allDay)
Sets whether the dtstart is a all-day (i.e.
QDateTime startDateTime() const
Return the start date/time of the recurrence (Time for all-day recurrences will be 0:00).
void addRRule(RecurrenceRule *rrule)
Add a recurrence rule to the recurrence.
void setDaily(int freq)
Sets an event to recur daily.
void unsetRecurs()
Removes all recurrence rules.
void dump() const
Debug output.
void addExRule(RecurrenceRule *exrule)
Add an exception rule to the recurrence.
void setEndDate(const QDate &endDate)
Sets the date of the last recurrence.
int weekStart() const
Returns the first day of the week.
QList< int > yearMonths() const
Returns the months within a yearly 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 setHourly(int freq)
Sets an event to recur hourly.
void clear()
Removes all recurrence and exception rules and dates.
bool recursOn(const QDate &date, const QTimeZone &timeZone) const
Returns true if the date specified is one on which the event will recur.
void addMonthlyDate(short day)
Adds a date (e.g.
QDateTime getPreviousDateTime(const QDateTime &afterDateTime) const
Returns the date and time of the last previous recurrence, before the specified date/time.
bool recursAt(const QDateTime &dt) const
Returns true if the date/time specified is one at which the event will recur.
QList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
bool operator==(const Recurrence &r) const
Comparison operator for equality.
void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
Shift the times of the recurrence so that they appear at the same clock time as before but in a new t...
void addWeeklyDays(const QBitArray &days)
Adds days to the weekly day recurrence list.
QList< RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
QDate endDate() const
Returns the date of the last recurrence.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
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.
~Recurrence() override
Destructor.
Recurrence()
Constructs an empty recurrence.
Q_SCRIPTABLE QString start(QString train="")
Q_SCRIPTABLE Q_NOREPLY void start()
This file is part of the API for handling calendar data and defines the IncidenceBase class.
Namespace for all KCalendarCore types.
Definition alarm.h:37
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
Alarm deserializer.
Definition alarm.cpp:833
QList< QDate > DateList
List of dates.
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
Alarm serializer.
Definition alarm.cpp:820
KCALENDARCORE_EXPORT bool identical(const QDateTime &dt1, const QDateTime &dt2)
Compare two QDateTimes for extended equality.
KIOCORE_EXPORT bool operator==(const UDSEntry &entry, const UDSEntry &other)
const QList< QKeySequence > & end()
QDate date() const const
bool isValid() const const
void setDate(QDate date)
void setTime(QTime time)
QTime time() const const
QDateTime toTimeZone(const QTimeZone &timeZone) const const
iterator end()
iterator find(const Key &key)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
bool contains(const AT &value) const const
qsizetype count() const const
T & first()
bool isEmpty() const const
T & last()
void removeAt(qsizetype i)
qsizetype size() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 28 2025 11:57:17 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.