56
63
/******************************************************************************
57
* Set up a KARecurrence from recurrence parameters, using the start date to
58
* determine the recurrence day/month as appropriate.
59
* Only a restricted subset of recurrence types is allowed.
60
* Reply = true if successful.
64
* Set up a KARecurrence from recurrence parameters, using the start date to
65
* determine the recurrence day/month as appropriate.
66
* Only a restricted subset of recurrence types is allowed.
67
* Reply = true if successful.
62
69
bool KARecurrence::set(Type recurType, int freq, int count, int f29, const KDateTime& start, const KDateTime& end)
65
RecurrenceRule::PeriodType rrtype;
68
case MINUTELY: rrtype = RecurrenceRule::rMinutely; break;
69
case DAILY: rrtype = RecurrenceRule::rDaily; break;
70
case WEEKLY: rrtype = RecurrenceRule::rWeekly; break;
71
case MONTHLY_DAY: rrtype = RecurrenceRule::rMonthly; break;
72
case ANNUAL_DATE: rrtype = RecurrenceRule::rYearly; break;
73
case NO_RECUR: rrtype = RecurrenceRule::rNone; break;
77
if (!init(rrtype, freq, count, f29, start, end))
84
days.setBit(start.date().dayOfWeek() - 1);
89
addMonthlyDate(start.date().day());
92
addYearlyDate(start.date().day());
93
addYearlyMonth(start.date().month());
72
RecurrenceRule::PeriodType rrtype;
75
case MINUTELY: rrtype = RecurrenceRule::rMinutely; break;
76
case DAILY: rrtype = RecurrenceRule::rDaily; break;
77
case WEEKLY: rrtype = RecurrenceRule::rWeekly; break;
78
case MONTHLY_DAY: rrtype = RecurrenceRule::rMonthly; break;
79
case ANNUAL_DATE: rrtype = RecurrenceRule::rYearly; break;
80
case NO_RECUR: rrtype = RecurrenceRule::rNone; break;
84
if (!init(rrtype, freq, count, f29, start, end))
91
days.setBit(start.date().dayOfWeek() - 1);
96
addMonthlyDate(start.date().day());
99
addYearlyDate(start.date().day());
100
addYearlyMonth(start.date().month());
101
108
/******************************************************************************
102
* Initialise a KARecurrence from recurrence parameters.
103
* Reply = true if successful.
109
* Initialise a KARecurrence from recurrence parameters.
110
* Reply = true if successful.
105
112
bool KARecurrence::init(RecurrenceRule::PeriodType recurType, int freq, int count, int f29, const KDateTime& start,
106
113
const KDateTime& end)
109
Feb29Type feb29Type = (f29 == -1) ? mDefaultFeb29 : static_cast<Feb29Type>(f29);
110
mFeb29Type = Feb29_None;
114
bool dateOnly = start.isDateOnly();
115
if (!count && ((!dateOnly && !end.isValid())
116
|| (dateOnly && !end.date().isValid())))
120
case RecurrenceRule::rMinutely:
121
case RecurrenceRule::rDaily:
122
case RecurrenceRule::rWeekly:
123
case RecurrenceRule::rMonthly:
124
case RecurrenceRule::rYearly:
131
setNewRecurrenceType(recurType, freq);
135
setEndDate(end.date());
138
KDateTime startdt = start;
139
if (recurType == RecurrenceRule::rYearly
140
&& (feb29Type == Feb29_Feb28 || feb29Type == Feb29_Mar1))
142
int year = startdt.date().year();
143
if (!QDate::isLeapYear(year)
144
&& startdt.date().dayOfYear() == (feb29Type == Feb29_Mar1 ? 60 : 59))
146
/* The event start date is February 28th or March 1st, but it
147
* is a recurrence on February 29th (recurring on February 28th
148
* or March 1st in non-leap years). Adjust the start date to
149
* be on February 29th in the last previous leap year.
150
* This is necessary because KARecurrence represents all types
151
* of 29th February recurrences by a simple 29th February.
153
while (!QDate::isLeapYear(--year)) ;
154
startdt.setDate(QDate(year, 2, 29));
156
mFeb29Type = feb29Type;
158
Recurrence::setStartDateTime(startdt); // sets recurrence all-day if date-only
116
Feb29Type feb29Type = (f29 == -1) ? mDefaultFeb29 : static_cast<Feb29Type>(f29);
117
mFeb29Type = Feb29_None;
121
bool dateOnly = start.isDateOnly();
122
if (!count && ((!dateOnly && !end.isValid())
123
|| (dateOnly && !end.date().isValid())))
127
case RecurrenceRule::rMinutely:
128
case RecurrenceRule::rDaily:
129
case RecurrenceRule::rWeekly:
130
case RecurrenceRule::rMonthly:
131
case RecurrenceRule::rYearly:
138
setNewRecurrenceType(recurType, freq);
142
setEndDate(end.date());
145
KDateTime startdt = start;
146
if (recurType == RecurrenceRule::rYearly
147
&& (feb29Type == Feb29_Feb28 || feb29Type == Feb29_Mar1))
149
int year = startdt.date().year();
150
if (!QDate::isLeapYear(year)
151
&& startdt.date().dayOfYear() == (feb29Type == Feb29_Mar1 ? 60 : 59))
153
/* The event start date is February 28th or March 1st, but it
154
* is a recurrence on February 29th (recurring on February 28th
155
* or March 1st in non-leap years). Adjust the start date to
156
* be on February 29th in the last previous leap year.
157
* This is necessary because KARecurrence represents all types
158
* of 29th February recurrences by a simple 29th February.
160
while (!QDate::isLeapYear(--year)) ;
161
startdt.setDate(QDate(year, 2, 29));
163
mFeb29Type = feb29Type;
165
Recurrence::setStartDateTime(startdt); // sets recurrence all-day if date-only
162
169
/******************************************************************************
163
* Initialise the recurrence from an iCalendar RRULE string.
170
* Initialise the recurrence from an iCalendar RRULE string.
165
172
bool KARecurrence::set(const QString& icalRRULE)
167
static QString RRULE = QLatin1String("RRULE:");
170
if (icalRRULE.isEmpty())
173
if (!format.fromString(defaultRRule(true),
174
(icalRRULE.startsWith(RRULE) ? icalRRULE.mid(RRULE.length()) : icalRRULE)))
174
static QString RRULE = QLatin1String("RRULE:");
177
if (icalRRULE.isEmpty())
180
if (!format.fromString(defaultRRule(true),
181
(icalRRULE.startsWith(RRULE) ? icalRRULE.mid(RRULE.length()) : icalRRULE)))
180
187
/******************************************************************************
189
196
void KARecurrence::fix()
192
mFeb29Type = Feb29_None;
194
int days[2] = { 0, 0 };
195
RecurrenceRule* rrules[2];
196
RecurrenceRule::List rrulelist = rRules();
198
int rrend = rrulelist.count();
199
for (int i = 0; i < 2 && rri < rrend; ++i, ++rri)
201
RecurrenceRule* rrule = rrulelist[rri];
204
int rtype = recurrenceType(rrule);
208
// Convert an hourly recurrence to a minutely one
209
rrule->setRecurrenceType(RecurrenceRule::rMinutely);
210
rrule->setFrequency(rrule->frequency() * 60);
211
// fall through to rMinutely
219
++rri; // remove all rules except the first
222
if (dailyType(rrule))
223
{ // it's a daily rule with BYDAYS
225
++rri; // remove all rules except the first
230
// Ensure that the yearly day number is 60 (i.e. Feb 29th/Mar 1st)
233
// This is the second rule.
234
// Ensure that it can be combined with the first one.
236
|| rrule->frequency() != rrules[0]->frequency()
237
|| rrule->startDt() != rrules[0]->startDt())
240
QList<int> ds = rrule->byYearDays();
241
if (!ds.isEmpty() && ds.first() == 60)
243
++convert; // this rule needs to be converted
248
break; // not day 60, so remove this rule
252
QList<int> ds = rrule->byMonthDays();
255
int day = ds.first();
258
// This is the second rule.
259
// Ensure that it can be combined with the first one.
260
if (day == days[0] || (day == -1 && days[0] == 60)
261
|| rrule->frequency() != rrules[0]->frequency()
262
|| rrule->startDt() != rrules[0]->startDt())
267
ds.clear(); // remove all but the first day
269
rrule->setByMonthDays(ds);
273
// Last day of the month - only combine if it's February
274
QList<int> months = rrule->byMonths();
275
if (months.count() != 1 || months.first() != 2)
278
if (day == 29 || day == -1)
280
++convert; // this rule may need to be converted
297
// Remove surplus rules
298
for ( ; rri < rrend; ++rri)
299
deleteRRule(rrulelist[rri]);
306
// There are two yearly recurrence rules to combine into a February 29th recurrence.
307
// Combine the two recurrence rules into a single rYearlyMonth rule falling on Feb 29th.
308
// Find the duration of the two RRULEs combined, using the shorter of the two if they differ.
311
// Swap the two rules so that the 29th rule is the first
312
RecurrenceRule* rr = rrules[0];
313
rrules[0] = rrules[1]; // the 29th rule
317
days[1] = d; // the non-29th day
319
// If February is included in the 29th rule, remove it to avoid duplication
320
months = rrules[0]->byMonths();
321
if (months.removeAll(2))
322
rrules[0]->setByMonths(months);
324
count = combineDurations(rrules[0], rrules[1], end);
325
mFeb29Type = (days[1] == 60) ? Feb29_Mar1 : Feb29_Feb28;
327
else if (convert == 1 && days[0] == 60)
329
// There is a single 60th day of the year rule.
330
// Convert it to a February 29th recurrence.
334
mFeb29Type = Feb29_Mar1;
339
// Create the new February 29th recurrence
340
setNewRecurrenceType(RecurrenceRule::rYearly, frequency());
341
RecurrenceRule* rrule = defaultRRule();
343
rrule->setByMonths(months);
346
rrule->setByMonthDays(ds);
199
mFeb29Type = Feb29_None;
201
int days[2] = { 0, 0 };
202
RecurrenceRule* rrules[2];
203
RecurrenceRule::List rrulelist = rRules();
205
int rrend = rrulelist.count();
206
for (int i = 0; i < 2 && rri < rrend; ++i, ++rri)
208
RecurrenceRule* rrule = rrulelist[rri];
211
int rtype = recurrenceType(rrule);
215
// Convert an hourly recurrence to a minutely one
216
rrule->setRecurrenceType(RecurrenceRule::rMinutely);
217
rrule->setFrequency(rrule->frequency() * 60);
218
// fall through to rMinutely
226
++rri; // remove all rules except the first
229
if (dailyType(rrule))
230
{ // it's a daily rule with BYDAYS
232
++rri; // remove all rules except the first
237
// Ensure that the yearly day number is 60 (i.e. Feb 29th/Mar 1st)
240
// This is the second rule.
241
// Ensure that it can be combined with the first one.
243
|| rrule->frequency() != rrules[0]->frequency()
244
|| rrule->startDt() != rrules[0]->startDt())
247
QList<int> ds = rrule->byYearDays();
248
if (!ds.isEmpty() && ds.first() == 60)
250
++convert; // this rule needs to be converted
255
break; // not day 60, so remove this rule
259
QList<int> ds = rrule->byMonthDays();
262
int day = ds.first();
265
// This is the second rule.
266
// Ensure that it can be combined with the first one.
267
if (day == days[0] || (day == -1 && days[0] == 60)
268
|| rrule->frequency() != rrules[0]->frequency()
269
|| rrule->startDt() != rrules[0]->startDt())
274
ds.clear(); // remove all but the first day
276
rrule->setByMonthDays(ds);
280
// Last day of the month - only combine if it's February
281
QList<int> months = rrule->byMonths();
282
if (months.count() != 1 || months.first() != 2)
285
if (day == 29 || day == -1)
287
++convert; // this rule may need to be converted
304
// Remove surplus rules
305
for ( ; rri < rrend; ++rri)
306
deleteRRule(rrulelist[rri]);
313
// There are two yearly recurrence rules to combine into a February 29th recurrence.
314
// Combine the two recurrence rules into a single rYearlyMonth rule falling on Feb 29th.
315
// Find the duration of the two RRULEs combined, using the shorter of the two if they differ.
318
// Swap the two rules so that the 29th rule is the first
319
RecurrenceRule* rr = rrules[0];
320
rrules[0] = rrules[1]; // the 29th rule
324
days[1] = d; // the non-29th day
326
// If February is included in the 29th rule, remove it to avoid duplication
327
months = rrules[0]->byMonths();
328
if (months.removeAll(2))
329
rrules[0]->setByMonths(months);
331
count = combineDurations(rrules[0], rrules[1], end);
332
mFeb29Type = (days[1] == 60) ? Feb29_Mar1 : Feb29_Feb28;
334
else if (convert == 1 && days[0] == 60)
336
// There is a single 60th day of the year rule.
337
// Convert it to a February 29th recurrence.
341
mFeb29Type = Feb29_Mar1;
346
// Create the new February 29th recurrence
347
setNewRecurrenceType(RecurrenceRule::rYearly, frequency());
348
RecurrenceRule* rrule = defaultRRule();
350
rrule->setByMonths(months);
353
rrule->setByMonthDays(ds);
353
360
/******************************************************************************
375
382
KDateTime KARecurrence::getPreviousDateTime(const KDateTime& afterDateTime) const
383
writeRecurrence(recur);
384
return recur.getPreviousDateTime(afterDateTime);
387
return Recurrence::getPreviousDateTime(afterDateTime);
390
writeRecurrence(recur);
391
return recur.getPreviousDateTime(afterDateTime);
394
return Recurrence::getPreviousDateTime(afterDateTime);
391
398
/******************************************************************************
392
399
* Initialise a KCal::Recurrence to be the same as this instance.
393
400
* Additional recurrence rules are created as necessary if it recurs on Feb 29th.
395
void KARecurrence::writeRecurrence(KCal::Recurrence& recur) const
402
void KARecurrence::writeRecurrence(Recurrence& recur) const
398
recur.setStartDateTime(startDateTime());
399
recur.setExDates(exDates());
400
recur.setExDateTimes(exDateTimes());
401
const RecurrenceRule* rrule = defaultRRuleConst();
404
int freq = frequency();
405
int count = duration();
406
static_cast<KARecurrence*>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq);
408
recur.setDuration(count);
410
recur.setEndDateTime(endDateTime());
414
if (rrule->byDays().isEmpty())
416
// fall through to rWeekly
419
recur.defaultRRule(true)->setByDays(rrule->byDays());
422
recur.defaultRRule(true)->setByMonthDays(rrule->byMonthDays());
425
recur.defaultRRule(true)->setByMonths(rrule->byMonths());
426
recur.defaultRRule()->setByDays(rrule->byDays());
430
QList<int> months = rrule->byMonths();
431
QList<int> days = monthDays();
432
bool special = (mFeb29Type != Feb29_None && !days.isEmpty()
433
&& days.first() == 29 && months.removeAll(2));
434
RecurrenceRule* rrule1 = recur.defaultRRule();
435
rrule1->setByMonths(months);
436
rrule1->setByMonthDays(days);
440
// It recurs on the 29th February.
441
// Create an additional 60th day of the year, or last day of February, rule.
442
RecurrenceRule* rrule2 = new RecurrenceRule();
443
rrule2->setRecurrenceType(RecurrenceRule::rYearly);
444
rrule2->setFrequency(freq);
445
rrule2->setStartDt(startDateTime());
446
rrule2->setAllDay(allDay());
448
rrule2->setEndDt(endDateTime());
449
if (mFeb29Type == Feb29_Mar1)
453
rrule2->setByYearDays(ds);
459
rrule2->setByMonthDays(ds);
462
rrule2->setByMonths(ms);
465
if (months.isEmpty())
467
// Only February recurs.
468
// Replace the RRULE and keep the recurrence count the same.
470
rrule2->setDuration(count);
475
// Months other than February also recur on the 29th.
476
// Remove February from the list and add a separate RRULE for February.
479
rrule1->setDuration(-1);
480
rrule2->setDuration(-1);
483
/* Adjust counts in the two rules to keep the correct occurrence total.
484
* Note that durationTo() always includes the start date. Since for an
485
* individual RRULE the start date may not actually be included, we need
486
* to decrement the count if the start date doesn't actually recur in
488
* Note that if the count is small, one of the rules may not recur at
489
* all. In that case, retain it so that the February 29th characteristic
490
* is not lost should the user later change the recurrence count.
492
KDateTime end = endDateTime();
493
int count1 = rrule1->durationTo(end)
494
- (rrule1->recursOn(startDate(), startDateTime().timeSpec()) ? 0 : 1);
496
rrule1->setDuration(count1);
498
rrule1->setEndDt(startDateTime());
499
int count2 = rrule2->durationTo(end)
500
- (rrule2->recursOn(startDate(), startDateTime().timeSpec()) ? 0 : 1);
502
rrule2->setDuration(count2);
504
rrule2->setEndDt(startDateTime());
508
recur.addRRule(rrule2);
405
recur.setStartDateTime(startDateTime());
406
recur.setExDates(exDates());
407
recur.setExDateTimes(exDateTimes());
408
const RecurrenceRule* rrule = defaultRRuleConst();
411
int freq = frequency();
412
int count = duration();
413
static_cast<KARecurrence*>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq);
415
recur.setDuration(count);
417
recur.setEndDateTime(endDateTime());
421
if (rrule->byDays().isEmpty())
423
// fall through to rWeekly
426
recur.defaultRRule(true)->setByDays(rrule->byDays());
429
recur.defaultRRule(true)->setByMonthDays(rrule->byMonthDays());
432
recur.defaultRRule(true)->setByMonths(rrule->byMonths());
433
recur.defaultRRule()->setByDays(rrule->byDays());
437
QList<int> months = rrule->byMonths();
438
QList<int> days = monthDays();
439
bool special = (mFeb29Type != Feb29_None && !days.isEmpty()
440
&& days.first() == 29 && months.removeAll(2));
441
RecurrenceRule* rrule1 = recur.defaultRRule();
442
rrule1->setByMonths(months);
443
rrule1->setByMonthDays(days);
447
// It recurs on the 29th February.
448
// Create an additional 60th day of the year, or last day of February, rule.
449
RecurrenceRule* rrule2 = new RecurrenceRule();
450
rrule2->setRecurrenceType(RecurrenceRule::rYearly);
451
rrule2->setFrequency(freq);
452
rrule2->setStartDt(startDateTime());
453
rrule2->setAllDay(allDay());
455
rrule2->setEndDt(endDateTime());
456
if (mFeb29Type == Feb29_Mar1)
460
rrule2->setByYearDays(ds);
466
rrule2->setByMonthDays(ds);
469
rrule2->setByMonths(ms);
472
if (months.isEmpty())
474
// Only February recurs.
475
// Replace the RRULE and keep the recurrence count the same.
477
rrule2->setDuration(count);
482
// Months other than February also recur on the 29th.
483
// Remove February from the list and add a separate RRULE for February.
486
rrule1->setDuration(-1);
487
rrule2->setDuration(-1);
490
/* Adjust counts in the two rules to keep the correct occurrence total.
491
* Note that durationTo() always includes the start date. Since for an
492
* individual RRULE the start date may not actually be included, we need
493
* to decrement the count if the start date doesn't actually recur in
495
* Note that if the count is small, one of the rules may not recur at
496
* all. In that case, retain it so that the February 29th characteristic
497
* is not lost should the user later change the recurrence count.
499
KDateTime end = endDateTime();
500
int count1 = rrule1->durationTo(end)
501
- (rrule1->recursOn(startDate(), startDateTime().timeSpec()) ? 0 : 1);
503
rrule1->setDuration(count1);
505
rrule1->setEndDt(startDateTime());
506
int count2 = rrule2->durationTo(end)
507
- (rrule2->recursOn(startDate(), startDateTime().timeSpec()) ? 0 : 1);
509
rrule2->setDuration(count2);
511
rrule2->setEndDt(startDateTime());
515
recur.addRRule(rrule2);
516
523
/******************************************************************************
519
526
KDateTime KARecurrence::endDateTime() const
521
if (mFeb29Type == Feb29_None || duration() <= 1)
523
/* Either it doesn't have any special February 29th treatment,
524
* it's infinite (count = -1), the end date is specified
525
* (count = 0), or it ends on the start date (count = 1).
526
* So just use the normal KCal end date calculation.
528
return Recurrence::endDateTime();
531
/* Create a temporary recurrence rule to find the end date.
532
* In a standard KCal recurrence, the 29th February only occurs once every
533
* 4 years. So shift the temporary recurrence date to the 28th to ensure
534
* that it occurs every year, thus giving the correct occurrence count.
536
RecurrenceRule* rrule = new RecurrenceRule();
537
rrule->setRecurrenceType(RecurrenceRule::rYearly);
538
KDateTime dt = startDateTime();
543
// The start date is definitely a recurrence date, so shift
544
// start date to the temporary recurrence date of the 28th
545
d.setYMD(d.year(), d.month(), 28);
548
if (d.month() != 2 || mFeb29Type != Feb29_Feb28 || QDate::isLeapYear(d.year()))
550
// Start date is not a recurrence date, so shift it to 27th
551
d.setYMD(d.year(), d.month(), 27);
555
if (d.month() == 3 && mFeb29Type == Feb29_Mar1 && !QDate::isLeapYear(d.year()))
557
// Start date is a March 1st recurrence date, so shift
558
// start date to the temporary recurrence date of the 28th
559
d.setYMD(d.year(), 2, 28);
566
rrule->setStartDt(dt);
567
rrule->setAllDay(allDay());
568
rrule->setFrequency(frequency());
569
rrule->setDuration(duration());
572
rrule->setByMonthDays(ds);
573
rrule->setByMonths(defaultRRuleConst()->byMonths());
577
// We've found the end date for a recurrence on the 28th. Unless that date
578
// is a real February 28th recurrence, adjust to the actual recurrence date.
579
if (mFeb29Type == Feb29_Feb28 && dt.date().month() == 2 && !QDate::isLeapYear(dt.date().year()))
581
return dt.addDays(1);
528
if (mFeb29Type == Feb29_None || duration() <= 1)
530
/* Either it doesn't have any special February 29th treatment,
531
* it's infinite (count = -1), the end date is specified
532
* (count = 0), or it ends on the start date (count = 1).
533
* So just use the normal KCal end date calculation.
535
return Recurrence::endDateTime();
538
/* Create a temporary recurrence rule to find the end date.
539
* In a standard KCal recurrence, the 29th February only occurs once every
540
* 4 years. So shift the temporary recurrence date to the 28th to ensure
541
* that it occurs every year, thus giving the correct occurrence count.
543
RecurrenceRule* rrule = new RecurrenceRule();
544
rrule->setRecurrenceType(RecurrenceRule::rYearly);
545
KDateTime dt = startDateTime();
550
// The start date is definitely a recurrence date, so shift
551
// start date to the temporary recurrence date of the 28th
552
d.setYMD(d.year(), d.month(), 28);
555
if (d.month() != 2 || mFeb29Type != Feb29_Feb28 || QDate::isLeapYear(d.year()))
557
// Start date is not a recurrence date, so shift it to 27th
558
d.setYMD(d.year(), d.month(), 27);
562
if (d.month() == 3 && mFeb29Type == Feb29_Mar1 && !QDate::isLeapYear(d.year()))
564
// Start date is a March 1st recurrence date, so shift
565
// start date to the temporary recurrence date of the 28th
566
d.setYMD(d.year(), 2, 28);
573
rrule->setStartDt(dt);
574
rrule->setAllDay(allDay());
575
rrule->setFrequency(frequency());
576
rrule->setDuration(duration());
579
rrule->setByMonthDays(ds);
580
rrule->setByMonths(defaultRRuleConst()->byMonths());
584
// We've found the end date for a recurrence on the 28th. Unless that date
585
// is a real February 28th recurrence, adjust to the actual recurrence date.
586
if (mFeb29Type == Feb29_Feb28 && dt.date().month() == 2 && !QDate::isLeapYear(dt.date().year()))
588
return dt.addDays(1);
584
591
/******************************************************************************
622
629
int KARecurrence::combineDurations(const RecurrenceRule* rrule1, const RecurrenceRule* rrule2, QDate& end) const
624
int count1 = rrule1->duration();
625
int count2 = rrule2->duration();
626
if (count1 == -1 && count2 == -1)
629
// One of the RRULEs may not recur at all if the recurrence count is small.
630
// In this case, its end date will have been set to the start date.
631
if (count1 && !count2 && rrule2->endDt().date() == startDateTime().date())
633
if (count2 && !count1 && rrule1->endDt().date() == startDateTime().date())
636
/* The duration counts will be different even for RRULEs of the same length,
637
* because the first RRULE only actually occurs every 4 years. So we need to
638
* compare the end dates.
640
if (!count1 || !count2)
642
// Get the two rules sorted by end date.
643
KDateTime end1 = rrule1->endDt();
644
KDateTime end2 = rrule2->endDt();
645
if (end1.date() == end2.date())
648
return count1 + count2;
650
const RecurrenceRule* rr1; // earlier end date
651
const RecurrenceRule* rr2; // later end date
653
&& (!end1.isValid() || end1.date() > end2.date()))
655
// Swap the two rules to make rr1 have the earlier end date
668
// Get the date of the next occurrence after the end of the earlier ending rule
669
RecurrenceRule rr(*rr1);
671
KDateTime next1(rr.getNextDate(end1));
672
next1.setDateOnly(true);
673
if (!next1.isValid())
677
if (end2.isValid() && next1 > end2)
679
// The next occurrence after the end of the earlier ending rule
680
// is later than the end of the later ending rule. So simply use
681
// the end date of the later rule.
683
return count1 + count2;
685
QDate prev2 = rr2->getPreviousDate(next1).date();
686
end = (prev2 > end1.date()) ? prev2 : end1.date();
689
count2 = rr2->durationTo(end);
690
return count1 + count2;
631
int count1 = rrule1->duration();
632
int count2 = rrule2->duration();
633
if (count1 == -1 && count2 == -1)
636
// One of the RRULEs may not recur at all if the recurrence count is small.
637
// In this case, its end date will have been set to the start date.
638
if (count1 && !count2 && rrule2->endDt().date() == startDateTime().date())
640
if (count2 && !count1 && rrule1->endDt().date() == startDateTime().date())
643
/* The duration counts will be different even for RRULEs of the same length,
644
* because the first RRULE only actually occurs every 4 years. So we need to
645
* compare the end dates.
647
if (!count1 || !count2)
649
// Get the two rules sorted by end date.
650
KDateTime end1 = rrule1->endDt();
651
KDateTime end2 = rrule2->endDt();
652
if (end1.date() == end2.date())
655
return count1 + count2;
657
const RecurrenceRule* rr1; // earlier end date
658
const RecurrenceRule* rr2; // later end date
660
&& (!end1.isValid() || end1.date() > end2.date()))
662
// Swap the two rules to make rr1 have the earlier end date
675
// Get the date of the next occurrence after the end of the earlier ending rule
676
RecurrenceRule rr(*rr1);
678
KDateTime next1(rr.getNextDate(end1));
679
next1.setDateOnly(true);
680
if (!next1.isValid())
684
if (end2.isValid() && next1 > end2)
686
// The next occurrence after the end of the earlier ending rule
687
// is later than the end of the later ending rule. So simply use
688
// the end date of the later rule.
690
return count1 + count2;
692
QDate prev2 = rr2->getPreviousDate(next1).date();
693
end = (prev2 > end1.date()) ? prev2 : end1.date();
696
count2 = rr2->durationTo(end);
697
return count1 + count2;
693
700
/******************************************************************************
694
* Return the longest interval between recurrences.
695
* Reply = 0 if it never recurs.
701
* Return the longest interval between recurrences.
702
* Reply = 0 if it never recurs.
697
704
Duration KARecurrence::longestInterval() const
699
int freq = frequency();
703
return Duration(freq * 60, Duration::Seconds);
707
QList<RecurrenceRule::WDayPos> days = defaultRRuleConst()->byDays();
709
return Duration(freq, Duration::Days);
711
// After applying the frequency, the specified days of the week
712
// further restrict when the recurrence occurs.
713
// So the maximum interval may be greater than the frequency.
714
bool ds[7] = { false, false, false, false, false, false, false };
715
for (int i = 0, end = days.count(); i < end; ++i)
716
if (days[i].pos() == 0)
717
ds[days[i].day() - 1] = true;
720
// It will recur on every day of the week in some week or other
721
// (except for those days which are excluded).
725
for (int i = 0; i < freq*7; i += freq)
731
else if (i - last > maxgap)
736
int wrap = freq*7 - last + first;
739
return Duration(maxgap, Duration::Days);
743
// It will recur on the same day of the week every time.
744
// Ensure that the day is a day which is not excluded.
745
if (ds[startDate().dayOfWeek() - 1])
746
return Duration(freq, Duration::Days);
752
// Find which days of the week it recurs on, and if on more than
753
// one, reduce the maximum interval accordingly.
754
QBitArray ds = days();
758
// Use the user's definition of the week, starting at the
759
// day of the week specified by the user's locale.
760
int weekStart = KGlobal::locale()->weekStartDay() - 1; // zero-based
761
for (int i = 0; i < 7; ++i)
763
// Get the standard KDE day-of-week number (zero-based)
764
// for the day-of-week number in the user's locale.
765
if (ds.testBit((i + weekStart) % 7))
769
else if (i - last > maxgap)
775
break; // no days recur
776
int span = last - first;
778
return Duration(freq*7 - span, Duration::Days);
779
if (7 - span > maxgap)
780
return Duration(7 - span, Duration::Days);
781
return Duration(maxgap, Duration::Days);
785
return Duration(freq * 31, Duration::Days);
790
// Find which months of the year it recurs on, and if on more than
791
// one, reduce the maximum interval accordingly.
792
const QList<int> months = yearMonths(); // month list is sorted
793
if (months.isEmpty())
794
break; // no months recur
795
if (months.count() == 1)
796
return Duration(freq * 365, Duration::Days);
800
for (int i = 0, end = months.count(); i < end; ++i)
806
int span = QDate(2001, last, 1).daysTo(QDate(2001, months[i], 1));
812
int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1));
814
return Duration(freq*365 - span, Duration::Days);
815
if (365 - span > maxgap)
816
return Duration(365 - span, Duration::Days);
817
return Duration(maxgap, Duration::Days);
706
int freq = frequency();
710
return Duration(freq * 60, Duration::Seconds);
714
QList<RecurrenceRule::WDayPos> days = defaultRRuleConst()->byDays();
716
return Duration(freq, Duration::Days);
718
// After applying the frequency, the specified days of the week
719
// further restrict when the recurrence occurs.
720
// So the maximum interval may be greater than the frequency.
721
bool ds[7] = { false, false, false, false, false, false, false };
722
for (int i = 0, end = days.count(); i < end; ++i)
723
if (days[i].pos() == 0)
724
ds[days[i].day() - 1] = true;
727
// It will recur on every day of the week in some week or other
728
// (except for those days which are excluded).
732
for (int i = 0; i < freq*7; i += freq)
738
else if (i - last > maxgap)
743
int wrap = freq*7 - last + first;
746
return Duration(maxgap, Duration::Days);
750
// It will recur on the same day of the week every time.
751
// Ensure that the day is a day which is not excluded.
752
if (ds[startDate().dayOfWeek() - 1])
753
return Duration(freq, Duration::Days);
759
// Find which days of the week it recurs on, and if on more than
760
// one, reduce the maximum interval accordingly.
761
QBitArray ds = days();
765
// Use the user's definition of the week, starting at the
766
// day of the week specified by the user's locale.
767
int weekStart = KGlobal::locale()->weekStartDay() - 1; // zero-based
768
for (int i = 0; i < 7; ++i)
770
// Get the standard KDE day-of-week number (zero-based)
771
// for the day-of-week number in the user's locale.
772
if (ds.testBit((i + weekStart) % 7))
776
else if (i - last > maxgap)
782
break; // no days recur
783
int span = last - first;
785
return Duration(freq*7 - span, Duration::Days);
786
if (7 - span > maxgap)
787
return Duration(7 - span, Duration::Days);
788
return Duration(maxgap, Duration::Days);
792
return Duration(freq * 31, Duration::Days);
797
// Find which months of the year it recurs on, and if on more than
798
// one, reduce the maximum interval accordingly.
799
const QList<int> months = yearMonths(); // month list is sorted
800
if (months.isEmpty())
801
break; // no months recur
802
if (months.count() == 1)
803
return Duration(freq * 365, Duration::Days);
807
for (int i = 0, end = months.count(); i < end; ++i)
813
int span = QDate(2001, last, 1).daysTo(QDate(2001, months[i], 1));
819
int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1));
821
return Duration(freq*365 - span, Duration::Days);
822
if (365 - span > maxgap)
823
return Duration(365 - span, Duration::Days);
824
return Duration(maxgap, Duration::Days);
825
832
/******************************************************************************
830
837
Duration KARecurrence::regularInterval() const
832
int freq = frequency();
836
return Duration(freq * 60, Duration::Seconds);
839
QList<RecurrenceRule::WDayPos> days = defaultRRuleConst()->byDays();
841
return Duration(freq, Duration::Days);
842
// After applying the frequency, the specified days of the week
843
// further restrict when the recurrence occurs.
844
// Find which days occur, and count the number of days which occur.
845
bool ds[7] = { false, false, false, false, false, false, false };
846
for (int i = 0, end = days.count(); i < end; ++i)
847
if (days[i].pos() == 0)
848
ds[days[i].day() - 1] = true;
851
// It will recur on the same day of the week every time.
852
// Check whether that day is in the list of included days.
853
if (ds[startDate().dayOfWeek() - 1])
854
return Duration(freq, Duration::Days);
857
int n = 0; // number of days which occur
858
for (int i = 0; i < 7; ++i)
862
return Duration(freq, Duration::Days); // every day is included
864
return Duration(freq * 7, Duration::Days); // only one day of the week is included
869
QList<RecurrenceRule::WDayPos> days = defaultRRuleConst()->byDays();
871
return Duration(freq * 7, Duration::Days);
872
// The specified days of the week occur every week in which the
873
// recurrence occurs.
874
// Find which days occur, and count the number of days which occur.
875
bool ds[7] = { false, false, false, false, false, false, false };
876
for (int i = 0, end = days.count(); i < end; ++i)
877
if (days[i].pos() == 0)
878
ds[days[i].day() - 1] = true;
879
int n = 0; // number of days which occur
880
for (int i = 0; i < 7; ++i)
886
return Duration(freq, Duration::Days); // every day is included
890
return Duration(freq * 7, Duration::Days); // only one day of the week is included
839
int freq = frequency();
843
return Duration(freq * 60, Duration::Seconds);
846
QList<RecurrenceRule::WDayPos> days = defaultRRuleConst()->byDays();
848
return Duration(freq, Duration::Days);
849
// After applying the frequency, the specified days of the week
850
// further restrict when the recurrence occurs.
851
// Find which days occur, and count the number of days which occur.
852
bool ds[7] = { false, false, false, false, false, false, false };
853
for (int i = 0, end = days.count(); i < end; ++i)
854
if (days[i].pos() == 0)
855
ds[days[i].day() - 1] = true;
858
// It will recur on the same day of the week every time.
859
// Check whether that day is in the list of included days.
860
if (ds[startDate().dayOfWeek() - 1])
861
return Duration(freq, Duration::Days);
864
int n = 0; // number of days which occur
865
for (int i = 0; i < 7; ++i)
869
return Duration(freq, Duration::Days); // every day is included
871
return Duration(freq * 7, Duration::Days); // only one day of the week is included
876
QList<RecurrenceRule::WDayPos> days = defaultRRuleConst()->byDays();
878
return Duration(freq * 7, Duration::Days);
879
// The specified days of the week occur every week in which the
880
// recurrence occurs.
881
// Find which days occur, and count the number of days which occur.
882
bool ds[7] = { false, false, false, false, false, false, false };
883
for (int i = 0, end = days.count(); i < end; ++i)
884
if (days[i].pos() == 0)
885
ds[days[i].day() - 1] = true;
886
int n = 0; // number of days which occur
887
for (int i = 0; i < 7; ++i)
893
return Duration(freq, Duration::Days); // every day is included
897
return Duration(freq * 7, Duration::Days); // only one day of the week is included
899
906
/******************************************************************************
900
* Return the recurrence's period type.
907
* Return the recurrence's period type.
902
909
KARecurrence::Type KARecurrence::type() const
904
if (mCachedType == -1)
905
mCachedType = type(defaultRRuleConst());
906
return static_cast<Type>(mCachedType);
911
if (mCachedType == -1)
912
mCachedType = type(defaultRRuleConst());
913
return static_cast<Type>(mCachedType);
916
/******************************************************************************
917
* Return the recurrence rule type.
909
919
KARecurrence::Type KARecurrence::type(const RecurrenceRule* rrule)
911
switch (recurrenceType(rrule))
913
case rMinutely: return MINUTELY;
914
case rDaily: return DAILY;
915
case rWeekly: return WEEKLY;
916
case rMonthlyDay: return MONTHLY_DAY;
917
case rMonthlyPos: return MONTHLY_POS;
918
case rYearlyMonth: return ANNUAL_DATE;
919
case rYearlyPos: return ANNUAL_POS;
921
if (dailyType(rrule))
921
switch (recurrenceType(rrule))
923
case rMinutely: return MINUTELY;
924
case rDaily: return DAILY;
925
case rWeekly: return WEEKLY;
926
case rMonthlyDay: return MONTHLY_DAY;
927
case rMonthlyPos: return MONTHLY_POS;
928
case rYearlyMonth: return ANNUAL_DATE;
929
case rYearlyPos: return ANNUAL_POS;
931
if (dailyType(rrule))
927
937
/******************************************************************************
928
* Check if the rule is a daily rule with or without BYDAYS specified.
938
* Check if the rule is a daily rule with or without BYDAYS specified.
930
940
bool KARecurrence::dailyType(const RecurrenceRule* rrule)
932
if (rrule->recurrenceType() != RecurrenceRule::rDaily
933
|| !rrule->bySeconds().isEmpty()
934
|| !rrule->byMinutes().isEmpty()
935
|| !rrule->byHours().isEmpty()
936
|| !rrule->byWeekNumbers().isEmpty()
937
|| !rrule->byMonthDays().isEmpty()
938
|| !rrule->byMonths().isEmpty()
939
|| !rrule->bySetPos().isEmpty()
940
|| !rrule->byYearDays().isEmpty())
942
QList<RecurrenceRule::WDayPos> days = rrule->byDays();
945
// Check that all the positions are zero (i.e. every time)
947
for (int i = 0, end = days.count(); i < end; ++i)
949
if (days[i].pos() != 0)
942
if (rrule->recurrenceType() != RecurrenceRule::rDaily
943
|| !rrule->bySeconds().isEmpty()
944
|| !rrule->byMinutes().isEmpty()
945
|| !rrule->byHours().isEmpty()
946
|| !rrule->byWeekNumbers().isEmpty()
947
|| !rrule->byMonthDays().isEmpty()
948
|| !rrule->byMonths().isEmpty()
949
|| !rrule->bySetPos().isEmpty()
950
|| !rrule->byYearDays().isEmpty())
952
QList<RecurrenceRule::WDayPos> days = rrule->byDays();
955
// Check that all the positions are zero (i.e. every time)
957
for (int i = 0, end = days.count(); i < end; ++i)
959
if (days[i].pos() != 0)