Skip to content

Commit

Permalink
Merge branch 'tz' into 'master'
Browse files Browse the repository at this point in the history
[nemo-qml-plugin-calendar] Add timezone bindings for events. Contributes to TJC#81380

See merge request mer-core/nemo-qml-plugin-calendar!51
  • Loading branch information
pvuorela committed Sep 9, 2020
2 parents 884f815 + d4cc301 commit 1ccc5ee
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 18 deletions.
48 changes: 46 additions & 2 deletions src/calendarevent.cpp
Expand Up @@ -64,12 +64,56 @@ QString CalendarEvent::description() const

QDateTime CalendarEvent::startTime() const
{
return mManager->getEvent(mUniqueId, mRecurrenceId).startTime.dateTime();
// Cannot use KDateTime::dateTime() here because it is handling UTC
// spec in a different manner than other specs. If UTC, the QDateTime
// will be in UTC also and the UI will convert it to local when displaying
// the time, while in every other case, it set the QDateTime in
// local zone.
const KDateTime kdt = mManager->getEvent(mUniqueId, mRecurrenceId).startTime;
return QDateTime(kdt.date(), kdt.time());
}

QDateTime CalendarEvent::endTime() const
{
return mManager->getEvent(mUniqueId, mRecurrenceId).endTime.dateTime();
const KDateTime kdt = mManager->getEvent(mUniqueId, mRecurrenceId).endTime;
return QDateTime(kdt.date(), kdt.time());
}

static CalendarEvent::TimeSpec toTimeSpec(const KDateTime &dt)
{
switch (dt.timeType()) {
case (KDateTime::ClockTime):
return CalendarEvent::SpecClockTime;
case (KDateTime::LocalZone):
return CalendarEvent::SpecLocalZone;
case (KDateTime::TimeZone):
return CalendarEvent::SpecTimeZone;
case (KDateTime::UTC):
return CalendarEvent::SpecUtc;
default:
// Ignore other time types.
return CalendarEvent::SpecLocalZone;
}
}

CalendarEvent::TimeSpec CalendarEvent::startTimeSpec() const
{
return toTimeSpec(mManager->getEvent(mUniqueId, mRecurrenceId).startTime);
}

CalendarEvent::TimeSpec CalendarEvent::endTimeSpec() const
{
return toTimeSpec(mManager->getEvent(mUniqueId, mRecurrenceId).endTime);
}

QString CalendarEvent::startTimeZone() const
{
return mManager->getEvent(mUniqueId, mRecurrenceId).startTime.timeZone().name();
}

QString CalendarEvent::endTimeZone() const
{
return mManager->getEvent(mUniqueId, mRecurrenceId).endTime.timeZone().name();
}

