Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 'allDay' into 'master'
[mkcal] Properly detect all day for stored events with a time zone. Contributes to JB#43311

See merge request mer-core/mkcal!17
  • Loading branch information
chriadam committed Apr 7, 2020
2 parents ac24df7 + acf7c0e commit db931f8
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 81 deletions.
6 changes: 3 additions & 3 deletions src/extendedcalendar.cpp
Expand Up @@ -273,7 +273,7 @@ Incidence::Ptr ExtendedCalendar::dissociateSingleOccurrence(const Incidence::Ptr
return Incidence::Ptr();
}

if (!dateTime.isDateOnly()) {
if (!incidence->allDay()) {
if (!incidence->recursAt(dateTime)) {
return Incidence::Ptr();
}
Expand Down Expand Up @@ -333,7 +333,7 @@ Incidence::Ptr ExtendedCalendar::dissociateSingleOccurrence(const Incidence::Ptr

recur = incidence->recurrence();
if (recur) {
if (dateTime.isDateOnly()) {
if (incidence->allDay()) {
recur->addExDate(dateTime.date());
} else {
recur->addExDateTime(dateTime);
Expand Down Expand Up @@ -1762,7 +1762,7 @@ ExtendedCalendar::ExpandedIncidenceList ExtendedCalendar::expandRecurrences(
qCDebug(lcMkcal) << "---appending" << (*iit)->summary() << dt.toString();
#endif /* DEBUG_EXPANSION */
if ((*iit)->recurs()) {
if (!(*iit)->dtStart().isDateOnly()) {
if (!(*iit)->allDay()) {
if (!(*iit)->recursAt((*iit)->dtStart())) {
#ifdef DEBUG_EXPANSION
qCDebug(lcMkcal) << "--not recurring at" << (*iit)->dtStart() << (*iit)->summary();
Expand Down
104 changes: 42 additions & 62 deletions src/sqliteformat.cpp
Expand Up @@ -96,8 +96,8 @@ class mKCal::SqliteFormat::Private
const int &type);
bool modifyRdates(Incidence::Ptr incidence, int rowid, DBOperation dbop, sqlite3_stmt *stmt1,
sqlite3_stmt *stmt2);
bool modifyRdate(int rowid, int type, const KDateTime &rdate, DBOperation dbop,
sqlite3_stmt *stmt);
bool modifyRdate(int rowid, int type, const KDateTime &rdate, bool allDay,
DBOperation dbop, sqlite3_stmt *stmt);
bool modifyCalendarProperties(Notebook::Ptr notebook, DBOperation dbop);
bool deleteCalendarProperties(const QByteArray &id);
bool insertCalendarProperty(const QByteArray &id, const QByteArray &key,
Expand Down Expand Up @@ -216,7 +216,7 @@ bool SqliteFormat::purgeDeletedComponents(const KCalCore::Incidence::Ptr &incide
return false;
}

static bool setDateTime(SqliteStorage *storage, sqlite3_stmt *stmt, int &index, const KDateTime &dateTime)
static bool setDateTime(SqliteStorage *storage, sqlite3_stmt *stmt, int &index, const KDateTime &dateTime, bool allDay)
{
int rv = 0;
sqlite3_int64 secs;
Expand All @@ -227,7 +227,7 @@ static bool setDateTime(SqliteStorage *storage, sqlite3_stmt *stmt, int &index,
sqlite3_bind_int64(stmt, index, secs);
secs = storage->toLocalOriginTime(dateTime);
sqlite3_bind_int64(stmt, index, secs);
if (dateTime.isDateOnly() && dateTime.timeSpec().isClockTime()) {
if (allDay) {
tz = FLOATING_DATE;
} else {
tz = dateTime.timeZone().name().toUtf8();
Expand All @@ -243,10 +243,10 @@ static bool setDateTime(SqliteStorage *storage, sqlite3_stmt *stmt, int &index,
return false;
}

#define sqlite3_bind_date_time( storage, stmt, index, dt) \
{ \
if (!setDateTime(storage, stmt, index, dt)) \
goto error; \
#define sqlite3_bind_date_time( storage, stmt, index, dt, allDay) \
{ \
if (!setDateTime(storage, stmt, index, dt, allDay)) \
goto error; \
}

bool SqliteFormat::modifyComponents(const Incidence::Ptr &incidence, const QString &nbook,
Expand Down Expand Up @@ -324,45 +324,27 @@ bool SqliteFormat::modifyComponents(const Incidence::Ptr &incidence, const QStri

if ((incidence->type() == Incidence::TypeEvent) ||
(incidence->type() == Incidence::TypeJournal)) {
sqlite3_bind_date_time(d->mStorage, stmt1, index, incidence->dtStart());
sqlite3_bind_date_time(d->mStorage, stmt1, index, incidence->dtStart(), incidence->allDay());

// set HasDueDate to false
sqlite3_bind_int(stmt1, index, 0);

KDateTime effectiveDtEnd;
if (incidence->type() == Incidence::TypeEvent) {
Event::Ptr event = incidence.staticCast<Event>();

if (event->hasEndDate()) {
effectiveDtEnd = event->dtEnd();
} else if (incidence->dtStart().isValid()) {
// No end date, use start date if possible
effectiveDtEnd = incidence->dtStart();
}
// all day inclusive of end time, add one day here and remove one day when reading
if (effectiveDtEnd.isValid() && incidence->allDay()) {
effectiveDtEnd = effectiveDtEnd.addDays(1);
}
}
sqlite3_bind_date_time(d->mStorage, stmt1, index, effectiveDtEnd);
sqlite3_bind_date_time(d->mStorage, stmt1, index, effectiveDtEnd, incidence->allDay());
} else if (incidence->type() == Incidence::TypeTodo) {
Todo::Ptr todo = incidence.staticCast<Todo>();
sqlite3_bind_date_time(d->mStorage, stmt1, index,
todo->hasStartDate() ? todo->dtStart(true) : KDateTime());
todo->hasStartDate() ? todo->dtStart(true) : KDateTime(), todo->allDay());

sqlite3_bind_int(stmt1, index, (int) todo->hasDueDate());

KDateTime effectiveDtDue;
if (todo->hasDueDate()) {
effectiveDtDue = todo->dtDue(true);
} else if (todo->hasStartDate()) {
// No due date, use start date if possible.
if (incidence->allDay())
effectiveDtDue = todo->dtStart(true).addDays(1);
else
effectiveDtDue = todo->dtStart(true);
}
sqlite3_bind_date_time(d->mStorage, stmt1, index, effectiveDtDue);
sqlite3_bind_date_time(d->mStorage, stmt1, index, todo->hasDueDate() ? todo->dtDue(true) : KDateTime(), todo->allDay());
}

if (incidence->type() != Incidence::TypeJournal) {
Expand Down Expand Up @@ -436,7 +418,7 @@ bool SqliteFormat::modifyComponents(const Incidence::Ptr &incidence, const QStri

sqlite3_bind_int(stmt1, index, 0); //Invitation status removed. Needed? FIXME

sqlite3_bind_date_time(d->mStorage, stmt1, index, incidence->recurrenceId());
sqlite3_bind_date_time(d->mStorage, stmt1, index, incidence->recurrenceId(), incidence->allDay());

relatedtouid = incidence->relatedTo().toUtf8();
sqlite3_bind_text(stmt1, index, relatedtouid.constData(), relatedtouid.length(), SQLITE_STATIC);
Expand Down Expand Up @@ -472,7 +454,7 @@ bool SqliteFormat::modifyComponents(const Incidence::Ptr &incidence, const QStri
}
}
sqlite3_bind_int(stmt1, index, percentComplete);
sqlite3_bind_date_time(d->mStorage, stmt1, index, effectiveDtCompleted);
sqlite3_bind_date_time(d->mStorage, stmt1, index, effectiveDtCompleted, incidence->allDay());

if (dbop == DBUpdate)
sqlite3_bind_int(stmt1, index, rowid);
Expand Down Expand Up @@ -574,7 +556,7 @@ bool SqliteFormat::Private::modifyRdates(Incidence::Ptr incidence, int rowid, DB
if (dbop == DBUpdate || dbop == DBDelete) {
// In Update always delete all first then insert all
// In Delete delete with uid at once
if (!modifyRdate(rowid, 0, KDateTime(), DBDelete, stmt1)) {
if (!modifyRdate(rowid, 0, KDateTime(), false, DBDelete, stmt1)) {
qCWarning(lcMkcal) << "failed to modify rdates for incidence" << incidence->uid();
success = false;
}
Expand All @@ -585,7 +567,7 @@ bool SqliteFormat::Private::modifyRdates(Incidence::Ptr incidence, int rowid, DB
DateList dateList = incidence->recurrence()->rDates();
DateList::ConstIterator dt;
for (dt = dateList.constBegin(); dt != dateList.constEnd(); ++dt) {
if (!modifyRdate(rowid, type, KDateTime((*dt), QTime(00, 00, 00)).toClockTime(), (dbop == DBUpdate ? DBInsert : dbop),
if (!modifyRdate(rowid, type, KDateTime((*dt)), true, (dbop == DBUpdate ? DBInsert : dbop),
stmt2)) {
qCWarning(lcMkcal) << "failed to modify rdates for incidence" << incidence->uid();
success = false;
Expand All @@ -595,7 +577,7 @@ bool SqliteFormat::Private::modifyRdates(Incidence::Ptr incidence, int rowid, DB
type = SqliteFormat::XDate;
dateList = incidence->recurrence()->exDates();
for (dt = dateList.constBegin(); dt != dateList.constEnd(); ++dt) {
if (!modifyRdate(rowid, type, KDateTime((*dt), QTime(00, 00, 00)).toClockTime(), (dbop == DBUpdate ? DBInsert : dbop),
if (!modifyRdate(rowid, type, KDateTime((*dt)), true, (dbop == DBUpdate ? DBInsert : dbop),
stmt2)) {
qCWarning(lcMkcal) << "failed to modify xdates for incidence" << incidence->uid();
success = false;
Expand All @@ -613,10 +595,8 @@ bool SqliteFormat::Private::modifyRdates(Incidence::Ptr incidence, int rowid, DB
DateTimeList dateTimeList = incidence->recurrence()->rDateTimes();
DateTimeList::ConstIterator it;
for (it = dateTimeList.constBegin(); it != dateTimeList.constEnd(); ++it) {
bool allDay(incidence->allDay() && it->isLocalZone() && it->time() == QTime(0,0));
if (!modifyRdate(rowid, type,
(allDay) ? KDateTime(it->date(), QTime(0, 0), KDateTime::ClockTime) : (*it),
(dbop == DBUpdate ? DBInsert : dbop), stmt2)) {
bool allDay(incidence->allDay() && it->isClockTime() && it->time() == QTime(0,0));
if (!modifyRdate(rowid, type, *it, allDay, (dbop == DBUpdate ? DBInsert : dbop), stmt2)) {
qCWarning(lcMkcal) << "failed to modify rdatetimes for incidence" << incidence->uid();
success = false;
}
Expand All @@ -625,10 +605,8 @@ bool SqliteFormat::Private::modifyRdates(Incidence::Ptr incidence, int rowid, DB
type = SqliteFormat::XDateTime;
dateTimeList = incidence->recurrence()->exDateTimes();
for (it = dateTimeList.constBegin(); it != dateTimeList.constEnd(); ++it) {
bool allDay(incidence->allDay() && it->isLocalZone() && it->time() == QTime(0,0));
if (!modifyRdate(rowid, type,
(allDay) ? KDateTime(it->date(), QTime(0, 0), KDateTime::ClockTime) : (*it),
(dbop == DBUpdate ? DBInsert : dbop), stmt2)) {
bool allDay(incidence->allDay() && it->isClockTime() && it->time() == QTime(0,0));
if (!modifyRdate(rowid, type, *it, allDay, (dbop == DBUpdate ? DBInsert : dbop), stmt2)) {
qCWarning(lcMkcal) << "failed to modify xdatetimes for incidence" << incidence->uid();
success = false;
}
Expand All @@ -639,7 +617,7 @@ bool SqliteFormat::Private::modifyRdates(Incidence::Ptr incidence, int rowid, DB
}

bool SqliteFormat::Private::modifyRdate(int rowid, int type, const KDateTime &date,
DBOperation dbop, sqlite3_stmt *stmt)
bool allDay, DBOperation dbop, sqlite3_stmt *stmt)
{
int rv = 0;
int index = 1;
Expand All @@ -650,7 +628,7 @@ bool SqliteFormat::Private::modifyRdate(int rowid, int type, const KDateTime &da

if (dbop == DBInsert) {
sqlite3_bind_int(stmt, index, type);
sqlite3_bind_date_time(mStorage, stmt, index, date);
sqlite3_bind_date_time(mStorage, stmt, index, date, allDay);
}

sqlite3_step(stmt);
Expand Down Expand Up @@ -770,7 +748,7 @@ bool SqliteFormat::Private::modifyAlarm(int rowid, Alarm::Ptr alarm,
} else {
sqlite3_bind_int(stmt, index, 0); // offset
sqlite3_bind_text(stmt, index, "", 0, SQLITE_STATIC); // relation
sqlite3_bind_date_time(mStorage, stmt, index, alarm->time());
sqlite3_bind_date_time(mStorage, stmt, index, alarm->time(), false);
}

sqlite3_bind_text(stmt, index, description.constData(), description.length(), SQLITE_STATIC);
Expand Down Expand Up @@ -861,7 +839,7 @@ bool SqliteFormat::Private::modifyRecursive(int rowid, RecurrenceRule *rule, DBO

sqlite3_bind_int(stmt, index, (int)rule->recurrenceType()); // frequency

sqlite3_bind_date_time(mStorage, stmt, index, rule->endDt());
sqlite3_bind_date_time(mStorage, stmt, index, rule->endDt(), rule->allDay());

sqlite3_bind_int(stmt, index, rule->duration()); // count

Expand Down Expand Up @@ -1160,11 +1138,23 @@ static KDateTime getDateTime(SqliteStorage *storage, sqlite3_stmt *stmt, int ind
date = sqlite3_column_int64(stmt, index + 1);
dateTime = storage->fromOriginTime(date);
dateTime.setTimeSpec(KDateTime::ClockTime);
if (isDate) {
// This is a workaround, for wrongly stored date
// as a date and time and not as a floating date.
QTime localTime(dateTime.time());
*isDate = dateTime.isValid() &&
localTime.hour() == 0 &&
localTime.minute() == 0 &&
localTime.second() == 0;
}
} else if (timezone == QStringLiteral(FLOATING_DATE)) {
date = sqlite3_column_int64(stmt, index + 1);
dateTime = storage->fromOriginTime(date);
dateTime.setTimeSpec(KDateTime::ClockTime);
dateTime.setDateOnly(true);
if (isDate) {
*isDate = dateTime.isValid();
}
} else {
date = sqlite3_column_int64(stmt, index);
dateTime = storage->fromOriginTime(date, timezone);
Expand All @@ -1174,13 +1164,9 @@ static KDateTime getDateTime(SqliteStorage *storage, sqlite3_stmt *stmt, int ind
date = sqlite3_column_int64(stmt, index + 1);
dateTime = storage->fromLocalOriginTime(date);
}
}
if (isDate) {
QTime localTime(dateTime.toLocalZone().time());
*isDate = dateTime.isValid() &&
localTime.hour() == 0 &&
localTime.minute() == 0 &&
localTime.second() == 0;
if (isDate) {
*isDate = false;
}
}
return dateTime;
}
Expand Down Expand Up @@ -1214,15 +1200,9 @@ Incidence::Ptr SqliteFormat::selectComponents(sqlite3_stmt *stmt1, sqlite3_stmt
bool endIsDate;
KDateTime end = getDateTime(d->mStorage, stmt1, 9, &endIsDate);
if (startIsDate && (!end.isValid() || endIsDate)) {
// all day events saved with one extra day due to KCalCore::Event::dtEnd() being inclusive of end time
if (end.isValid()) {
KDateTime dtEnd = end.addDays(-1);
if (dtEnd > start) {
event->setDtEnd(dtEnd);
}
}
event->setAllDay(true);
} else {
}
if (end.isValid()) {
event->setDtEnd(end);
}
incidence = event;
Expand Down
46 changes: 30 additions & 16 deletions tests/tst_storage.cpp
Expand Up @@ -344,15 +344,15 @@ void tst_storage::tst_alldayUtc()

auto fetchedEvent = m_calendar->event(uid);
QVERIFY(fetchedEvent.data());
QVERIFY(fetchedEvent->dtStart().isUtc());

KDateTime localStart = fetchedEvent->dtStart().toZone(KSystemTimeZones::zone("Europe/Helsinki"));
QVERIFY(localStart.time() == QTime(2, 0));

KDateTime localEnd = fetchedEvent->dtEnd().toZone(KSystemTimeZones::zone("Europe/Helsinki"));
QVERIFY(localEnd.time() == QTime(2, 0));
QVERIFY(fetchedEvent->allDay());
QVERIFY(!fetchedEvent->hasEndDate());
QVERIFY(fetchedEvent->dtStart().isDateOnly());
QCOMPARE(fetchedEvent->dtStart().date(), startDate);
QVERIFY(fetchedEvent->dtEnd().isDateOnly());
QCOMPARE(fetchedEvent->dtEnd().date(), startDate);

QCOMPARE(localEnd.date(), localStart.date().addDays(1));
QCOMPARE(fetchedEvent->dtStart().timeSpec(), KDateTime::Spec::ClockTime());
QCOMPARE(fetchedEvent->dtEnd().timeSpec(), KDateTime::Spec::ClockTime());
}

// Verify that a recurring all day event is kept by storage
Expand All @@ -361,13 +361,14 @@ void tst_storage::tst_alldayRecurrence()
auto event = KCalCore::Event::Ptr(new KCalCore::Event);

QDate startDate(2013, 12, 1);
event->setDtStart(KDateTime(startDate, QTime(), KDateTime::ClockTime));
event->setDtStart(KDateTime(startDate));
event->setAllDay(true);

KCalCore::Recurrence *recurrence = event->recurrence();
recurrence->setWeekly(1);
recurrence->setStartDateTime(event->dtStart());
recurrence->setAllDay(true);
recurrence->addRDate(startDate.addDays(2));

m_calendar->addEvent(event, NotebookId);
m_storage->save();
Expand All @@ -379,7 +380,9 @@ void tst_storage::tst_alldayRecurrence()
KCalCore::Recurrence *fetchRecurrence = fetchEvent->recurrence();
QVERIFY(fetchRecurrence);
QCOMPARE(*recurrence, *fetchRecurrence);
KDateTime match = recurrence->getNextDateTime(KDateTime(startDate));
KDateTime match = fetchRecurrence->getNextDateTime(KDateTime(startDate));
QCOMPARE(match, KDateTime(startDate.addDays(2)));
match = fetchRecurrence->getNextDateTime(KDateTime(startDate.addDays(3)));
QCOMPARE(match, KDateTime(startDate.addDays(7), QTime(), KDateTime::ClockTime));
}

Expand Down Expand Up @@ -817,7 +820,7 @@ void tst_storage::tst_rawEvents()
event->setAllDay(true);
}
} else {
event->setDtStart(KDateTime(date, KDateTime::ClockTime));
event->setDtStart(KDateTime(date));
}
event->setSummary(QStringLiteral("testing rawExpandedEvents()"));

Expand All @@ -826,7 +829,7 @@ void tst_storage::tst_rawEvents()
recurrence->setStartDateTime(event->dtStart());
recurrence->setDuration(5);
recurrence->setAllDay(event->allDay());
if (event->dtStart().isDateOnly()) {
if (event->allDay()) {
// Save exception as clock time
recurrence->addExDateTime(KDateTime(event->dtStart().date().addDays(1), QTime(0,0), KDateTime::ClockTime));
// Save exception in exception time zone
Expand Down Expand Up @@ -1298,8 +1301,13 @@ void tst_storage::tst_dissociateSingleOccurrence()
QVERIFY(occurrence);
QVERIFY(occurrence->hasRecurrenceId());
QCOMPARE(occurrence->recurrenceId(), recId);
QCOMPARE(recurrence->exDateTimes().length(), 1);
QCOMPARE(recurrence->exDateTimes()[0], recId);
if (event->allDay()) {
QCOMPARE(recurrence->exDates().length(), 1);
QCOMPARE(recurrence->exDates()[0], recId.date());
} else {
QCOMPARE(recurrence->exDateTimes().length(), 1);
QCOMPARE(recurrence->exDateTimes()[0], recId);
}
QCOMPARE(event->created().dateTime(), createdDate);
QVERIFY(occurrence->created().secsTo(KDateTime::currentUtcDateTime()) < 2);

Expand All @@ -1311,10 +1319,16 @@ void tst_storage::tst_dissociateSingleOccurrence()

KCalCore::Event::Ptr fetchEvent = m_calendar->event(event->uid());
QVERIFY(fetchEvent);
QCOMPARE(fetchEvent->allDay(), event->allDay());
QVERIFY(fetchEvent->recurs());
KCalCore::Recurrence *fetchRecurrence = event->recurrence();
QCOMPARE(fetchRecurrence->exDateTimes().length(), 1);
QCOMPARE(fetchRecurrence->exDateTimes()[0], recId);
if (event->allDay()) {
QCOMPARE(fetchRecurrence->exDates().length(), 1);
QCOMPARE(fetchRecurrence->exDates()[0], recId.date());
} else {
QCOMPARE(fetchRecurrence->exDateTimes().length(), 1);
QCOMPARE(fetchRecurrence->exDateTimes()[0], recId);
}

KCalCore::Incidence::List occurences = m_calendar->instances(event);
QCOMPARE(occurences.length(), 1);
Expand Down

0 comments on commit db931f8

Please sign in to comment.