Skip to content
This repository has been archived by the owner on Nov 11, 2021. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 'jb43708v3' into 'master'
[kcalcore] Properly handle exception datetimes for ClockTime events. Contributes to JB#43708

See merge request mer-core/kcalcore!12
  • Loading branch information
chriadam committed Nov 27, 2019
2 parents f8153fe + 4477195 commit 3ef35e5
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
45 changes: 45 additions & 0 deletions kcalcore/recurrence.cpp
Expand Up @@ -30,6 +30,51 @@

using namespace KCalCore;

namespace KCalCore {
template <>
int SortableList<KDateTime>::findSorted( const KDateTime &value, int start ) const
{
// Do a binary search to find the item == value
const KDateTime lzValue = value.isDateOnly()
? KDateTime(value.date(), QTime(0, 0), value.timeSpec()).toLocalZone()
: value.toLocalZone();
int st = start - 1;
int end = count();
while ( end - st > 1 ) {
int i = ( st + end ) / 2;
const KDateTime dt(at(i));
if (value.isClockTime()) {
if (value.date() < dt.date() || (value.date() == dt.date() && value.time() < dt.time())) {
end = i;
} else {
st = i;
}
} else if (dt.isClockTime()) {
if (lzValue.date() < dt.date() || (lzValue.date() == dt.date() && lzValue.time() < dt.time())) {
end = i;
} else {
st = i;
}
} else if ( value < at( i ) ) {
end = i;
} else {
st = i;
}
}
if (start >= end) {
return -1;
}
const KDateTime dt(at(st));
if (value.isClockTime()) {
return (value.date() == dt.date() && value.time() == dt.time()) ? st : -1;
} else if (dt.isClockTime()) {
return (lzValue.date() == dt.date() && lzValue.time() == dt.time()) ? st : -1;
} else {
return (value == dt) ? st : -1;
}
}
}

//@cond PRIVATE
class KCalCore::Recurrence::Private
{
Expand Down
88 changes: 88 additions & 0 deletions kcalcore/tests/testtimesininterval.cpp
Expand Up @@ -237,3 +237,91 @@ void TimesInIntervalTest::testWeeklyDayOfWeekRecurrenceDtStart()

QCOMPARE(expectedEventOccurrences.size(), 0);
}

void TimesInIntervalTest::testClockTimeHandlingAllDay()
{
// Create an event which occurs every weekday of every week,
// starting from Friday the 11th of October, and lasts for two weeks,
// with three exception datetimes (only two of which will apply).
KDateTime::Spec expansionSpec(KDateTime::Spec::LocalZone());
KDateTime::Spec exceptionSpec(expansionSpec.timeZone().name().contains("Toronto", Qt::CaseInsensitive)
? KSystemTimeZones::zone(QStringLiteral("Pacific/Midway"))
: KSystemTimeZones::zone(QStringLiteral("America/Toronto")));
KCalCore::Event::Ptr event = KCalCore::Event::Ptr(new KCalCore::Event());
event->setAllDay(true);
event->setDtStart(KDateTime(QDate(2019, 10, 11), KDateTime::ClockTime));

RecurrenceRule * const rule = new RecurrenceRule();
rule->setRecurrenceType(RecurrenceRule::rDaily);
rule->setStartDt(event->dtStart());
rule->setFrequency(1);
rule->setDuration(14);
rule->setByDays(QList<RecurrenceRule::WDayPos>() << RecurrenceRule::WDayPos(0, 1) // monday
<< RecurrenceRule::WDayPos(0, 2) // tuesday
<< RecurrenceRule::WDayPos(0, 3) // wednesday
<< RecurrenceRule::WDayPos(0, 4) // thursday
<< RecurrenceRule::WDayPos(0, 5)); // friday

event->recurrence()->addRRule(rule);
event->recurrence()->addExDateTime(KDateTime(QDate(2019, 10, 15), KDateTime::ClockTime));
event->recurrence()->addExDateTime(KDateTime(QDate(2019, 10, 17), exceptionSpec)); // this one will not apply.
event->recurrence()->addExDateTime(KDateTime(QDate(2019, 10, 24), expansionSpec)); // this one will apply.

// Expand the events and within a wide interval
const DateTimeList timesInInterval = event->recurrence()->timesInInterval(
KDateTime(QDate(2019, 10, 05), QTime(0, 0), expansionSpec),
KDateTime(QDate(2019, 10, 25), QTime(23, 59), expansionSpec));

// ensure that the expansion does not include weekend days,
// nor either of the exception date times.
const QList<int> expectedDays { 11, 14, 16, 17, 18, 21, 22, 23, 25 };
for (int day : expectedDays) {
QVERIFY(timesInInterval.contains(KDateTime(QDate(2019, 10, day), QTime(0, 0), KDateTime::ClockTime)));
}
QCOMPARE(timesInInterval.size(), expectedDays.size());
}