bool CalendarEvent::allDay() const
Expand Down
14 changes: 12 additions & 2 deletions src/calendarevent.h
Expand Up @@ -44,14 +44,17 @@ class CalendarEvent : public QObject
{
Q_OBJECT
Q_ENUMS(Reminder)
Q_ENUMS(TimeSpec)
Q_ENUMS(Secrecy)
Q_ENUMS(Response)

Q_PROPERTY(QString displayLabel READ displayLabel NOTIFY displayLabelChanged)
Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
Q_PROPERTY(QDateTime startTime READ startTime NOTIFY startTimeChanged)
Q_PROPERTY(QDateTime endTime READ endTime NOTIFY endTimeChanged)
Q_PROPERTY(CalendarEvent::TimeSpec startTimeSpec READ startTimeSpec NOTIFY startTimeChanged)
Q_PROPERTY(CalendarEvent::TimeSpec endTimeSpec READ endTimeSpec NOTIFY endTimeChanged)
Q_PROPERTY(QString startTimeZone READ startTimeZone NOTIFY startTimeChanged)
Q_PROPERTY(QString endTimeZone READ endTimeZone NOTIFY endTimeChanged)
Q_PROPERTY(bool allDay READ allDay NOTIFY allDayChanged)
Q_PROPERTY(CalendarEvent::Recur recur READ recur NOTIFY recurChanged)
Q_PROPERTY(QDateTime recurEndDate READ recurEndDate NOTIFY recurEndDateChanged)
Expand Down Expand Up @@ -99,8 +102,11 @@ class CalendarEvent : public QObject

enum TimeSpec {
SpecLocalZone,
SpecClockTime
SpecClockTime,
SpecTimeZone,
SpecUtc
};
Q_ENUM(TimeSpec)

enum Secrecy {
SecrecyPublic,
Expand All @@ -122,6 +128,10 @@ class CalendarEvent : public QObject
QString description() const;
QDateTime startTime() const;
QDateTime endTime() const;
TimeSpec startTimeSpec() const;
TimeSpec endTimeSpec() const;
QString startTimeZone() const;
QString endTimeZone() const;
bool allDay() const;
Recur recur() const;
QDateTime recurEndDate() const;
Expand Down
33 changes: 21 additions & 12 deletions src/calendareventmodification.cpp
@@ -1,5 +1,6 @@
#include "calendareventmodification.h"
#include "calendarmanager.h"
#include <ksystemtimezone.h>

#include <QDebug>

Expand Down Expand Up @@ -54,14 +55,27 @@ QDateTime CalendarEventModification::startTime() const
return m_event.startTime.dateTime();
}

void CalendarEventModification::setStartTime(const QDateTime &startTime, int spec)
static KDateTime toKDateTime(const QDateTime &dt, int spec, const QString &timezone)
{
KDateTime::SpecType kSpec = KDateTime::LocalZone;
if (spec == CalendarEvent::SpecClockTime) {
kSpec = KDateTime::ClockTime;
if (spec == CalendarEvent::SpecTimeZone) {
KTimeZone tz = KSystemTimeZones::zone(timezone);
if (tz.isValid()) {
return KDateTime(dt, tz);
} else {
qWarning() << "Invalid zone name, falling back to local zone:" << timezone;
return KDateTime(dt, KDateTime::LocalZone);
}
} else if (spec == CalendarEvent::SpecClockTime) {
return KDateTime(dt, KDateTime::ClockTime);
} else if (spec == CalendarEvent::SpecUtc) {
return KDateTime(QDateTime(dt.date(), dt.time(), Qt::UTC));
}
return KDateTime(dt, KDateTime::LocalZone);
}

KDateTime time(startTime, kSpec);
void CalendarEventModification::setStartTime(const QDateTime &startTime, int spec, const QString &timezone)
{
const KDateTime time = toKDateTime(startTime, spec, timezone);
if (m_event.startTime != time) {
m_event.startTime = time;
emit startTimeChanged();
Expand All @@ -73,14 +87,9 @@ QDateTime CalendarEventModification::endTime() const
return m_event.endTime.dateTime();
}

void CalendarEventModification::setEndTime(const QDateTime &endTime, int spec)
void CalendarEventModification::setEndTime(const QDateTime &endTime, int spec, const QString &timezone)
{
KDateTime::SpecType kSpec = KDateTime::LocalZone;
if (spec == CalendarEvent::SpecClockTime) {
kSpec = KDateTime::ClockTime;
}
KDateTime time(endTime, kSpec);

const KDateTime time = toKDateTime(endTime, spec, timezone);
if (m_event.endTime != time) {
m_event.endTime = time;
emit endTimeChanged();
Expand Down
4 changes: 2 additions & 2 deletions src/calendareventmodification.h
Expand Up @@ -38,10 +38,10 @@ class CalendarEventModification : public QObject
void setDescription(const QString &description);

QDateTime startTime() const;
Q_INVOKABLE void setStartTime(const QDateTime &startTime, int spec);
Q_INVOKABLE void setStartTime(const QDateTime &startTime, int spec, const QString &timezone = QString());

QDateTime endTime() const;
Q_INVOKABLE void setEndTime(const QDateTime &endTime, int spec);
Q_INVOKABLE void setEndTime(const QDateTime &endTime, int spec, const QString &timezone = QString());

bool allDay() const;
void setAllDay(bool);
Expand Down
32 changes: 32 additions & 0 deletions src/calendareventoccurrence.cpp
Expand Up @@ -32,6 +32,8 @@

#include "calendareventoccurrence.h"

#include <QTimeZone>

#include "calendarevent.h"
#include "calendarmanager.h"

Expand Down Expand Up @@ -70,3 +72,33 @@ void CalendarEventOccurrence::eventUidChanged(QString oldUid, QString newUid)
if (mEventUid == oldUid)
mEventUid = newUid;
}

static QDateTime toEventDateTime(const QDateTime &dateTime,
CalendarEvent::TimeSpec eventSpec,
const QString &eventTimezone)
{
switch (eventSpec) {
case (CalendarEvent::SpecTimeZone): {
const QDateTime dt = dateTime.toTimeZone(QTimeZone(eventTimezone.toUtf8()));
return QDateTime(dt.date(), dt.time());
}
case (CalendarEvent::SpecUtc): {
const QDateTime dt = dateTime.toUTC();
return QDateTime(dt.date(), dt.time());
}
default:
return dateTime;
}
}

QDateTime CalendarEventOccurrence::startTimeInTz() const
{
const CalendarEvent *event = eventObject();
return event ? toEventDateTime(mStartTime, event->startTimeSpec(), event->startTimeZone()) : mStartTime;
}

QDateTime CalendarEventOccurrence::endTimeInTz() const
{
const CalendarEvent *event = eventObject();
return event ? toEventDateTime(mEndTime, event->endTimeSpec(), event->endTimeZone()) : mEndTime;
}
6 changes: 6 additions & 0 deletions src/calendareventoccurrence.h
Expand Up @@ -43,8 +43,12 @@ class CalendarEvent;
class CalendarEventOccurrence : public QObject
{
Q_OBJECT
// startTime and endTime are givent in local time.
Q_PROPERTY(QDateTime startTime READ startTime CONSTANT)
Q_PROPERTY(QDateTime endTime READ endTime CONSTANT)
// startTimeInTz and endTimeInTz are given in event startTime / endTime timezone
Q_PROPERTY(QDateTime startTimeInTz READ startTimeInTz CONSTANT)
Q_PROPERTY(QDateTime endTimeInTz READ endTimeInTz CONSTANT)
Q_PROPERTY(CalendarEvent *event READ eventObject CONSTANT)

public:
Expand All @@ -57,6 +61,8 @@ class CalendarEventOccurrence : public QObject

QDateTime startTime() const;
QDateTime endTime() const;
QDateTime startTimeInTz() const;
QDateTime endTimeInTz() const;
CalendarEvent *eventObject() const;

private slots:
Expand Down
83 changes: 83 additions & 0 deletions tests/tst_calendarevent/tst_calendarevent.cpp
Expand Up @@ -24,6 +24,8 @@ private slots:

void modSetters();
void testSave();
void testTimeZone_data();
void testTimeZone();
void testRecurrenceException();
void testDate_data();
void testDate();
Expand Down Expand Up @@ -182,6 +184,13 @@ void tst_CalendarEvent::testSave()
QCOMPARE(eventB->endTime().toTime_t(), endTime.toTime_t());
QCOMPARE(eventB->startTime().toTime_t(), startTime.toTime_t());

QCOMPARE(eventB->endTime().timeSpec(), Qt::LocalTime);
QCOMPARE(eventB->startTime().timeSpec(), Qt::LocalTime);
QCOMPARE(eventB->endTimeSpec(), CalendarEvent::SpecTimeZone);
QCOMPARE(eventB->startTimeSpec(), CalendarEvent::SpecTimeZone);
QCOMPARE(eventB->endTimeZone().toUtf8(), endTime.timeZone().id());
QCOMPARE(eventB->startTimeZone().toUtf8(), startTime.timeZone().id());

QCOMPARE(eventB->allDay(), allDay);
QCOMPARE(eventB->description(), description);
QCOMPARE(eventB->displayLabel(), displayLabel);
Expand All @@ -196,6 +205,80 @@ void tst_CalendarEvent::testSave()
delete eventMod;
}

void tst_CalendarEvent::testTimeZone_data()
{
QTest::addColumn<CalendarEvent::TimeSpec>("spec");

QTest::newRow("clock time") << CalendarEvent::SpecClockTime;
QTest::newRow("local zone") << CalendarEvent::SpecLocalZone;
QTest::newRow("UTC") << CalendarEvent::SpecUtc;
QTest::newRow("time zone") << CalendarEvent::SpecTimeZone;
}

void tst_CalendarEvent::testTimeZone()
{
QFETCH(CalendarEvent::TimeSpec, spec);

CalendarEventModification *eventMod = calendarApi->createNewEvent();
QVERIFY(eventMod != 0);

QDateTime startTime = QDateTime(QDate(2020, 4, 8), QTime(16, 50));
if (spec == CalendarEvent::SpecTimeZone) {
// Using the system time zone, because agendamodels are looking for
// events in the same day in the system time zone.
eventMod->setStartTime(startTime, spec, QDateTime::currentDateTime().timeZone().id());
} else {
eventMod->setStartTime(startTime, spec);
}
QDateTime endTime = startTime.addSecs(3600);
if (spec == CalendarEvent::SpecTimeZone) {
eventMod->setEndTime(endTime, spec, QDateTime::currentDateTime().timeZone().id());
} else {
eventMod->setEndTime(endTime, spec);
}

QString uid;
bool ok = saveEvent(eventMod, &uid);
if (!ok) {
QFAIL("Failed to fetch new event uid");
}
QVERIFY(!uid.isEmpty());
mSavedEvents.insert(uid);

CalendarEventQuery query;
query.setUniqueId(uid);

for (int i = 0; i < 30; i++) {
if (query.event())
break;

QTest::qWait(100);
}
CalendarEvent *eventB = (CalendarEvent*) query.event();
QVERIFY(eventB != 0);

QCOMPARE(eventB->endTime(), endTime);
QCOMPARE(eventB->startTime(), startTime);

QCOMPARE(eventB->endTime().timeSpec(), Qt::LocalTime);
QCOMPARE(eventB->startTime().timeSpec(), Qt::LocalTime);
if (spec == CalendarEvent::SpecClockTime
|| spec == CalendarEvent::SpecUtc) {
QCOMPARE(eventB->endTimeSpec(), spec);
QCOMPARE(eventB->startTimeSpec(), spec);
} else {
QCOMPARE(eventB->endTimeSpec(), CalendarEvent::SpecTimeZone);
QCOMPARE(eventB->startTimeSpec(), CalendarEvent::SpecTimeZone);
QCOMPARE(eventB->endTimeZone().toUtf8(), endTime.timeZone().id());
QCOMPARE(eventB->startTimeZone().toUtf8(), startTime.timeZone().id());
}

calendarApi->remove(uid);
mSavedEvents.remove(uid);

delete eventMod;
}

void tst_CalendarEvent::testRecurrenceException()
{
CalendarEventModification *event = calendarApi->createNewEvent();
Expand Down

0 comments on commit 1ccc5ee

Please sign in to comment.