void TimesInIntervalTest::testClockTimeHandlingNonAllDay()
{
// Create an event which occurs every weekday of every week,
// starting from Friday the 11th of October, from 12 pm until 1 pm, clock time,
// and lasts for two weeks, with three exception datetimes,
// (only two of which will apply).
KDateTime::Spec expansionSpec(KDateTime::Spec::LocalZone());
KDateTime::Spec exceptionSpec(expansionSpec.timeZone().name().contains("Toronto", Qt::CaseInsensitive)
? KSystemTimeZones::zone(QStringLiteral("Pacific/Midway"))
: KSystemTimeZones::zone(QStringLiteral("America/Toronto")));
KCalCore::Event::Ptr event = KCalCore::Event::Ptr(new KCalCore::Event());
event->setAllDay(false);
event->setDtStart(KDateTime(QDate(2019, 10, 11), QTime(12, 0), KDateTime::ClockTime));
event->setDtEnd(KDateTime(QDate(2019, 10, 11), QTime(13, 0), KDateTime::ClockTime));

RecurrenceRule * const rule = new RecurrenceRule();
rule->setRecurrenceType(RecurrenceRule::rDaily);
rule->setStartDt(event->dtStart());
rule->setFrequency(1);
rule->setDuration(14);
rule->setByDays(QList<RecurrenceRule::WDayPos>() << RecurrenceRule::WDayPos(0, 1) // monday
<< RecurrenceRule::WDayPos(0, 2) // tuesday
<< RecurrenceRule::WDayPos(0, 3) // wednesday
<< RecurrenceRule::WDayPos(0, 4) // thursday
<< RecurrenceRule::WDayPos(0, 5)); // friday

event->recurrence()->addRRule(rule);
event->recurrence()->addExDateTime(KDateTime(QDate(2019, 10, 15), QTime(12, 0), KDateTime::ClockTime));
event->recurrence()->addExDateTime(KDateTime(QDate(2019, 10, 17), QTime(12, 0), exceptionSpec)); // this one will not apply.
event->recurrence()->addExDateTime(KDateTime(QDate(2019, 10, 24), QTime(12, 0), expansionSpec).toTimeSpec(exceptionSpec)); // this one will apply.

// Expand the events and within a wide interval
const DateTimeList timesInInterval = event->recurrence()->timesInInterval(
KDateTime(QDate(2019, 10, 05), QTime(0, 0), expansionSpec),
KDateTime(QDate(2019, 10, 25), QTime(23, 59), expansionSpec));

// ensure that the expansion does not include weekend days,
// nor either of the exception date times.
const QList<int> expectedDays { 11, 14, 16, 17, 18, 21, 22, 23, 25 };
for (int day : expectedDays) {
QVERIFY(timesInInterval.contains(KDateTime(QDate(2019, 10, day), QTime(12, 0), KDateTime::ClockTime)));
}
QCOMPARE(timesInInterval.size(), expectedDays.size());
}
2 changes: 2 additions & 0 deletions kcalcore/tests/testtimesininterval.h
Expand Up @@ -35,6 +35,8 @@ class TimesInIntervalTest : public QObject
void testSubDailyRecurrenceIntervalLimits();
void testDailyRecurrenceDtStart();
void testWeeklyDayOfWeekRecurrenceDtStart();
void testClockTimeHandlingAllDay();
void testClockTimeHandlingNonAllDay();
};

#endif

0 comments on commit 3ef35e5

Please sign in to comment.