diff --git a/autotests/kadatetimetest.cpp b/autotests/kadatetimetest.cpp index 4efb12c..1c5e1e5 100644 --- a/autotests/kadatetimetest.cpp +++ b/autotests/kadatetimetest.cpp @@ -1,3667 +1,3671 @@ /* This file is part of kalarmcal library, which provides access to KAlarm calendar data. Copyright (c) 2005,2006,2010,2011,2018 David Jarvie This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kadatetimetest.h" #include "kadatetime.h" using KAlarmCal::KADateTime; #include #include #include #include #include #include #include #include //TODO: test new methods QTEST_GUILESS_MAIN(KADateTimeTest) namespace KAlarmCal { extern KALARMCAL_EXPORT int KADateTime_utcCacheHit; extern KALARMCAL_EXPORT int KADateTime_zoneCacheHit; } //////////////////////////////////////////////////////////////////////// // KADateTime::Spec constructors and basic property information methods, // and the static convenience instances/methods. //////////////////////////////////////////////////////////////////////// void KADateTimeTest::specConstructors() { QTimeZone london("Europe/London"); QTimeZone losAngeles("America/Los_Angeles"); QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":Europe/London"); ::tzset(); // Ensure that local time is different from UTC and different from 'london' qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Default constructor KADateTime::Spec invalid; QVERIFY(!invalid.isValid()); QCOMPARE(invalid.type(), KADateTime::Invalid); QVERIFY(!invalid.isLocalZone()); QVERIFY(!invalid.isUtc()); QVERIFY(!invalid.isOffsetFromUtc()); QCOMPARE(invalid.utcOffset(), 0); QVERIFY(!invalid.timeZone().isValid()); // Time zone KADateTime::Spec tz(london); QVERIFY(tz.isValid()); QCOMPARE(tz.type(), KADateTime::TimeZone); QVERIFY(!tz.isUtc()); QVERIFY(!tz.isOffsetFromUtc()); QVERIFY(!tz.isLocalZone()); QCOMPARE(tz.utcOffset(), 0); QCOMPARE(tz.timeZone(), london); KADateTime::Spec tzLocal(losAngeles); QVERIFY(tzLocal.isValid()); QCOMPARE(tzLocal.type(), KADateTime::TimeZone); QVERIFY(!tzLocal.isUtc()); QVERIFY(!tzLocal.isOffsetFromUtc()); QVERIFY(!tzLocal.isLocalZone()); QCOMPARE(tzLocal.utcOffset(), 0); QCOMPARE(tzLocal.timeZone(), losAngeles); // ... copy constructor KADateTime::Spec tzCopy(tz); QVERIFY(tzCopy.isValid()); QCOMPARE(tzCopy.type(), KADateTime::TimeZone); QVERIFY(!tzCopy.isUtc()); QVERIFY(!tzCopy.isOffsetFromUtc()); QVERIFY(!tzCopy.isLocalZone()); QCOMPARE(tzCopy.utcOffset(), 0); QCOMPARE(tzCopy.timeZone(), london); // Local time zone KADateTime::Spec local(KADateTime::LocalZone); QVERIFY(local.isValid()); QCOMPARE(local.type(), KADateTime::LocalZone); QCOMPARE(local, KADateTime::Spec(KADateTime::LocalZone)); QVERIFY(!local.isUtc()); QVERIFY(!local.isOffsetFromUtc()); QVERIFY(local.isLocalZone()); QCOMPARE(local.utcOffset(), 0); QCOMPARE(local.timeZone(), QTimeZone::systemTimeZone()); KADateTime::Spec localx(KADateTime::Spec(KADateTime::LocalZone, 2 * 3600)); QVERIFY(localx.isValid()); QCOMPARE(localx.type(), KADateTime::LocalZone); QCOMPARE(localx, KADateTime::Spec(KADateTime::LocalZone)); QVERIFY(!localx.isUtc()); QVERIFY(!localx.isOffsetFromUtc()); QVERIFY(localx.isLocalZone()); QCOMPARE(localx.utcOffset(), 0); QCOMPARE(localx.timeZone(), QTimeZone::systemTimeZone()); KADateTime::Spec local2 = KADateTime::Spec::LocalZone(); QVERIFY(local2.isValid()); QCOMPARE(local2.type(), KADateTime::LocalZone); QCOMPARE(local2, KADateTime::Spec(KADateTime::LocalZone)); QVERIFY(!local2.isUtc()); QVERIFY(!local2.isOffsetFromUtc()); QVERIFY(local2.isLocalZone()); QCOMPARE(local2.utcOffset(), 0); QCOMPARE(local2.timeZone(), QTimeZone::systemTimeZone()); // ... copy constructor KADateTime::Spec localCopy(local); QVERIFY(localCopy.isValid()); QCOMPARE(localCopy.type(), KADateTime::LocalZone); QCOMPARE(localCopy, KADateTime::Spec(KADateTime::LocalZone)); QVERIFY(!localCopy.isUtc()); QVERIFY(!localCopy.isOffsetFromUtc()); QVERIFY(localCopy.isLocalZone()); QCOMPARE(localCopy.utcOffset(), 0); QCOMPARE(localCopy.timeZone(), losAngeles); // UTC KADateTime::Spec utc(KADateTime::UTC); QVERIFY(utc.isValid()); QCOMPARE(utc.type(), KADateTime::UTC); QVERIFY(utc.isUtc()); QVERIFY(!utc.isOffsetFromUtc()); QVERIFY(!utc.isLocalZone()); QCOMPARE(utc.utcOffset(), 0); QCOMPARE(utc.timeZone(), QTimeZone::utc()); KADateTime::Spec utcx(KADateTime::UTC, 2 * 3600); QVERIFY(utcx.isValid()); QCOMPARE(utcx.type(), KADateTime::UTC); QVERIFY(utcx.isUtc()); QVERIFY(!utcx.isOffsetFromUtc()); QVERIFY(!utcx.isLocalZone()); QCOMPARE(utcx.utcOffset(), 0); QCOMPARE(utcx.timeZone(), QTimeZone::utc()); const KADateTime::Spec &utc2 = KADateTime::Spec::UTC(); QVERIFY(utc2.isValid()); QCOMPARE(utc2.type(), KADateTime::UTC); QVERIFY(utc2.isUtc()); QVERIFY(!utc2.isOffsetFromUtc()); QVERIFY(!utc2.isLocalZone()); QCOMPARE(utc2.utcOffset(), 0); QCOMPARE(utc2.timeZone(), QTimeZone::utc()); // ... copy constructor KADateTime::Spec utcCopy(utc); QVERIFY(utcCopy.isValid()); QCOMPARE(utcCopy.type(), KADateTime::UTC); QVERIFY(utcCopy.isUtc()); QVERIFY(!utcCopy.isOffsetFromUtc()); QVERIFY(!utcCopy.isLocalZone()); QCOMPARE(utcCopy.utcOffset(), 0); QCOMPARE(utcCopy.timeZone(), QTimeZone::utc()); // Offset from UTC KADateTime::Spec offset0(KADateTime::OffsetFromUTC); QVERIFY(offset0.isValid()); QCOMPARE(offset0.type(), KADateTime::OffsetFromUTC); QVERIFY(offset0.isUtc()); QVERIFY(offset0.isOffsetFromUtc()); QVERIFY(!offset0.isLocalZone()); QCOMPARE(offset0.utcOffset(), 0); QVERIFY(!offset0.timeZone().isValid()); KADateTime::Spec offset(KADateTime::Spec(KADateTime::OffsetFromUTC, -2 * 3600)); QVERIFY(offset.isValid()); QCOMPARE(offset.type(), KADateTime::OffsetFromUTC); QVERIFY(!offset.isUtc()); QVERIFY(offset.isOffsetFromUtc()); QVERIFY(!offset.isLocalZone()); QCOMPARE(offset.utcOffset(), -2 * 3600); QVERIFY(!offset.timeZone().isValid()); KADateTime::Spec offset2 = KADateTime::Spec::OffsetFromUTC(2 * 3600); QVERIFY(offset2.isValid()); QCOMPARE(offset2.type(), KADateTime::OffsetFromUTC); QVERIFY(!offset2.isUtc()); QVERIFY(offset2.isOffsetFromUtc()); QVERIFY(!offset2.isLocalZone()); QCOMPARE(offset2.utcOffset(), 2 * 3600); QVERIFY(!offset2.timeZone().isValid()); // ... copy constructor KADateTime::Spec offsetCopy(offset); QVERIFY(offsetCopy.isValid()); QCOMPARE(offsetCopy.type(), KADateTime::OffsetFromUTC); QVERIFY(!offsetCopy.isUtc()); QVERIFY(offsetCopy.isOffsetFromUtc()); QVERIFY(!offsetCopy.isLocalZone()); QCOMPARE(offsetCopy.utcOffset(), -2 * 3600); QVERIFY(!offsetCopy.timeZone().isValid()); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } //////////////////////////////////////////////////////////////////////// // KADateTime::Spec setType(), operator==(), etc. //////////////////////////////////////////////////////////////////////// void KADateTimeTest::specSet() { QTimeZone london("Europe/London"); QTimeZone losAngeles("America/Los_Angeles"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); KADateTime::Spec spec; QCOMPARE(spec.type(), KADateTime::Invalid); spec.setType(KADateTime::OffsetFromUTC, 7200); QCOMPARE(spec.type(), KADateTime::OffsetFromUTC); QVERIFY(spec.equivalentTo(KADateTime::Spec::OffsetFromUTC(7200))); QVERIFY(!spec.equivalentTo(KADateTime::Spec::OffsetFromUTC(0))); QVERIFY(spec == KADateTime::Spec::OffsetFromUTC(7200)); QVERIFY(!(spec != KADateTime::Spec::OffsetFromUTC(7200))); QVERIFY(spec != KADateTime::Spec::OffsetFromUTC(-7200)); QVERIFY(spec != KADateTime::Spec(london)); spec.setType(KADateTime::OffsetFromUTC, 0); QCOMPARE(spec.type(), KADateTime::OffsetFromUTC); QVERIFY(spec.equivalentTo(KADateTime::Spec::OffsetFromUTC(0))); QVERIFY(spec.equivalentTo(KADateTime::Spec::UTC())); QVERIFY(!spec.equivalentTo(KADateTime::Spec::OffsetFromUTC(7200))); QVERIFY(spec == KADateTime::Spec::OffsetFromUTC(0)); QVERIFY(!(spec != KADateTime::Spec::OffsetFromUTC(0))); QVERIFY(spec != KADateTime::Spec::OffsetFromUTC(-7200)); QVERIFY(spec != KADateTime::Spec(london)); spec.setType(london); QCOMPARE(spec.type(), KADateTime::TimeZone); QVERIFY(spec.equivalentTo(KADateTime::Spec(london))); QVERIFY(spec == KADateTime::Spec(london)); QVERIFY(!(spec != KADateTime::Spec(london))); QVERIFY(spec != KADateTime::Spec::OffsetFromUTC(0)); QVERIFY(!spec.equivalentTo(KADateTime::Spec::OffsetFromUTC(0))); spec.setType(KADateTime::LocalZone); QCOMPARE(spec.type(), KADateTime::LocalZone); QVERIFY(spec.equivalentTo(KADateTime::Spec::LocalZone())); QVERIFY(spec == KADateTime::Spec::LocalZone()); QVERIFY(!(spec != KADateTime::Spec::LocalZone())); QVERIFY(spec.equivalentTo(KADateTime::Spec(losAngeles))); QVERIFY(spec != KADateTime::Spec(losAngeles)); QVERIFY(spec != KADateTime::Spec(london)); QVERIFY(!spec.equivalentTo(KADateTime::Spec(london))); spec.setType(KADateTime::UTC); QCOMPARE(spec.type(), KADateTime::UTC); QVERIFY(spec.equivalentTo(KADateTime::Spec::UTC())); QVERIFY(spec == KADateTime::Spec::UTC()); QVERIFY(!(spec != KADateTime::Spec::UTC())); QVERIFY(spec != KADateTime::Spec::LocalZone()); QVERIFY(!spec.equivalentTo(KADateTime::Spec::LocalZone())); QVERIFY(spec.equivalentTo(KADateTime::Spec::OffsetFromUTC(0))); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } ////////////////////////////////////////////////////// // Constructors and basic property information methods ////////////////////////////////////////////////////// void KADateTimeTest::constructors() { QDate d(2001, 2, 13); QTime t(3, 45, 14); QDateTime dtLocal(d, t, Qt::LocalTime); QDateTime dtUTC(d, t, Qt::UTC); QDateTime dtLocal2(QDate(2016,3,27), QTime(1,0,0)); //Europe/London daylight savings change time QTimeZone london("Europe/London"); QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":Europe/London"); ::tzset(); QDateTime dtUTCtoLondon = dtUTC.toLocalTime(); // Ensure that local time is different from UTC and different from 'london' qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Default constructor KADateTime deflt; QVERIFY(deflt.isNull()); QVERIFY(!deflt.isValid()); // No time zone or timeSpec explicitly specified KADateTime datetimeL(dtLocal); QVERIFY(!datetimeL.isNull()); QVERIFY(datetimeL.isValid()); QVERIFY(!datetimeL.isDateOnly()); QCOMPARE(datetimeL.timeType(), KADateTime::LocalZone); QCOMPARE(datetimeL.timeSpec(), KADateTime::Spec::LocalZone()); QVERIFY(datetimeL.isLocalZone()); QVERIFY(!datetimeL.isUtc()); QVERIFY(!datetimeL.isOffsetFromUtc()); QCOMPARE(datetimeL.utcOffset(), -8 * 3600); QCOMPARE(datetimeL.timeZone(), QTimeZone::systemTimeZone()); QCOMPARE(datetimeL.date(), dtLocal.date()); QCOMPARE(datetimeL.time(), dtLocal.time()); QCOMPARE(datetimeL.qDateTime(), dtLocal); KADateTime datetimeU(dtUTC); QVERIFY(!datetimeU.isNull()); QVERIFY(datetimeU.isValid()); QVERIFY(!datetimeU.isDateOnly()); QCOMPARE(datetimeU.timeType(), KADateTime::UTC); QVERIFY(!datetimeU.isLocalZone()); QVERIFY(datetimeU.isUtc()); QVERIFY(!datetimeU.isOffsetFromUtc()); QCOMPARE(datetimeU.utcOffset(), 0); QCOMPARE(datetimeU.timeZone(), QTimeZone::utc()); QCOMPARE(datetimeU.date(), dtUTC.date()); QCOMPARE(datetimeU.time(), dtUTC.time()); QCOMPARE(datetimeU.qDateTime(), dtUTC); // Time zone KADateTime dateTz(d, london); QVERIFY(!dateTz.isNull()); QVERIFY(dateTz.isValid()); QVERIFY(dateTz.isDateOnly()); QCOMPARE(dateTz.timeType(), KADateTime::TimeZone); QVERIFY(!dateTz.isUtc()); QVERIFY(!dateTz.isOffsetFromUtc()); QVERIFY(!dateTz.isLocalZone()); QCOMPARE(dateTz.utcOffset(), 0); QCOMPARE(dateTz.timeZone(), london); QCOMPARE(dateTz.date(), d); QCOMPARE(dateTz.time(), QTime(0, 0, 0)); QCOMPARE(dateTz.qDateTime(), QDateTime(d, QTime(0, 0, 0), london)); KADateTime dateTimeTz(d, QTime(3, 45, 14), london); QVERIFY(!dateTimeTz.isNull()); QVERIFY(dateTimeTz.isValid()); QVERIFY(!dateTimeTz.isDateOnly()); QCOMPARE(dateTimeTz.timeType(), KADateTime::TimeZone); QVERIFY(!dateTimeTz.isUtc()); QVERIFY(!dateTimeTz.isOffsetFromUtc()); QVERIFY(!dateTimeTz.isLocalZone()); QCOMPARE(dateTimeTz.utcOffset(), 0); QCOMPARE(dateTimeTz.timeZone(), london); QCOMPARE(dateTimeTz.date(), d); QCOMPARE(dateTimeTz.time(), QTime(3, 45, 14)); QCOMPARE(dateTimeTz.qDateTime(), QDateTime(d, QTime(3, 45, 14), london)); KADateTime datetimeTz(dtLocal, london); QVERIFY(!datetimeTz.isNull()); QVERIFY(datetimeTz.isValid()); QVERIFY(!dateTimeTz.isDateOnly()); QCOMPARE(datetimeTz.timeType(), KADateTime::TimeZone); QVERIFY(!datetimeTz.isUtc()); QVERIFY(!datetimeTz.isOffsetFromUtc()); QVERIFY(!datetimeTz.isLocalZone()); QCOMPARE(datetimeTz.utcOffset(), 0); QCOMPARE(datetimeTz.timeZone(), london); QCOMPARE(datetimeTz.date(), dtLocal.date()); QCOMPARE(datetimeTz.time(), QTime(11, 45, 14)); QCOMPARE(datetimeTz.qDateTime(), dtLocal.toTimeZone(london)); KADateTime datetimeTz2(dtUTC, london); QVERIFY(!datetimeTz2.isNull()); QVERIFY(datetimeTz2.isValid()); QVERIFY(!dateTimeTz.isDateOnly()); QCOMPARE(datetimeTz2.timeType(), KADateTime::TimeZone); QVERIFY(!datetimeTz2.isUtc()); QVERIFY(!datetimeTz2.isOffsetFromUtc()); QVERIFY(!datetimeTz2.isLocalZone()); QCOMPARE(datetimeTz2.utcOffset(), 0); QCOMPARE(datetimeTz2.timeZone(), london); QCOMPARE(datetimeTz2.date(), dtUTCtoLondon.date()); QCOMPARE(datetimeTz2.time(), dtUTCtoLondon.time()); QCOMPARE(datetimeTz2.qDateTime(), dtUTC); // ... copy constructor KADateTime datetimeTzCopy(datetimeTz); QVERIFY(!datetimeTzCopy.isNull()); QVERIFY(datetimeTzCopy.isValid()); QVERIFY(!dateTimeTz.isDateOnly()); QCOMPARE(datetimeTzCopy.timeType(), KADateTime::TimeZone); QVERIFY(!datetimeTzCopy.isUtc()); QVERIFY(!datetimeTzCopy.isOffsetFromUtc()); QVERIFY(!datetimeTzCopy.isLocalZone()); QCOMPARE(datetimeTzCopy.utcOffset(), 0); QCOMPARE(datetimeTzCopy.timeZone(), datetimeTz.timeZone()); QCOMPARE(datetimeTzCopy.date(), datetimeTz.date()); QCOMPARE(datetimeTzCopy.time(), datetimeTz.time()); QCOMPARE(datetimeTzCopy.qDateTime(), datetimeTz.qDateTime()); // UTC KADateTime date_UTC(d, KADateTime::Spec::UTC()); QVERIFY(!date_UTC.isNull()); QVERIFY(date_UTC.isValid()); QVERIFY(date_UTC.isDateOnly()); QCOMPARE(date_UTC.timeType(), KADateTime::UTC); QVERIFY(date_UTC.isUtc()); QVERIFY(!date_UTC.isOffsetFromUtc()); QVERIFY(!date_UTC.isLocalZone()); QCOMPARE(date_UTC.utcOffset(), 0); QCOMPARE(date_UTC.timeZone(), QTimeZone::utc()); QCOMPARE(date_UTC.date(), d); QCOMPARE(date_UTC.time(), QTime(0, 0, 0)); QCOMPARE(date_UTC.qDateTime(), QDateTime(d, QTime(0, 0, 0), Qt::UTC)); KADateTime dateTime_UTC(d, t, KADateTime::UTC); QVERIFY(!dateTime_UTC.isNull()); QVERIFY(dateTime_UTC.isValid()); QVERIFY(!dateTime_UTC.isDateOnly()); QCOMPARE(dateTime_UTC.timeType(), KADateTime::UTC); QVERIFY(dateTime_UTC.isUtc()); QVERIFY(!dateTime_UTC.isOffsetFromUtc()); QVERIFY(!dateTime_UTC.isLocalZone()); QCOMPARE(dateTime_UTC.utcOffset(), 0); QCOMPARE(dateTime_UTC.timeZone(), QTimeZone::utc()); QCOMPARE(dateTime_UTC.date(), d); QCOMPARE(dateTime_UTC.time(), t); QCOMPARE(dateTime_UTC.qDateTime(), QDateTime(d, t, Qt::UTC)); KADateTime datetime_UTC(dtLocal, KADateTime::UTC); QVERIFY(!datetime_UTC.isNull()); QVERIFY(datetime_UTC.isValid()); QVERIFY(!datetime_UTC.isDateOnly()); QCOMPARE(datetime_UTC.timeType(), KADateTime::UTC); QVERIFY(datetime_UTC.isUtc()); QVERIFY(!datetime_UTC.isOffsetFromUtc()); QVERIFY(!datetime_UTC.isLocalZone()); QCOMPARE(datetime_UTC.utcOffset(), 0); QCOMPARE(datetime_UTC.timeZone(), QTimeZone::utc()); { QDateTime utc = dtLocal.toUTC(); QCOMPARE(datetime_UTC.date(), utc.date()); QCOMPARE(datetime_UTC.time(), utc.time()); QCOMPARE(datetime_UTC.qDateTime(), utc); } KADateTime datetime_UTC2(dtUTC, KADateTime::UTC); QVERIFY(!datetime_UTC2.isNull()); QVERIFY(datetime_UTC2.isValid()); QVERIFY(!datetime_UTC2.isDateOnly()); QCOMPARE(datetime_UTC2.timeType(), KADateTime::UTC); QVERIFY(datetime_UTC2.isUtc()); QVERIFY(!datetime_UTC2.isOffsetFromUtc()); QVERIFY(!datetime_UTC2.isLocalZone()); QCOMPARE(datetime_UTC2.utcOffset(), 0); QCOMPARE(datetime_UTC2.timeZone(), QTimeZone::utc()); QCOMPARE(datetime_UTC2.date(), dtUTC.date()); QCOMPARE(datetime_UTC2.time(), dtUTC.time()); QCOMPARE(datetime_UTC2.qDateTime(), dtUTC); // ... copy constructor KADateTime datetime_UTCCopy(datetime_UTC); QVERIFY(!datetime_UTCCopy.isNull()); QVERIFY(datetime_UTCCopy.isValid()); QVERIFY(!datetime_UTCCopy.isDateOnly()); QCOMPARE(datetime_UTCCopy.timeType(), KADateTime::UTC); QVERIFY(datetime_UTCCopy.isUtc()); QVERIFY(!datetime_UTCCopy.isOffsetFromUtc()); QVERIFY(!datetime_UTCCopy.isLocalZone()); QCOMPARE(datetime_UTCCopy.utcOffset(), 0); QCOMPARE(datetime_UTCCopy.timeZone(), datetime_UTC.timeZone()); QCOMPARE(datetime_UTCCopy.date(), datetime_UTC.date()); QCOMPARE(datetime_UTCCopy.time(), datetime_UTC.time()); QCOMPARE(datetime_UTCCopy.qDateTime(), datetime_UTC.qDateTime()); // Offset from UTC KADateTime date_OffsetFromUTC(d, KADateTime::Spec::OffsetFromUTC(-2 * 3600)); QVERIFY(!date_OffsetFromUTC.isNull()); QVERIFY(date_OffsetFromUTC.isValid()); QVERIFY(date_OffsetFromUTC.isDateOnly()); QCOMPARE(date_OffsetFromUTC.timeType(), KADateTime::OffsetFromUTC); QVERIFY(!date_OffsetFromUTC.isUtc()); QVERIFY(date_OffsetFromUTC.isOffsetFromUtc()); QVERIFY(!date_OffsetFromUTC.isLocalZone()); QCOMPARE(date_OffsetFromUTC.utcOffset(), -2 * 3600); QVERIFY(!date_OffsetFromUTC.timeZone().isValid()); QCOMPARE(date_OffsetFromUTC.date(), d); QCOMPARE(date_OffsetFromUTC.time(), QTime(0, 0, 0)); QCOMPARE(date_OffsetFromUTC.qDateTime(), QDateTime(d, QTime(0, 0, 0), Qt::OffsetFromUTC, -2 * 3600)); KADateTime dateTime_OffsetFromUTC(d, t, KADateTime::Spec::OffsetFromUTC(2 * 3600)); QVERIFY(!dateTime_OffsetFromUTC.isNull()); QVERIFY(dateTime_OffsetFromUTC.isValid()); QVERIFY(!dateTime_OffsetFromUTC.isDateOnly()); QCOMPARE(dateTime_OffsetFromUTC.timeType(), KADateTime::OffsetFromUTC); QVERIFY(!dateTime_OffsetFromUTC.isUtc()); QVERIFY(dateTime_OffsetFromUTC.isOffsetFromUtc()); QVERIFY(!dateTime_OffsetFromUTC.isLocalZone()); QCOMPARE(dateTime_OffsetFromUTC.utcOffset(), 2 * 3600); QVERIFY(!dateTime_OffsetFromUTC.timeZone().isValid()); QCOMPARE(dateTime_OffsetFromUTC.date(), d); QCOMPARE(dateTime_OffsetFromUTC.time(), t); QCOMPARE(dateTime_OffsetFromUTC.qDateTime(), QDateTime(d, t, Qt::OffsetFromUTC, 2 * 3600)); KADateTime datetime_OffsetFromUTC(dtLocal, KADateTime::Spec::OffsetFromUTC(-2 * 3600)); QVERIFY(!datetime_OffsetFromUTC.isNull()); QVERIFY(datetime_OffsetFromUTC.isValid()); QVERIFY(!datetime_OffsetFromUTC.isDateOnly()); QCOMPARE(datetime_OffsetFromUTC.timeType(), KADateTime::OffsetFromUTC); QVERIFY(!datetime_OffsetFromUTC.isUtc()); QVERIFY(datetime_OffsetFromUTC.isOffsetFromUtc()); QVERIFY(!datetime_OffsetFromUTC.isLocalZone()); QCOMPARE(datetime_OffsetFromUTC.utcOffset(), -2 * 3600); QVERIFY(!datetime_OffsetFromUTC.timeZone().isValid()); QCOMPARE(datetime_OffsetFromUTC.date(), dtLocal.date()); QCOMPARE(datetime_OffsetFromUTC.time(), dtLocal.time().addSecs(6 * 3600)); QCOMPARE(datetime_OffsetFromUTC.qDateTime(), dtLocal.toOffsetFromUtc(-2 * 3600)); KADateTime datetime_OffsetFromUTC2(dtUTC, KADateTime::Spec::OffsetFromUTC(2 * 3600)); QVERIFY(!datetime_OffsetFromUTC2.isNull()); QVERIFY(datetime_OffsetFromUTC2.isValid()); QVERIFY(!datetime_OffsetFromUTC2.isDateOnly()); QCOMPARE(datetime_OffsetFromUTC2.timeType(), KADateTime::OffsetFromUTC); QVERIFY(!datetime_OffsetFromUTC2.isUtc()); QVERIFY(datetime_OffsetFromUTC2.isOffsetFromUtc()); QVERIFY(!datetime_OffsetFromUTC2.isLocalZone()); QCOMPARE(datetime_OffsetFromUTC2.utcOffset(), 2 * 3600); QVERIFY(!datetime_OffsetFromUTC2.timeZone().isValid()); { QDateTime dtof = dtUTC.addSecs(2 * 3600); dtof.setTimeSpec(Qt::LocalTime); QCOMPARE(datetime_OffsetFromUTC2.date(), dtof.date()); QCOMPARE(datetime_OffsetFromUTC2.time(), dtof.time()); } QCOMPARE(datetime_OffsetFromUTC2.qDateTime(), dtUTC.toOffsetFromUtc(2 * 3600)); // ... copy constructor KADateTime datetime_OffsetFromUTCCopy(datetime_OffsetFromUTC); QVERIFY(!datetime_OffsetFromUTCCopy.isNull()); QVERIFY(datetime_OffsetFromUTCCopy.isValid()); QVERIFY(!datetime_OffsetFromUTCCopy.isDateOnly()); QCOMPARE(datetime_OffsetFromUTCCopy.timeType(), KADateTime::OffsetFromUTC); QVERIFY(!datetime_OffsetFromUTCCopy.isUtc()); QVERIFY(datetime_OffsetFromUTCCopy.isOffsetFromUtc()); QVERIFY(!datetime_OffsetFromUTCCopy.isLocalZone()); QCOMPARE(datetime_OffsetFromUTCCopy.utcOffset(), -2 * 3600); QVERIFY(!datetime_OffsetFromUTCCopy.timeZone().isValid()); QCOMPARE(datetime_OffsetFromUTCCopy.date(), datetime_OffsetFromUTC.date()); QCOMPARE(datetime_OffsetFromUTCCopy.time(), datetime_OffsetFromUTC.time()); QCOMPARE(datetime_OffsetFromUTCCopy.qDateTime(), datetime_OffsetFromUTC.qDateTime()); // Local time zone KADateTime date_LocalZone(d, KADateTime::Spec::LocalZone()); QVERIFY(!date_LocalZone.isNull()); QVERIFY(date_LocalZone.isValid()); QVERIFY(date_LocalZone.isDateOnly()); QCOMPARE(date_LocalZone.timeType(), KADateTime::LocalZone); QCOMPARE(date_LocalZone.timeSpec(), KADateTime::Spec::LocalZone()); QVERIFY(!date_LocalZone.isUtc()); QVERIFY(!date_LocalZone.isOffsetFromUtc()); QVERIFY(date_LocalZone.isLocalZone()); QCOMPARE(date_LocalZone.utcOffset(), -8 * 3600); QCOMPARE(date_LocalZone.timeZone(), QTimeZone::systemTimeZone()); QCOMPARE(date_LocalZone.date(), d); QCOMPARE(date_LocalZone.time(), QTime(0, 0, 0)); QCOMPARE(date_LocalZone.qDateTime(), QDateTime(d, QTime(0, 0, 0), Qt::LocalTime)); KADateTime dateTime_LocalZone(d, t, KADateTime::LocalZone); QVERIFY(!dateTime_LocalZone.isNull()); QVERIFY(dateTime_LocalZone.isValid()); QVERIFY(!dateTime_LocalZone.isDateOnly()); QCOMPARE(dateTime_LocalZone.timeType(), KADateTime::LocalZone); QCOMPARE(dateTime_LocalZone.timeSpec(), KADateTime::Spec::LocalZone()); QVERIFY(!dateTime_LocalZone.isUtc()); QVERIFY(!dateTime_LocalZone.isOffsetFromUtc()); QVERIFY(dateTime_LocalZone.isLocalZone()); QCOMPARE(dateTime_LocalZone.utcOffset(), -8 * 3600); QCOMPARE(dateTime_LocalZone.timeZone(), QTimeZone::systemTimeZone()); QCOMPARE(dateTime_LocalZone.date(), d); QCOMPARE(dateTime_LocalZone.time(), t); QCOMPARE(dateTime_LocalZone.qDateTime(), QDateTime(d, t, Qt::LocalTime)); KADateTime datetime_LocalZone(dtLocal, KADateTime::LocalZone); QVERIFY(!datetime_LocalZone.isNull()); QVERIFY(datetime_LocalZone.isValid()); QVERIFY(!datetime_LocalZone.isDateOnly()); QCOMPARE(datetime_LocalZone.timeType(), KADateTime::LocalZone); QCOMPARE(datetime_LocalZone.timeSpec(), KADateTime::Spec::LocalZone()); QVERIFY(!datetime_LocalZone.isUtc()); QVERIFY(!datetime_LocalZone.isOffsetFromUtc()); QVERIFY(datetime_LocalZone.isLocalZone()); QCOMPARE(datetime_LocalZone.utcOffset(), -8 * 3600); QCOMPARE(datetime_LocalZone.timeZone(), QTimeZone::systemTimeZone()); QCOMPARE(datetime_LocalZone.date(), dtLocal.date()); QCOMPARE(datetime_LocalZone.time(), dtLocal.time()); QCOMPARE(datetime_LocalZone.qDateTime(), dtLocal); KADateTime datetime_LocalZone2(dtUTC, KADateTime::LocalZone); QVERIFY(!datetime_LocalZone2.isNull()); QVERIFY(datetime_LocalZone2.isValid()); QVERIFY(!datetime_LocalZone2.isDateOnly()); QCOMPARE(datetime_LocalZone2.timeType(), KADateTime::LocalZone); QCOMPARE(datetime_LocalZone2.timeSpec(), KADateTime::Spec::LocalZone()); QVERIFY(!datetime_LocalZone2.isUtc()); QVERIFY(!datetime_LocalZone2.isOffsetFromUtc()); QVERIFY(datetime_LocalZone2.isLocalZone()); QCOMPARE(datetime_LocalZone2.utcOffset(), -8 * 3600); QCOMPARE(datetime_LocalZone2.timeZone(), QTimeZone::systemTimeZone()); { QDateTime local = dtUTC.toLocalTime(); QCOMPARE(datetime_LocalZone2.date(), local.date()); QCOMPARE(datetime_LocalZone2.time(), local.time()); } QCOMPARE(datetime_LocalZone2.qDateTime(), dtUTC.toLocalTime()); // ... copy constructor KADateTime datetime_LocalZoneCopy(datetime_LocalZone); QVERIFY(!datetime_LocalZoneCopy.isNull()); QVERIFY(datetime_LocalZoneCopy.isValid()); QVERIFY(!datetime_LocalZoneCopy.isDateOnly()); QCOMPARE(datetime_LocalZoneCopy.timeType(), KADateTime::LocalZone); QCOMPARE(datetime_LocalZoneCopy.timeSpec(), KADateTime::Spec::LocalZone()); QVERIFY(!datetime_LocalZoneCopy.isUtc()); QVERIFY(!datetime_LocalZoneCopy.isOffsetFromUtc()); QVERIFY(datetime_LocalZoneCopy.isLocalZone()); QCOMPARE(datetime_LocalZoneCopy.utcOffset(), -8 * 3600); QCOMPARE(datetime_LocalZoneCopy.timeZone(), datetime_LocalZone.timeZone()); QCOMPARE(datetime_LocalZoneCopy.date(), datetime_LocalZone.date()); QCOMPARE(datetime_LocalZoneCopy.time(), datetime_LocalZone.time()); QCOMPARE(datetime_LocalZoneCopy.qDateTime(), datetime_LocalZone.qDateTime()); // Invalid time zone specification for a constructor KADateTime date_TimeZone(d, KADateTime::Spec(KADateTime::TimeZone)); QVERIFY(!date_TimeZone.isValid()); KADateTime dateTime_TimeZone(d, t, KADateTime::Spec(KADateTime::TimeZone)); QVERIFY(!dateTime_TimeZone.isValid()); KADateTime datetime_TimeZone(dtLocal, KADateTime::Spec(KADateTime::TimeZone)); QVERIFY(!datetime_TimeZone.isValid()); KADateTime datetime_Invalid(dtLocal, KADateTime::Spec(KADateTime::Invalid)); QVERIFY(!datetime_Invalid.isValid()); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } /////////////////////////////////// // Time conversion and operator==() /////////////////////////////////// void KADateTimeTest::toUtc() { QTimeZone london("Europe/London"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Zone -> UTC KADateTime londonWinter(QDate(2005, 1, 1), QTime(0, 0, 0), london); KADateTime utcWinter = londonWinter.toUtc(); QVERIFY(utcWinter.isUtc()); QCOMPARE(utcWinter.date(), QDate(2005, 1, 1)); QCOMPARE(utcWinter.time(), QTime(0, 0, 0)); QVERIFY(londonWinter == utcWinter); KADateTime londonSummer(QDate(2005, 6, 1), QTime(0, 0, 0), london); KADateTime utcSummer = londonSummer.toUtc(); QVERIFY(utcSummer.isUtc()); QCOMPARE(utcSummer.date(), QDate(2005, 5, 31)); QCOMPARE(utcSummer.time(), QTime(23, 0, 0)); QVERIFY(londonSummer == utcSummer); QVERIFY(!(londonSummer == utcWinter)); QVERIFY(!(londonWinter == utcSummer)); // UTC offset -> UTC KADateTime offset(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 KADateTime utcOffset = offset.toUtc(); QVERIFY(utcOffset.isUtc()); QCOMPARE(utcOffset.date(), QDate(2005, 6, 6)); QCOMPARE(utcOffset.time(), QTime(2, 32, 30)); QVERIFY(offset == utcOffset); QVERIFY(!(offset == utcSummer)); // Local time -> UTC KADateTime localz(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::LocalZone); KADateTime utcLocalz = localz.toUtc(); QVERIFY(utcLocalz.isUtc()); QCOMPARE(utcLocalz.date(), QDate(2005, 6, 6)); QCOMPARE(utcLocalz.time(), QTime(8, 2, 30)); QVERIFY(localz == utcLocalz); QVERIFY(!(localz == utcOffset)); // UTC -> UTC KADateTime utc(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::UTC); KADateTime utcUtc = utc.toUtc(); QVERIFY(utcUtc.isUtc()); QCOMPARE(utcUtc.date(), QDate(2005, 6, 6)); QCOMPARE(utcUtc.time(), QTime(1, 2, 30)); QVERIFY(utc == utcUtc); QVERIFY(!(utc == utcLocalz)); // ** Date only ** // // Zone -> UTC londonSummer.setDateOnly(true); utcSummer = londonSummer.toUtc(); QVERIFY(utcSummer.isDateOnly()); QCOMPARE(utcSummer.date(), QDate(2005, 6, 1)); QCOMPARE(utcSummer.time(), QTime(0, 0, 0)); QVERIFY(utcSummer != londonSummer); QVERIFY(!(utcSummer == londonSummer)); londonWinter.setDateOnly(true); utcWinter = londonWinter.toUtc(); QVERIFY(utcWinter == londonWinter); QVERIFY(!(utcWinter != londonWinter)); // UTC offset -> UTC offset.setDateOnly(true); utcOffset = offset.toUtc(); QVERIFY(utcOffset.isDateOnly()); QCOMPARE(utcOffset.date(), QDate(2005, 6, 6)); QCOMPARE(utcOffset.time(), QTime(0, 0, 0)); QVERIFY(offset != utcOffset); QVERIFY(!(offset == utcOffset)); KADateTime utcOffset1(QDate(2005, 6, 6), KADateTime::Spec::OffsetFromUTC(0)); QVERIFY(utcOffset1 == utcOffset1.toUtc()); // Local time -> UTC localz.setDateOnly(true); utcLocalz = localz.toUtc(); QVERIFY(utcLocalz.isDateOnly()); QCOMPARE(utcLocalz.date(), QDate(2005, 6, 6)); QCOMPARE(utcLocalz.time(), QTime(0, 0, 0)); QVERIFY(localz != utcLocalz); QVERIFY(!(localz == utcLocalz)); // UTC -> UTC utc.setDateOnly(true); utcUtc = utc.toUtc(); QVERIFY(utcUtc.isDateOnly()); QCOMPARE(utcUtc.date(), QDate(2005, 6, 6)); QCOMPARE(utcUtc.time(), QTime(0, 0, 0)); QVERIFY(utc == utcUtc); QVERIFY(!(utc != utcUtc)); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::toOffsetFromUtc() { QTimeZone london("Europe/London"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // ***** toOffsetFromUtc(void) ***** // Zone -> UTC offset KADateTime londonWinter(QDate(2005, 1, 1), QTime(2, 0, 0), london); KADateTime offsetWinter = londonWinter.toOffsetFromUtc(); QVERIFY(offsetWinter.isOffsetFromUtc()); QCOMPARE(offsetWinter.utcOffset(), 0); QCOMPARE(offsetWinter.date(), QDate(2005, 1, 1)); QCOMPARE(offsetWinter.time(), QTime(2, 0, 0)); QVERIFY(londonWinter == offsetWinter); KADateTime londonSummer(QDate(2005, 6, 1), QTime(14, 0, 0), london); KADateTime offsetSummer = londonSummer.toOffsetFromUtc(); QVERIFY(offsetSummer.isOffsetFromUtc()); QCOMPARE(offsetSummer.utcOffset(), 3600); QCOMPARE(offsetSummer.date(), QDate(2005, 6, 1)); QCOMPARE(offsetSummer.time(), QTime(14, 0, 0)); QVERIFY(londonSummer == offsetSummer); QVERIFY(!(londonSummer == offsetWinter)); QVERIFY(!(londonWinter == offsetSummer)); // UTC offset -> UTC offset KADateTime offset(QDate(2005, 6, 6), QTime(11, 2, 30), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 KADateTime offsetOffset = offset.toOffsetFromUtc(); QVERIFY(offsetOffset.isOffsetFromUtc()); QCOMPARE(offsetOffset.utcOffset(), -5400); QCOMPARE(offsetOffset.date(), QDate(2005, 6, 6)); QCOMPARE(offsetOffset.time(), QTime(11, 2, 30)); QVERIFY(offset == offsetOffset); QVERIFY(!(offset == offsetSummer)); // Local time -> UTC offset KADateTime localz(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::LocalZone); KADateTime offsetLocalz = localz.toOffsetFromUtc(); QVERIFY(offsetLocalz.isOffsetFromUtc()); QCOMPARE(offsetLocalz.utcOffset(), -7 * 3600); QCOMPARE(offsetLocalz.date(), QDate(2005, 6, 6)); QCOMPARE(offsetLocalz.time(), QTime(1, 2, 30)); QVERIFY(localz == offsetLocalz); QVERIFY(!(localz == offsetOffset)); // UTC -> UTC offset KADateTime utc(QDate(2005, 6, 6), QTime(11, 2, 30), KADateTime::UTC); KADateTime offsetUtc = utc.toOffsetFromUtc(); QVERIFY(offsetUtc.isOffsetFromUtc()); QCOMPARE(offsetUtc.utcOffset(), 0); QCOMPARE(offsetUtc.date(), QDate(2005, 6, 6)); QCOMPARE(offsetUtc.time(), QTime(11, 2, 30)); QVERIFY(utc == offsetUtc); QVERIFY(!(utc == offsetLocalz)); // ** Date only ** // // Zone -> UTC offset londonSummer.setDateOnly(true); offsetSummer = londonSummer.toOffsetFromUtc(); QVERIFY(offsetSummer.isDateOnly()); QVERIFY(offsetSummer.isOffsetFromUtc()); QCOMPARE(offsetSummer.utcOffset(), 3600); QCOMPARE(offsetSummer.date(), QDate(2005, 6, 1)); QCOMPARE(offsetSummer.time(), QTime(0, 0, 0)); QVERIFY(offsetSummer == londonSummer); QVERIFY(!(offsetSummer != londonSummer)); londonWinter.setDateOnly(true); offsetWinter = londonWinter.toUtc(); QVERIFY(offsetWinter == londonWinter); QVERIFY(!(offsetWinter != londonWinter)); // UTC offset -> UTC offset offset.setDateOnly(true); offsetOffset = offset.toOffsetFromUtc(); QVERIFY(offsetOffset.isDateOnly()); QVERIFY(offsetOffset.isOffsetFromUtc()); QCOMPARE(offsetOffset.utcOffset(), -5400); QCOMPARE(offsetOffset.date(), QDate(2005, 6, 6)); QCOMPARE(offsetOffset.time(), QTime(0, 0, 0)); QVERIFY(offset == offsetOffset); QVERIFY(!(offset != offsetOffset)); // Local time -> UTC offset localz.setDateOnly(true); offsetLocalz = localz.toOffsetFromUtc(); QVERIFY(offsetLocalz.isDateOnly()); QVERIFY(offsetLocalz.isOffsetFromUtc()); QCOMPARE(offsetLocalz.utcOffset(), -7 * 3600); QCOMPARE(offsetLocalz.date(), QDate(2005, 6, 6)); QCOMPARE(offsetLocalz.time(), QTime(0, 0, 0)); QVERIFY(localz == offsetLocalz); QVERIFY(!(localz != offsetLocalz)); // UTC -> UTC offset utc.setDateOnly(true); offsetUtc = utc.toOffsetFromUtc(); QVERIFY(offsetUtc.isDateOnly()); QVERIFY(offsetUtc.isOffsetFromUtc()); QCOMPARE(offsetUtc.utcOffset(), 0); QCOMPARE(offsetUtc.date(), QDate(2005, 6, 6)); QCOMPARE(offsetUtc.time(), QTime(0, 0, 0)); QVERIFY(utc == offsetUtc); QVERIFY(!(utc != offsetUtc)); // ***** toOffsetFromUtc(int utcOffset) ***** // Zone -> UTC offset KADateTime londonWinter2(QDate(2005, 1, 1), QTime(2, 0, 0), london); offsetWinter = londonWinter2.toOffsetFromUtc(5400); // +1H30M QVERIFY(offsetWinter.isOffsetFromUtc()); QCOMPARE(offsetWinter.utcOffset(), 5400); QCOMPARE(offsetWinter.date(), QDate(2005, 1, 1)); QCOMPARE(offsetWinter.time(), QTime(3, 30, 0)); QVERIFY(londonWinter2 == offsetWinter); KADateTime londonSummer2(QDate(2005, 6, 1), QTime(14, 0, 0), london); offsetSummer = londonSummer2.toOffsetFromUtc(5400); QVERIFY(offsetSummer.isOffsetFromUtc()); QCOMPARE(offsetSummer.utcOffset(), 5400); QCOMPARE(offsetSummer.date(), QDate(2005, 6, 1)); QCOMPARE(offsetSummer.time(), QTime(14, 30, 0)); QVERIFY(londonSummer2 == offsetSummer); QVERIFY(!(londonSummer2 == offsetWinter)); QVERIFY(!(londonWinter2 == offsetSummer)); // UTC offset -> UTC offset KADateTime offset2(QDate(2005, 6, 6), QTime(11, 2, 30), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 offsetOffset = offset2.toOffsetFromUtc(3600); QVERIFY(offsetOffset.isOffsetFromUtc()); QCOMPARE(offsetOffset.utcOffset(), 3600); QCOMPARE(offsetOffset.date(), QDate(2005, 6, 6)); QCOMPARE(offsetOffset.time(), QTime(13, 32, 30)); QVERIFY(offset2 == offsetOffset); QVERIFY(!(offset2 == offsetSummer)); // Local time -> UTC offset KADateTime localz2(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::LocalZone); offsetLocalz = localz2.toOffsetFromUtc(0); QVERIFY(offsetLocalz.isOffsetFromUtc()); QCOMPARE(offsetLocalz.utcOffset(), 0); QCOMPARE(offsetLocalz.date(), QDate(2005, 6, 6)); QCOMPARE(offsetLocalz.time(), QTime(8, 2, 30)); QVERIFY(localz2 == offsetLocalz); QVERIFY(!(localz2 == offsetOffset)); // UTC -> UTC offset KADateTime utc2(QDate(2005, 6, 6), QTime(11, 2, 30), KADateTime::UTC); offsetUtc = utc2.toOffsetFromUtc(-3600); QVERIFY(offsetUtc.isOffsetFromUtc()); QCOMPARE(offsetUtc.utcOffset(), -3600); QCOMPARE(offsetUtc.date(), QDate(2005, 6, 6)); QCOMPARE(offsetUtc.time(), QTime(10, 2, 30)); QVERIFY(utc2 == offsetUtc); QVERIFY(!(utc2 == offsetLocalz)); // ** Date only ** // // Zone -> UTC offset londonSummer2.setDateOnly(true); offsetSummer = londonSummer2.toOffsetFromUtc(5400); QVERIFY(offsetSummer.isDateOnly()); QVERIFY(offsetSummer.isOffsetFromUtc()); QCOMPARE(offsetSummer.utcOffset(), 5400); QCOMPARE(offsetSummer.date(), QDate(2005, 6, 1)); QCOMPARE(offsetSummer.time(), QTime(0, 0, 0)); QVERIFY(londonSummer2 != offsetSummer); QVERIFY(!(londonSummer2 == offsetSummer)); QVERIFY(londonSummer2 == KADateTime(QDate(2005, 6, 1), KADateTime::Spec::OffsetFromUTC(3600))); // UTC offset -> UTC offset offset2.setDateOnly(true); offsetOffset = offset2.toOffsetFromUtc(-3600); QVERIFY(offsetOffset.isDateOnly()); QVERIFY(offsetOffset.isOffsetFromUtc()); QCOMPARE(offsetOffset.utcOffset(), -3600); QCOMPARE(offsetOffset.date(), QDate(2005, 6, 6)); QCOMPARE(offsetOffset.time(), QTime(0, 0, 0)); QVERIFY(offset2 != offsetOffset); QVERIFY(!(offset2 == offsetOffset)); // Local time -> UTC offset localz2.setDateOnly(true); offsetLocalz = localz2.toOffsetFromUtc(6 * 3600); QVERIFY(offsetLocalz.isDateOnly()); QVERIFY(offsetLocalz.isOffsetFromUtc()); QCOMPARE(offsetLocalz.utcOffset(), 6 * 3600); QCOMPARE(offsetLocalz.date(), QDate(2005, 6, 6)); QCOMPARE(offsetLocalz.time(), QTime(0, 0, 0)); QVERIFY(localz2 != offsetLocalz); QVERIFY(!(localz2 == offsetLocalz)); QVERIFY(localz == KADateTime(QDate(2005, 6, 6), KADateTime::Spec::OffsetFromUTC(-7 * 3600))); // UTC -> UTC offset utc2.setDateOnly(true); offsetUtc = utc2.toOffsetFromUtc(1800); QVERIFY(offsetUtc.isDateOnly()); QVERIFY(offsetUtc.isOffsetFromUtc()); QCOMPARE(offsetUtc.utcOffset(), 1800); QCOMPARE(offsetUtc.date(), QDate(2005, 6, 6)); QCOMPARE(offsetUtc.time(), QTime(0, 0, 0)); QVERIFY(utc2 != offsetUtc); QVERIFY(!(utc2 == offsetUtc)); QVERIFY(utc2 == KADateTime(QDate(2005, 6, 6), KADateTime::Spec::OffsetFromUTC(0))); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::toLocalZone() { QTimeZone london("Europe/London"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Zone -> LocalZone KADateTime londonWinter(QDate(2005, 1, 1), QTime(0, 0, 0), london); KADateTime locWinter = londonWinter.toLocalZone(); QVERIFY(locWinter.isLocalZone()); QCOMPARE(locWinter.date(), QDate(2004, 12, 31)); QCOMPARE(locWinter.time(), QTime(16, 0, 0)); QVERIFY(londonWinter == locWinter); KADateTime londonSummer(QDate(2005, 6, 1), QTime(0, 0, 0), london); KADateTime locSummer = londonSummer.toLocalZone(); QVERIFY(locSummer.isLocalZone()); QCOMPARE(locSummer.date(), QDate(2005, 5, 31)); QCOMPARE(locSummer.time(), QTime(16, 0, 0)); QVERIFY(londonSummer == locSummer); QVERIFY(!(londonSummer == locWinter)); QVERIFY(!(londonWinter == locSummer)); // UTC offset -> LocalZone KADateTime offset(QDate(2005, 6, 6), QTime(11, 2, 30), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 KADateTime locOffset = offset.toLocalZone(); QVERIFY(locOffset.isLocalZone()); QCOMPARE(locOffset.date(), QDate(2005, 6, 6)); QCOMPARE(locOffset.time(), QTime(5, 32, 30)); QVERIFY(offset == locOffset); QVERIFY(!(offset == locSummer)); // UTC -> LocalZone KADateTime utc(QDate(2005, 6, 6), QTime(11, 2, 30), KADateTime::UTC); KADateTime locUtc = utc.toLocalZone(); QVERIFY(locUtc.isLocalZone()); QCOMPARE(locUtc.date(), QDate(2005, 6, 6)); QCOMPARE(locUtc.time(), QTime(4, 2, 30)); QVERIFY(utc == locUtc); // ** Date only ** // // Zone -> LocalZone londonSummer.setDateOnly(true); locSummer = londonSummer.toLocalZone(); QVERIFY(locSummer.isDateOnly()); QCOMPARE(locSummer.date(), QDate(2005, 6, 1)); QCOMPARE(locSummer.time(), QTime(0, 0, 0)); QVERIFY(londonSummer != locSummer); QVERIFY(!(londonSummer == locSummer)); // UTC offset -> LocalZone offset.setDateOnly(true); locOffset = offset.toLocalZone(); QVERIFY(locOffset.isDateOnly()); QCOMPARE(locOffset.date(), QDate(2005, 6, 6)); QCOMPARE(locOffset.time(), QTime(0, 0, 0)); QVERIFY(offset != locOffset); QVERIFY(!(offset == locOffset)); QVERIFY(locOffset == KADateTime(QDate(2005, 6, 6), KADateTime::Spec::OffsetFromUTC(-7 * 3600))); // UTC -> LocalZone utc.setDateOnly(true); locUtc = utc.toLocalZone(); QVERIFY(locUtc.isDateOnly()); QCOMPARE(locUtc.date(), QDate(2005, 6, 6)); QCOMPARE(locUtc.time(), QTime(0, 0, 0)); QVERIFY(utc != locUtc); QVERIFY(!(utc == locUtc)); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::toZone() { QTimeZone london("Europe/London"); QTimeZone losAngeles("America/Los_Angeles"); QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":Europe/London"); ::tzset(); // Zone -> Zone KADateTime londonWinter(QDate(2005, 1, 1), QTime(0, 0, 0), london); KADateTime locWinter = londonWinter.toZone(losAngeles); QCOMPARE(locWinter.timeZone(), losAngeles); QCOMPARE(locWinter.date(), QDate(2004, 12, 31)); QCOMPARE(locWinter.time(), QTime(16, 0, 0)); QVERIFY(londonWinter == locWinter); KADateTime londonSummer(QDate(2005, 6, 1), QTime(0, 0, 0), london); KADateTime locSummer = londonSummer.toZone(losAngeles); QCOMPARE(locWinter.timeZone(), losAngeles); QCOMPARE(locSummer.date(), QDate(2005, 5, 31)); QCOMPARE(locSummer.time(), QTime(16, 0, 0)); QVERIFY(londonSummer == locSummer); QVERIFY(!(londonSummer == locWinter)); QVERIFY(!(londonWinter == locSummer)); // UTC offset -> Zone KADateTime offset(QDate(2005, 6, 6), QTime(11, 2, 30), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 KADateTime locOffset = offset.toZone(losAngeles); QCOMPARE(locOffset.timeZone(), losAngeles); QCOMPARE(locOffset.date(), QDate(2005, 6, 6)); QCOMPARE(locOffset.time(), QTime(5, 32, 30)); QVERIFY(offset == locOffset); QVERIFY(!(offset == locSummer)); // Local time -> Zone KADateTime localz(QDate(2005, 6, 6), QTime(11, 2, 30), KADateTime::LocalZone); KADateTime locLocalz = localz.toZone(losAngeles); QCOMPARE(locLocalz.timeZone(), losAngeles); QCOMPARE(locLocalz.date(), QDate(2005, 6, 6)); QCOMPARE(locLocalz.time(), QTime(3, 2, 30)); QVERIFY(localz == locLocalz); QVERIFY(!(localz == locOffset)); // UTC -> Zone KADateTime utc(QDate(2005, 6, 6), QTime(11, 2, 30), KADateTime::UTC); KADateTime locUtc = utc.toZone(losAngeles); QCOMPARE(locUtc.timeZone(), losAngeles); QCOMPARE(locUtc.date(), QDate(2005, 6, 6)); QCOMPARE(locUtc.time(), QTime(4, 2, 30)); QVERIFY(utc == locUtc); QVERIFY(!(utc == locLocalz)); // ** Date only ** // // Zone -> Zone londonSummer.setDateOnly(true); locSummer = londonSummer.toZone(losAngeles); QVERIFY(locSummer.isDateOnly()); QCOMPARE(locSummer.date(), QDate(2005, 6, 1)); QCOMPARE(locSummer.time(), QTime(0, 0, 0)); QVERIFY(londonSummer != locSummer); QVERIFY(!(londonSummer == locSummer)); // UTC offset -> Zone offset.setDateOnly(true); locOffset = offset.toZone(losAngeles); QVERIFY(locOffset.isDateOnly()); QCOMPARE(locOffset.date(), QDate(2005, 6, 6)); QCOMPARE(locOffset.time(), QTime(0, 0, 0)); QVERIFY(offset != locOffset); QVERIFY(!(offset == locOffset)); // Local time -> Zone localz.setDateOnly(true); locLocalz = localz.toZone(losAngeles); QVERIFY(locLocalz.isDateOnly()); QCOMPARE(locLocalz.date(), QDate(2005, 6, 6)); QCOMPARE(locLocalz.time(), QTime(0, 0, 0)); QVERIFY(localz != locLocalz); QVERIFY(!(localz == locLocalz)); // UTC -> Zone utc.setDateOnly(true); locUtc = utc.toZone(losAngeles); QVERIFY(locUtc.isDateOnly()); QCOMPARE(locUtc.date(), QDate(2005, 6, 6)); QCOMPARE(locUtc.time(), QTime(0, 0, 0)); QVERIFY(utc != locUtc); QVERIFY(!(utc == locUtc)); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::toTimeSpec() { QTimeZone london("Europe/London"); QTimeZone cairo("Africa/Cairo"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); KADateTime::Spec utcSpec(KADateTime::UTC); KADateTime::Spec cairoSpec(cairo); KADateTime::Spec offset1200Spec(KADateTime::OffsetFromUTC, 1200); KADateTime::Spec localzSpec(KADateTime::LocalZone); KADateTime utc1(QDate(2004, 3, 1), QTime(3, 45, 2), KADateTime::UTC); KADateTime zone1(QDate(2004, 3, 1), QTime(3, 45, 2), cairo); KADateTime offset1(QDate(2004, 3, 1), QTime(3, 45, 2), KADateTime::Spec::OffsetFromUTC(1200)); // +00:20 KADateTime localz1(QDate(2004, 3, 1), QTime(3, 45, 2), KADateTime::LocalZone); KADateTime utc(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::UTC); KADateTime zone(QDate(2005, 7, 1), QTime(2, 0, 0), london); KADateTime offset(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::Spec::OffsetFromUTC(-5400)); // -01:30 KADateTime localz(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::LocalZone); // To UTC KADateTime utcZone = zone.toTimeSpec(utcSpec); QVERIFY(utcZone.isUtc()); QVERIFY(utcZone == KADateTime(QDate(2005, 7, 1), QTime(1, 0, 0), KADateTime::UTC)); QVERIFY(zone.timeSpec() != utcSpec); QVERIFY(utcZone.timeSpec() == utcSpec); KADateTime utcOffset = offset.toTimeSpec(utcSpec); QVERIFY(utcOffset.isUtc()); QVERIFY(utcOffset == KADateTime(QDate(2005, 6, 6), QTime(2, 32, 30), KADateTime::UTC)); QVERIFY(offset.timeSpec() != utcSpec); QVERIFY(utcOffset.timeSpec() == utcSpec); KADateTime utcLocalz = localz.toTimeSpec(utcSpec); QVERIFY(utcLocalz.isUtc()); QVERIFY(utcLocalz == KADateTime(QDate(2005, 6, 6), QTime(8, 2, 30), KADateTime::UTC)); QVERIFY(localz.timeSpec() != utcSpec); QVERIFY(utcZone.timeSpec() == utcSpec); KADateTime utcUtc = utc.toTimeSpec(utcSpec); QVERIFY(utcUtc.isUtc()); QVERIFY(utcUtc == KADateTime(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::UTC)); QVERIFY(utc.timeSpec() == utcSpec); QVERIFY(utcUtc.timeSpec() == utcSpec); // To Zone KADateTime zoneZone = zone.toTimeSpec(cairoSpec); QCOMPARE(zoneZone.timeZone(), cairo); QVERIFY(zoneZone == KADateTime(QDate(2005, 7, 1), QTime(4, 0, 0), cairo)); QVERIFY(zone.timeSpec() != cairoSpec); QVERIFY(zoneZone.timeSpec() == cairoSpec); KADateTime zoneOffset = offset.toTimeSpec(cairoSpec); QCOMPARE(zoneOffset.timeZone(), cairo); QVERIFY(zoneOffset == KADateTime(QDate(2005, 6, 6), QTime(5, 32, 30), cairo)); QVERIFY(offset.timeSpec() != cairoSpec); QVERIFY(zoneOffset.timeSpec() == cairoSpec); KADateTime zoneLocalz = localz.toTimeSpec(cairoSpec); QCOMPARE(zoneLocalz.timeZone(), cairo); QVERIFY(zoneLocalz == KADateTime(QDate(2005, 6, 6), QTime(11, 2, 30), cairo)); QVERIFY(localz.timeSpec() != cairoSpec); QVERIFY(zoneLocalz.timeSpec() == cairoSpec); KADateTime zoneUtc = utc.toTimeSpec(cairoSpec); QCOMPARE(zoneUtc.timeZone(), cairo); QVERIFY(zoneUtc == KADateTime(QDate(2005, 6, 6), QTime(4, 2, 30), cairo)); QVERIFY(utc.timeSpec() != cairoSpec); QVERIFY(zoneUtc.timeSpec() == cairoSpec); // To UTC offset KADateTime offsetZone = zone.toTimeSpec(offset1200Spec); QVERIFY(offsetZone.isOffsetFromUtc()); QCOMPARE(offsetZone.utcOffset(), 1200); QVERIFY(offsetZone == KADateTime(QDate(2005, 7, 1), QTime(1, 20, 0), KADateTime::Spec::OffsetFromUTC(1200))); QVERIFY(zone.timeSpec() != offset1200Spec); QVERIFY(offsetZone.timeSpec() == offset1200Spec); KADateTime offsetOffset = offset.toTimeSpec(offset1200Spec); QVERIFY(offsetOffset.isOffsetFromUtc()); QCOMPARE(offsetZone.utcOffset(), 1200); QVERIFY(offsetOffset == KADateTime(QDate(2005, 6, 6), QTime(2, 52, 30), KADateTime::Spec::OffsetFromUTC(1200))); QVERIFY(offset.timeSpec() != offset1200Spec); QVERIFY(offsetOffset.timeSpec() == offset1200Spec); KADateTime offsetLocalz = localz.toTimeSpec(offset1200Spec); QVERIFY(offsetLocalz.isOffsetFromUtc()); QCOMPARE(offsetZone.utcOffset(), 1200); QVERIFY(offsetLocalz == KADateTime(QDate(2005, 6, 6), QTime(8, 22, 30), KADateTime::Spec::OffsetFromUTC(1200))); QVERIFY(localz.timeSpec() != offset1200Spec); QVERIFY(offsetLocalz.timeSpec() == offset1200Spec); KADateTime offsetUtc = utc.toTimeSpec(offset1200Spec); QVERIFY(offsetUtc.isOffsetFromUtc()); QCOMPARE(offsetZone.utcOffset(), 1200); QVERIFY(offsetUtc == KADateTime(QDate(2005, 6, 6), QTime(1, 22, 30), KADateTime::Spec::OffsetFromUTC(1200))); QVERIFY(utc.timeSpec() != offset1200Spec); QVERIFY(offsetUtc.timeSpec() == offset1200Spec); // To Local time KADateTime localzZone = zone.toTimeSpec(localzSpec); QVERIFY(localzZone.isLocalZone()); QVERIFY(localzZone == KADateTime(QDate(2005, 6, 30), QTime(18, 0, 0), KADateTime::LocalZone)); QVERIFY(zone.timeSpec() != localzSpec); QVERIFY(localzZone.timeSpec() == localzSpec); KADateTime localzOffset = offset.toTimeSpec(localzSpec); QVERIFY(localzOffset.isLocalZone()); QVERIFY(localzOffset == KADateTime(QDate(2005, 6, 5), QTime(19, 32, 30), KADateTime::LocalZone)); QVERIFY(offset.timeSpec() != localzSpec); QVERIFY(localzOffset.timeSpec() == localzSpec); KADateTime localzLocalz = localz.toTimeSpec(localzSpec); QVERIFY(localzLocalz.isLocalZone()); QVERIFY(localzLocalz == KADateTime(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::LocalZone)); QVERIFY(localz.timeSpec() == localzSpec); QVERIFY(localzLocalz.timeSpec() == localzSpec); KADateTime localzUtc = utc.toTimeSpec(localzSpec); QVERIFY(localzUtc.isLocalZone()); QVERIFY(localzUtc == KADateTime(QDate(2005, 6, 5), QTime(18, 2, 30), KADateTime::LocalZone)); QVERIFY(utc.timeSpec() != localzSpec); QVERIFY(localzUtc.timeSpec() == localzSpec); // ** Date only ** // KADateTime zoned(QDate(2005, 7, 1), london); KADateTime offsetd(QDate(2005, 6, 6), KADateTime::Spec::OffsetFromUTC(-5400)); // -01:30 KADateTime localzd(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::LocalZone)); KADateTime utcd(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::UTC)); // To UTC utcZone = zoned.toTimeSpec(utcSpec); QVERIFY(utcZone.isUtc()); QVERIFY(utcZone.isDateOnly()); QVERIFY(utcZone == KADateTime(QDate(2005, 7, 1), KADateTime::Spec(KADateTime::UTC))); QVERIFY(utcZone != zoned); utcOffset = offsetd.toTimeSpec(utcSpec); QVERIFY(utcOffset.isUtc()); QVERIFY(utcOffset.isDateOnly()); QVERIFY(utcOffset == KADateTime(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::UTC))); QVERIFY(utcOffset != offsetd); utcLocalz = localzd.toTimeSpec(utcSpec); QVERIFY(utcLocalz.isUtc()); QVERIFY(utcLocalz.isDateOnly()); QVERIFY(utcLocalz == KADateTime(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::UTC))); QVERIFY(utcLocalz != localzd); utcUtc = utcd.toTimeSpec(utcSpec); QVERIFY(utcUtc.isUtc()); QVERIFY(utcUtc.isDateOnly()); QVERIFY(utcUtc == KADateTime(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::UTC))); QVERIFY(utcUtc == utcd); // To Zone zoneZone = zoned.toTimeSpec(cairoSpec); QVERIFY(zoneZone.isDateOnly()); QCOMPARE(zoneZone.timeZone(), cairo); QVERIFY(zoneZone == KADateTime(QDate(2005, 7, 1), cairo)); QVERIFY(zoneZone != zoned); zoneOffset = offsetd.toTimeSpec(cairoSpec); QVERIFY(zoneOffset.isDateOnly()); QCOMPARE(zoneOffset.timeZone(), cairo); QVERIFY(zoneOffset == KADateTime(QDate(2005, 6, 6), cairo)); QVERIFY(zoneOffset != offsetd); zoneLocalz = localzd.toTimeSpec(cairoSpec); QVERIFY(zoneLocalz.isDateOnly()); QCOMPARE(zoneLocalz.timeZone(), cairo); QVERIFY(zoneLocalz == KADateTime(QDate(2005, 6, 6), cairo)); QVERIFY(zoneLocalz != localzd); zoneUtc = utcd.toTimeSpec(cairoSpec); QVERIFY(zoneUtc.isDateOnly()); QCOMPARE(zoneUtc.timeZone(), cairo); QVERIFY(zoneUtc == KADateTime(QDate(2005, 6, 6), cairo)); QVERIFY(zoneUtc != utcd); // To UTC offset offsetZone = zoned.toTimeSpec(offset1200Spec); QVERIFY(offsetZone.isOffsetFromUtc()); QVERIFY(offsetZone.isDateOnly()); QCOMPARE(offsetZone.utcOffset(), 1200); QVERIFY(offsetZone == KADateTime(QDate(2005, 7, 1), KADateTime::Spec::OffsetFromUTC(1200))); QVERIFY(offsetZone != zoned); offsetOffset = offsetd.toTimeSpec(offset1200Spec); QVERIFY(offsetOffset.isOffsetFromUtc()); QVERIFY(offsetOffset.isDateOnly()); QCOMPARE(offsetZone.utcOffset(), 1200); QVERIFY(offsetOffset == KADateTime(QDate(2005, 6, 6), KADateTime::Spec::OffsetFromUTC(1200))); QVERIFY(offsetOffset != offsetd); offsetLocalz = localzd.toTimeSpec(offset1200Spec); QVERIFY(offsetLocalz.isOffsetFromUtc()); QVERIFY(offsetLocalz.isDateOnly()); QCOMPARE(offsetZone.utcOffset(), 1200); QVERIFY(offsetLocalz == KADateTime(QDate(2005, 6, 6), KADateTime::Spec::OffsetFromUTC(1200))); QVERIFY(offsetLocalz != localzd); offsetUtc = utcd.toTimeSpec(offset1200Spec); QVERIFY(offsetUtc.isOffsetFromUtc()); QVERIFY(offsetUtc.isDateOnly()); QCOMPARE(offsetZone.utcOffset(), 1200); QVERIFY(offsetUtc == KADateTime(QDate(2005, 6, 6), KADateTime::Spec::OffsetFromUTC(1200))); QVERIFY(offsetUtc != utcd); // To Local time localzZone = zoned.toTimeSpec(localzSpec); QVERIFY(localzZone.isLocalZone()); QVERIFY(localzZone.isDateOnly()); QVERIFY(localzZone == KADateTime(QDate(2005, 7, 1), KADateTime::Spec(KADateTime::LocalZone))); QVERIFY(localzZone != zoned); localzOffset = offsetd.toTimeSpec(localzSpec); QVERIFY(localzOffset.isLocalZone()); QVERIFY(localzOffset.isDateOnly()); QVERIFY(localzOffset == KADateTime(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::LocalZone))); QVERIFY(localzOffset != offsetd); localzLocalz = localzd.toTimeSpec(localzSpec); QVERIFY(localzLocalz.isLocalZone()); QVERIFY(localzLocalz.isDateOnly()); QVERIFY(localzLocalz == KADateTime(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::LocalZone))); QVERIFY(localzLocalz == localzd); localzUtc = utcd.toTimeSpec(localzSpec); QVERIFY(localzUtc.isLocalZone()); QVERIFY(localzUtc.isDateOnly()); QVERIFY(localzUtc == KADateTime(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::LocalZone))); QVERIFY(localzUtc != utcd); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } //////////////////////////////////////////////////////////////////////// // Set methods: setDate(), setTime(), setTimeSpec() //////////////////////////////////////////////////////////////////////// void KADateTimeTest::set() { QTimeZone london("Europe/London"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Zone KADateTime zoned(QDate(2005, 6, 1), london); zoned.setDate(QDate(2004, 5, 2)); QVERIFY(zoned.isDateOnly()); QCOMPARE(zoned.date(), QDate(2004, 5, 2)); QCOMPARE(zoned.time(), QTime(0, 0, 0)); zoned.setTime(QTime(12, 13, 14)); QVERIFY(!zoned.isDateOnly()); QCOMPARE(zoned.date(), QDate(2004, 5, 2)); QCOMPARE(zoned.time(), QTime(12, 13, 14)); zoned.setDate(QDate(2004, 5, 4)); QVERIFY(!zoned.isDateOnly()); zoned.setDateOnly(false); QVERIFY(!zoned.isDateOnly()); QCOMPARE(zoned.date(), QDate(2004, 5, 4)); QCOMPARE(zoned.time(), QTime(12, 13, 14)); zoned.setDateOnly(true); QVERIFY(zoned.isDateOnly()); QCOMPARE(zoned.date(), QDate(2004, 5, 4)); QCOMPARE(zoned.time(), QTime(0, 0, 0)); zoned.setDateOnly(false); QVERIFY(!zoned.isDateOnly()); QCOMPARE(zoned.date(), QDate(2004, 5, 4)); QCOMPARE(zoned.time(), QTime(0, 0, 0)); KADateTime zone(QDate(2005, 6, 1), QTime(3, 40, 0), london); zone.setDate(QDate(2004, 5, 2)); QCOMPARE(zone.date(), QDate(2004, 5, 2)); QCOMPARE(zone.time(), QTime(3, 40, 0)); zone.setTime(QTime(12, 13, 14)); QCOMPARE(zone.date(), QDate(2004, 5, 2)); QCOMPARE(zone.time(), QTime(12, 13, 14)); zone.setDate(QDate(2003,6,10)); zone.setTime(QTime(5,6,7)); QCOMPARE(zone.date(), QDate(2003, 6, 10)); QCOMPARE(zone.time(), QTime(5, 6, 7)); QCOMPARE(zone.utcOffset(), 3600); QCOMPARE(zone.toUtc().date(), QDate(2003, 6, 10)); QCOMPARE(zone.toUtc().time(), QTime(4, 6, 7)); // UTC offset KADateTime offsetd(QDate(2005, 6, 6), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 offsetd.setDate(QDate(2004, 5, 2)); QVERIFY(offsetd.isDateOnly()); QCOMPARE(offsetd.date(), QDate(2004, 5, 2)); QCOMPARE(offsetd.time(), QTime(0, 0, 0)); offsetd.setTime(QTime(12, 13, 14)); QVERIFY(!offsetd.isDateOnly()); QCOMPARE(offsetd.date(), QDate(2004, 5, 2)); QCOMPARE(offsetd.time(), QTime(12, 13, 14)); offsetd.setDate(QDate(2004, 5, 4)); QVERIFY(!offsetd.isDateOnly()); offsetd.setDateOnly(false); QVERIFY(!offsetd.isDateOnly()); QCOMPARE(offsetd.date(), QDate(2004, 5, 4)); QCOMPARE(offsetd.time(), QTime(12, 13, 14)); offsetd.setDateOnly(true); QVERIFY(offsetd.isDateOnly()); QCOMPARE(offsetd.date(), QDate(2004, 5, 4)); QCOMPARE(offsetd.time(), QTime(0, 0, 0)); offsetd.setDateOnly(false); QVERIFY(!offsetd.isDateOnly()); QCOMPARE(offsetd.date(), QDate(2004, 5, 4)); QCOMPARE(offsetd.time(), QTime(0, 0, 0)); KADateTime offset(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 offset.setDate(QDate(2004, 5, 2)); QCOMPARE(offset.date(), QDate(2004, 5, 2)); QCOMPARE(offset.time(), QTime(1, 2, 30)); offset.setTime(QTime(12, 13, 14)); QCOMPARE(offset.date(), QDate(2004, 5, 2)); QCOMPARE(offset.time(), QTime(12, 13, 14)); offset.setDate(QDate(2003, 12, 10)); offset.setTime(QTime(5, 6, 7)); QCOMPARE(offset.date(), QDate(2003, 12, 10)); QCOMPARE(offset.time(), QTime(5, 6, 7)); QCOMPARE(offset.utcOffset(), -5400); QCOMPARE(offset.toUtc().date(), QDate(2003, 12, 10)); QCOMPARE(offset.toUtc().time(), QTime(6, 36, 7)); // Local time KADateTime localzd(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::LocalZone)); localzd.setDate(QDate(2004, 5, 2)); QVERIFY(localzd.isDateOnly()); QCOMPARE(localzd.date(), QDate(2004, 5, 2)); QCOMPARE(localzd.time(), QTime(0, 0, 0)); localzd.setTime(QTime(12, 13, 14)); QVERIFY(!localzd.isDateOnly()); QCOMPARE(localzd.date(), QDate(2004, 5, 2)); QCOMPARE(localzd.time(), QTime(12, 13, 14)); localzd.setDate(QDate(2004, 5, 4)); QVERIFY(!localzd.isDateOnly()); localzd.setDateOnly(false); QVERIFY(!localzd.isDateOnly()); QCOMPARE(localzd.date(), QDate(2004, 5, 4)); QCOMPARE(localzd.time(), QTime(12, 13, 14)); localzd.setDateOnly(true); QVERIFY(localzd.isDateOnly()); QCOMPARE(localzd.date(), QDate(2004, 5, 4)); QCOMPARE(localzd.time(), QTime(0, 0, 0)); localzd.setDateOnly(false); QVERIFY(!localzd.isDateOnly()); QCOMPARE(localzd.date(), QDate(2004, 5, 4)); QCOMPARE(localzd.time(), QTime(0, 0, 0)); KADateTime localz(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::LocalZone); localz.setDate(QDate(2004, 5, 2)); QCOMPARE(localz.date(), QDate(2004, 5, 2)); QCOMPARE(localz.time(), QTime(1, 2, 30)); localz.setTime(QTime(12, 13, 14)); QCOMPARE(localz.date(), QDate(2004, 5, 2)); QCOMPARE(localz.time(), QTime(12, 13, 14)); localz.setDate(QDate(2003, 12, 10)); localz.setTime(QTime(5, 6, 7)); QCOMPARE(localz.date(), QDate(2003, 12, 10)); QCOMPARE(localz.time(), QTime(5, 6, 7)); QCOMPARE(localz.utcOffset(), -8 * 3600); QCOMPARE(localz.toUtc().date(), QDate(2003, 12, 10)); QCOMPARE(localz.toUtc().time(), QTime(13, 6, 7)); // UTC KADateTime utcd(QDate(2005, 6, 6), KADateTime::Spec(KADateTime::UTC)); utcd.setDate(QDate(2004, 5, 2)); QVERIFY(utcd.isDateOnly()); QCOMPARE(utcd.date(), QDate(2004, 5, 2)); QCOMPARE(utcd.time(), QTime(0, 0, 0)); utcd.setTime(QTime(12, 13, 14)); QVERIFY(!utcd.isDateOnly()); QCOMPARE(utcd.date(), QDate(2004, 5, 2)); QCOMPARE(utcd.time(), QTime(12, 13, 14)); utcd.setDate(QDate(2004, 5, 4)); QVERIFY(!utcd.isDateOnly()); utcd.setDateOnly(false); QVERIFY(!utcd.isDateOnly()); QCOMPARE(utcd.date(), QDate(2004, 5, 4)); QCOMPARE(utcd.time(), QTime(12, 13, 14)); utcd.setDateOnly(true); QVERIFY(utcd.isDateOnly()); QCOMPARE(utcd.date(), QDate(2004, 5, 4)); QCOMPARE(utcd.time(), QTime(0, 0, 0)); utcd.setDateOnly(false); QVERIFY(!utcd.isDateOnly()); QCOMPARE(utcd.date(), QDate(2004, 5, 4)); QCOMPARE(utcd.time(), QTime(0, 0, 0)); KADateTime utc(QDate(2005, 6, 6), QTime(1, 2, 30), KADateTime::UTC); utc.setDate(QDate(2004, 5, 2)); QCOMPARE(utc.date(), QDate(2004, 5, 2)); QCOMPARE(utc.time(), QTime(1, 2, 30)); utc.setTime(QTime(12, 13, 14)); QCOMPARE(utc.date(), QDate(2004, 5, 2)); QCOMPARE(utc.time(), QTime(12, 13, 14)); utc.setDate(QDate(2003, 12, 10)); utc.setTime(QTime(5, 6, 7)); QCOMPARE(utc.utcOffset(), 0); QCOMPARE(utc.date(), QDate(2003, 12, 10)); QCOMPARE(utc.time(), QTime(5, 6, 7)); // setTimeSpec(SpecType) QCOMPARE(zone.date(), QDate(2003, 6, 10)); QCOMPARE(zone.time(), QTime(5, 6, 7)); zone.setTimeSpec(KADateTime::Spec::OffsetFromUTC(7200)); QVERIFY(zone.isOffsetFromUtc()); QCOMPARE(zone.utcOffset(), 7200); QCOMPARE(zone.toUtc().date(), QDate(2003, 6, 10)); QCOMPARE(zone.toUtc().time(), QTime(3, 6, 7)); zone.setTimeSpec(KADateTime::LocalZone); QVERIFY(zone.isLocalZone()); QCOMPARE(zone.utcOffset(), -7 * 3600); QCOMPARE(zone.toUtc().date(), QDate(2003, 6, 10)); QCOMPARE(zone.toUtc().time(), QTime(12, 6, 7)); zone.setTimeSpec(KADateTime::UTC); QVERIFY(zone.isUtc()); QCOMPARE(zone.utcOffset(), 0); QCOMPARE(zone.date(), QDate(2003, 6, 10)); QCOMPARE(zone.time(), QTime(5, 6, 7)); // setTimeSpec(KADateTime::Spec) QCOMPARE(zone.date(), QDate(2003, 6, 10)); QCOMPARE(zone.time(), QTime(5, 6, 7)); zone.setTimeSpec(offset.timeSpec()); QVERIFY(zone.isOffsetFromUtc()); QCOMPARE(zone.toUtc().date(), QDate(2003, 6, 10)); QCOMPARE(zone.toUtc().time(), QTime(6, 36, 7)); QVERIFY(zone.timeSpec() == offset.timeSpec()); zone.setTimeSpec(KADateTime::LocalZone); QVERIFY(zone.isLocalZone()); QCOMPARE(zone.toUtc().date(), QDate(2003, 6, 10)); QCOMPARE(zone.toUtc().time(), QTime(12, 6, 7)); zone.setTimeSpec(utc.timeSpec()); QVERIFY(zone.isUtc()); QCOMPARE(zone.date(), QDate(2003, 6, 10)); QCOMPARE(zone.time(), QTime(5, 6, 7)); zone.setTimeSpec(london); QCOMPARE(zone.timeZone(), london); QCOMPARE(zone.utcOffset(), 3600); QCOMPARE(zone.toUtc().date(), QDate(2003, 6, 10)); QCOMPARE(zone.toUtc().time(), QTime(4, 6, 7)); // time_t utcd = KADateTime(QDate(2005, 6, 6), QTime(12, 15, 20), KADateTime::UTC); QDateTime qtt = utcd.qDateTime(); uint secs = qtt.toTime_t(); KADateTime tt; tt.setTime_t(static_cast(secs)); QVERIFY(tt.isUtc()); QCOMPARE(tt.date(), utcd.date()); QCOMPARE(tt.time(), utcd.time()); QCOMPARE(tt.toTime_t(), secs); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } ///////////////////////////////////////////////////////// // operator==() ///////////////////////////////////////////////////////// void KADateTimeTest::equal() { QTimeZone london("Europe/London"); QTimeZone cairo("Africa/Cairo"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Date/time values QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 2, 28), QTime(3, 45, 3), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 2, 29), QTime(3, 45, 3), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 1), QTime(3, 45, 3), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 2), QTime(3, 45, 3), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 3), QTime(3, 45, 3), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 3), cairo) == KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo))); QVERIFY(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo)); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 2, 28), QTime(3, 45, 3), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 2, 29), QTime(3, 45, 3), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 1), QTime(3, 45, 3), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 2), QTime(3, 45, 3), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 3), QTime(3, 45, 3), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), KADateTime::UTC))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), KADateTime::UTC) == KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo))); QVERIFY(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 1), QTime(17, 45, 2), KADateTime::LocalZone)); // Date/time : date-only QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(0, 0, 0), cairo) == KADateTime(QDate(2004, 3, 1), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 1), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo) == KADateTime(QDate(2004, 3, 1), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo) == KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), QTime(0, 0, 0), cairo) == KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(0, 0, 0), cairo) == KADateTime(QDate(2004, 3, 1), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) == KADateTime(QDate(2004, 3, 1), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo) == KADateTime(QDate(2004, 3, 1), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo) == KADateTime(QDate(2004, 3, 2), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), QTime(0, 0, 0), cairo) == KADateTime(QDate(2004, 3, 2), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(9, 59, 59, 999), cairo) == KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(10, 0, 0), cairo) == KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(10, 0, 1), cairo) == KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), QTime(9, 59, 59, 999), cairo) == KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), QTime(10, 0, 0), cairo) == KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)))); // Date-only : date/time QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 1), QTime(0, 0, 0), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) == KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) == KADateTime(QDate(2004, 3, 3), QTime(0, 0, 0), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 1), QTime(0, 0, 0), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) == KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) == KADateTime(QDate(2004, 3, 3), QTime(0, 0, 0), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)) == KADateTime(QDate(2004, 3, 2), QTime(9, 59, 59, 999), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)) == KADateTime(QDate(2004, 3, 2), QTime(10, 0, 0), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)) == KADateTime(QDate(2004, 3, 3), QTime(9, 59, 59, 999), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)) == KADateTime(QDate(2004, 3, 3), QTime(10, 0, 0), cairo))); // Date-only values QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(2 * 3600)))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), london) == KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(3 * 3600)))); QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 1), cairo)); QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) == KADateTime(QDate(2004, 3, 1), KADateTime::Spec::OffsetFromUTC(2 * 3600))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) == KADateTime(QDate(2004, 3, 1), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(3 * 3600)) == KADateTime(QDate(2004, 3, 1), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) == KADateTime(QDate(2004, 3, 1), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(2 * 3600)) == KADateTime(QDate(2004, 3, 1), cairo))); // Compare days when daylight savings changes occur QVERIFY(KADateTime(QDate(2005, 3, 27), london) == KADateTime(QDate(2005, 3, 27), london)); QVERIFY(!(KADateTime(QDate(2005, 3, 27), london) == KADateTime(QDate(2005, 3, 27), KADateTime::Spec::OffsetFromUTC(0)))); QVERIFY(KADateTime(QDate(2005, 3, 27), KADateTime::Spec::OffsetFromUTC(0)) == KADateTime(QDate(2005, 3, 27), KADateTime::Spec::OffsetFromUTC(0))); QVERIFY(!(KADateTime(QDate(2005, 3, 27), KADateTime::Spec::OffsetFromUTC(0)) == KADateTime(QDate(2005, 3, 27), london))); QVERIFY(KADateTime(QDate(2005, 10, 30), KADateTime::Spec(KADateTime::UTC)) == KADateTime(QDate(2005, 10, 30), KADateTime::Spec(KADateTime::UTC))); QVERIFY(!(KADateTime(QDate(2005, 10, 30), london) == KADateTime(QDate(2005, 10, 30), KADateTime::Spec(KADateTime::UTC)))); QVERIFY(!(KADateTime(QDate(2005, 10, 30), KADateTime::Spec(KADateTime::UTC)) == KADateTime(QDate(2005, 10, 30), london))); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } ///////////////////////////////////////////////////////// // operator<() ///////////////////////////////////////////////////////// void KADateTimeTest::lessThan() { QTimeZone london("Europe/London"); QTimeZone cairo("Africa/Cairo"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Date/time values QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 2, 28), QTime(3, 45, 3), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 2, 29), QTime(3, 45, 3), cairo))); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 1), QTime(3, 45, 3), cairo)); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 2), QTime(3, 45, 3), cairo)); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 3), QTime(3, 45, 3), cairo)); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 3), cairo) < KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo))); QVERIFY(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 1), cairo) < KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo)); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 2, 28), QTime(3, 45, 3), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 2, 29), QTime(3, 45, 3), london))); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 1), QTime(3, 45, 3), london)); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 2), QTime(3, 45, 3), london)); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 3), QTime(3, 45, 3), london)); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), london) < KADateTime(QDate(2004, 2, 28), QTime(3, 45, 3), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), london) < KADateTime(QDate(2004, 2, 29), QTime(3, 45, 3), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), london) < KADateTime(QDate(2004, 3, 1), QTime(3, 45, 3), cairo))); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), london) < KADateTime(QDate(2004, 3, 2), QTime(3, 45, 3), cairo)); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), london) < KADateTime(QDate(2004, 3, 3), QTime(3, 45, 3), cairo)); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), KADateTime::UTC)); QVERIFY(!(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), KADateTime::UTC) < KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 1), QTime(17, 45, 2), KADateTime::LocalZone))); QVERIFY(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 1), QTime(17, 45, 3), KADateTime::LocalZone)); // Date/time : date-only QVERIFY(KADateTime(QDate(2004, 2, 29), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), cairo)); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), cairo)); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo) < KADateTime(QDate(2004, 3, 2), cairo)); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(23, 59, 59, 999), cairo) < KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 4), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 5), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(KADateTime(QDate(2004, 2, 29), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), london)); QVERIFY(KADateTime(QDate(2004, 3, 1), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), london)); QVERIFY(KADateTime(QDate(2004, 3, 2), QTime(1, 59, 59, 999), cairo) < KADateTime(QDate(2004, 3, 2), london)); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(2, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo) < KADateTime(QDate(2004, 3, 2), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(23, 59, 59, 999), cairo) < KADateTime(QDate(2004, 3, 2), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 4), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 5), QTime(0, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), london))); QVERIFY(KADateTime(QDate(2004, 3, 2), QTime(9, 59, 59, 999), cairo) < KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(10, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), QTime(10, 0, 1), cairo) < KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), QTime(9, 59, 59, 999), cairo) < KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), QTime(10, 0, 0), cairo) < KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)))); // Date-only : date/time QVERIFY(KADateTime(QDate(2004, 2, 28), cairo) < KADateTime(QDate(2004, 3, 2), QTime(0, 0, 0), cairo)); QVERIFY(KADateTime(QDate(2004, 2, 29), cairo) < KADateTime(QDate(2004, 3, 2), QTime(0, 0, 0), cairo)); QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 2), QTime(0, 0, 0), cairo)); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) < KADateTime(QDate(2004, 3, 2), QTime(0, 0, 0), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) < KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) < KADateTime(QDate(2004, 3, 2), QTime(23, 59, 59, 999), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), cairo) < KADateTime(QDate(2004, 3, 2), QTime(23, 59, 59, 999), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 4), cairo) < KADateTime(QDate(2004, 3, 2), QTime(0, 0, 0), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 5), cairo) < KADateTime(QDate(2004, 3, 2), QTime(0, 0, 0), cairo))); QVERIFY(KADateTime(QDate(2004, 2, 28), cairo) < KADateTime(QDate(2004, 3, 1), QTime(22, 0, 0), london)); QVERIFY(KADateTime(QDate(2004, 2, 29), cairo) < KADateTime(QDate(2004, 3, 1), QTime(22, 0, 0), london)); QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 1), QTime(22, 0, 0), london)); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) < KADateTime(QDate(2004, 3, 1), QTime(22, 0, 0), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) < KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) < KADateTime(QDate(2004, 3, 1), QTime(21, 59, 59, 999), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), cairo) < KADateTime(QDate(2004, 3, 1), QTime(21, 59, 59, 999), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 4), cairo) < KADateTime(QDate(2004, 3, 1), QTime(22, 0, 0), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 5), cairo) < KADateTime(QDate(2004, 3, 1), QTime(22, 0, 0), london))); QVERIFY(KADateTime(QDate(2004, 2, 28), KADateTime::Spec(KADateTime::LocalZone)) < KADateTime(QDate(2004, 3, 2), QTime(9, 59, 59, 999), cairo)); QVERIFY(KADateTime(QDate(2004, 2, 29), KADateTime::Spec(KADateTime::LocalZone)) < KADateTime(QDate(2004, 3, 2), QTime(9, 59, 59, 999), cairo)); QVERIFY(!(KADateTime(QDate(2004, 3, 1), KADateTime::Spec(KADateTime::LocalZone)) < KADateTime(QDate(2004, 3, 2), QTime(9, 59, 59, 999), cairo))); QVERIFY(KADateTime(QDate(2004, 3, 1), KADateTime::Spec(KADateTime::LocalZone)) < KADateTime(QDate(2004, 3, 2), QTime(10, 0, 0), cairo)); QVERIFY(!(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)) < KADateTime(QDate(2004, 3, 2), QTime(9, 59, 59, 999), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 3), KADateTime::Spec(KADateTime::LocalZone)) < KADateTime(QDate(2004, 3, 2), QTime(10, 0, 0), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 6), KADateTime::Spec(KADateTime::LocalZone)) < KADateTime(QDate(2004, 3, 2), QTime(10, 0, 0), cairo))); // Date-only values QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 2), cairo)); QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(2 * 3600))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), london) < KADateTime(QDate(2004, 3, 2), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(3 * 3600)))); QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 3), KADateTime::Spec::OffsetFromUTC(3 * 3600))); QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 4), KADateTime::Spec::OffsetFromUTC(3 * 3600))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 1), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 1), KADateTime::Spec::OffsetFromUTC(2 * 3600)))); QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 5), london)); QVERIFY(KADateTime(QDate(2004, 3, 1), cairo) < KADateTime(QDate(2004, 3, 2), london)); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) < KADateTime(QDate(2004, 3, 1), london))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(3 * 3600)) < KADateTime(QDate(2004, 3, 1), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), cairo) < KADateTime(QDate(2004, 3, 1), cairo))); QVERIFY(!(KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(2 * 3600)) < KADateTime(QDate(2004, 3, 1), cairo))); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } ///////////////////////////////////////////////////////// // compare() ///////////////////////////////////////////////////////// void KADateTimeTest::compare() { QTimeZone london("Europe/London"); QTimeZone cairo("Africa/Cairo"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Date/time values QCOMPARE(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo).compare(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 3), cairo)), KADateTime::Before); QCOMPARE(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo).compare(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), KADateTime::UTC)), KADateTime::Before); QCOMPARE(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), KADateTime::UTC).compare(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo)), KADateTime::After); QCOMPARE(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 3), cairo).compare(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo)), KADateTime::After); QCOMPARE(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo).compare(KADateTime(QDate(2004, 3, 1), QTime(17, 45, 2), KADateTime::LocalZone)), KADateTime::Equal); QCOMPARE(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo).compare(KADateTime(QDate(2004, 3, 2), QTime(3, 45, 2), cairo)), KADateTime::Equal); // Date/time : date-only QCOMPARE(KADateTime(QDate(2004, 3, 1), QTime(0, 0, 0), cairo).compare(KADateTime(QDate(2004, 3, 1), cairo)), KADateTime::AtStart); QCOMPARE(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo).compare(KADateTime(QDate(2004, 3, 1), cairo)), KADateTime::Inside); QCOMPARE(KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo).compare(KADateTime(QDate(2004, 3, 1), cairo)), KADateTime::AtEnd); QCOMPARE(KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo).compare(KADateTime(QDate(2004, 3, 2), cairo)), KADateTime::Before); QCOMPARE(KADateTime(QDate(2004, 3, 3), QTime(0, 0, 0), cairo).compare(KADateTime(QDate(2004, 3, 2), cairo)), KADateTime::After); QCOMPARE(KADateTime(QDate(2004, 3, 2), QTime(9, 59, 59, 999), cairo).compare(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone))), KADateTime::Before); QCOMPARE(KADateTime(QDate(2004, 3, 2), QTime(10, 0, 0), cairo).compare(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone))), KADateTime::AtStart); QCOMPARE(KADateTime(QDate(2004, 3, 2), QTime(10, 0, 1), cairo).compare(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone))), KADateTime::Inside); QCOMPARE(KADateTime(QDate(2004, 3, 3), QTime(9, 59, 59, 999), cairo).compare(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone))), KADateTime::AtEnd); QCOMPARE(KADateTime(QDate(2004, 3, 3), QTime(10, 0, 0), cairo).compare(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone))), KADateTime::After); // Date-only : date/time QCOMPARE(KADateTime(QDate(2004, 3, 1), cairo).compare(KADateTime(QDate(2004, 3, 1), QTime(0, 0, 0), cairo)), KADateTime::StartsAt); QCOMPARE(KADateTime(QDate(2004, 3, 1), cairo).compare(KADateTime(QDate(2004, 3, 1), QTime(3, 45, 2), cairo)), KADateTime::Outside); QCOMPARE(KADateTime(QDate(2004, 3, 1), cairo).compare(KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo)), KADateTime::EndsAt); QCOMPARE(KADateTime(QDate(2004, 3, 2), cairo).compare(KADateTime(QDate(2004, 3, 1), QTime(23, 59, 59, 999), cairo)), KADateTime::After); QCOMPARE(KADateTime(QDate(2004, 3, 2), cairo).compare(KADateTime(QDate(2004, 3, 3), QTime(0, 0, 0), cairo)), KADateTime::Before); QCOMPARE(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)).compare(KADateTime(QDate(2004, 3, 2), QTime(9, 59, 59, 999), cairo)), KADateTime::After); QCOMPARE(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)).compare(KADateTime(QDate(2004, 3, 2), QTime(10, 0, 0), cairo)), KADateTime::StartsAt); QCOMPARE(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)).compare(KADateTime(QDate(2004, 3, 3), QTime(9, 59, 59, 999), cairo)), KADateTime::EndsAt); QCOMPARE(KADateTime(QDate(2004, 3, 2), KADateTime::Spec(KADateTime::LocalZone)).compare(KADateTime(QDate(2004, 3, 3), QTime(10, 0, 0), cairo)), KADateTime::Before); // Date-only values QCOMPARE(KADateTime(QDate(2004, 3, 1), cairo).compare(KADateTime(QDate(2004, 3, 2), cairo)), KADateTime::Before); QCOMPARE(KADateTime(QDate(2004, 3, 1), cairo).compare(KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(2 * 3600))), KADateTime::Before); QCOMPARE(KADateTime(QDate(2004, 3, 1), london).compare(KADateTime(QDate(2004, 3, 2), cairo)), static_cast(KADateTime::Before | KADateTime::AtStart | KADateTime::Inside)); QCOMPARE(KADateTime(QDate(2004, 3, 1), cairo).compare(KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(3 * 3600))), static_cast(KADateTime::Before | KADateTime::AtStart | KADateTime::Inside)); QCOMPARE(KADateTime(QDate(2004, 3, 1), cairo).compare(KADateTime(QDate(2004, 3, 1), cairo)), KADateTime::Equal); QCOMPARE(KADateTime(QDate(2004, 3, 1), cairo).compare(KADateTime(QDate(2004, 3, 1), KADateTime::Spec::OffsetFromUTC(2 * 3600))), KADateTime::Equal); QCOMPARE(KADateTime(QDate(2004, 3, 2), cairo).compare(KADateTime(QDate(2004, 3, 1), london)), static_cast(KADateTime::Inside | KADateTime::AtEnd | KADateTime::After)); QCOMPARE(KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(3 * 3600)).compare(KADateTime(QDate(2004, 3, 1), cairo)), static_cast(KADateTime::Inside | KADateTime::AtEnd | KADateTime::After)); QCOMPARE(KADateTime(QDate(2004, 3, 2), cairo).compare(KADateTime(QDate(2004, 3, 1), cairo)), KADateTime::After); QCOMPARE(KADateTime(QDate(2004, 3, 2), KADateTime::Spec::OffsetFromUTC(2 * 3600)).compare(KADateTime(QDate(2004, 3, 1), cairo)), KADateTime::After); // Compare days when daylight savings changes occur QCOMPARE(KADateTime(QDate(2005, 3, 27), london).compare(KADateTime(QDate(2005, 3, 27), KADateTime::Spec::OffsetFromUTC(0))), static_cast(KADateTime::AtStart | KADateTime::Inside)); QCOMPARE(KADateTime(QDate(2005, 3, 27), KADateTime::Spec::OffsetFromUTC(0)).compare(KADateTime(QDate(2005, 3, 27), london)), KADateTime::StartsAt); QCOMPARE(KADateTime(QDate(2005, 10, 30), london).compare(KADateTime(QDate(2005, 10, 30), KADateTime::Spec(KADateTime::UTC))), KADateTime::EndsAt); QCOMPARE(KADateTime(QDate(2005, 10, 30), KADateTime::Spec(KADateTime::UTC)).compare(KADateTime(QDate(2005, 10, 30), london)), static_cast(KADateTime::Inside | KADateTime::AtEnd)); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } ///////////////////////////////////////////////////////// // Addition and subtraction methods, and operator<() etc. ///////////////////////////////////////////////////////// void KADateTimeTest::addSubtract() { QTimeZone london("Europe/London"); QTimeZone losAngeles("America/Los_Angeles"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // UTC KADateTime utc1(QDate(2005, 7, 6), QTime(3, 40, 0), KADateTime::UTC); KADateTime utc2 = utc1.addSecs(184 * 86400); QVERIFY(utc2.isUtc()); QCOMPARE(utc2.date(), QDate(2006, 1, 6)); QCOMPARE(utc2.time(), QTime(3, 40, 0)); KADateTime utc3 = utc1.addDays(184); QVERIFY(utc3.isUtc()); QCOMPARE(utc2.date(), utc3.date()); QCOMPARE(utc2.time(), utc3.time()); KADateTime utc4 = utc1.addMonths(6); QVERIFY(utc4.isUtc()); QCOMPARE(utc2.date(), utc4.date()); QCOMPARE(utc2.time(), utc4.time()); KADateTime utc5 = utc1.addYears(4); QVERIFY(utc5.isUtc()); QCOMPARE(utc5.date(), QDate(2009, 7, 6)); QCOMPARE(utc5.time(), QTime(3, 40, 0)); QCOMPARE(utc1.secsTo(utc2), 184 * 86400); QCOMPARE(utc1.secsTo(utc3), 184 * 86400); QCOMPARE(utc1.daysTo(utc2), 184); QVERIFY(utc1 < utc2); QVERIFY(!(utc2 < utc1)); QVERIFY(utc2 == utc3); // UTC offset KADateTime offset1(QDate(2005, 7, 6), QTime(3, 40, 0), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 KADateTime offset2 = offset1.addSecs(184 * 86400); QVERIFY(offset2.isOffsetFromUtc()); QCOMPARE(offset2.utcOffset(), -5400); QCOMPARE(offset2.date(), QDate(2006, 1, 6)); QCOMPARE(offset2.time(), QTime(3, 40, 0)); KADateTime offset3 = offset1.addDays(184); QVERIFY(offset3.isOffsetFromUtc()); QCOMPARE(offset3.utcOffset(), -5400); QCOMPARE(offset2.date(), offset3.date()); QCOMPARE(offset2.time(), offset3.time()); KADateTime offset4 = offset1.addMonths(6); QVERIFY(offset4.isOffsetFromUtc()); QCOMPARE(offset4.utcOffset(), -5400); QCOMPARE(offset2.date(), offset4.date()); QCOMPARE(offset2.time(), offset4.time()); KADateTime offset5 = offset1.addYears(4); QVERIFY(offset5.isOffsetFromUtc()); QCOMPARE(offset5.utcOffset(), -5400); QCOMPARE(offset5.date(), QDate(2009, 7, 6)); QCOMPARE(offset5.time(), QTime(3, 40, 0)); QCOMPARE(offset1.secsTo(offset2), 184 * 86400); QCOMPARE(offset1.secsTo(offset3), 184 * 86400); QCOMPARE(offset1.daysTo(offset2), 184); QVERIFY(offset1 < offset2); QVERIFY(!(offset2 < offset1)); QVERIFY(offset2 == offset3); // Zone KADateTime zone1(QDate(2005, 7, 6), QTime(3, 40, 0), london); KADateTime zone2 = zone1.addSecs(184 * 86400); QCOMPARE(zone2.timeZone(), london); QCOMPARE(zone2.date(), QDate(2006, 1, 6)); QCOMPARE(zone2.time(), QTime(2, 40, 0)); KADateTime zone3 = zone1.addDays(184); QCOMPARE(zone3.timeZone(), london); QCOMPARE(zone3.date(), QDate(2006, 1, 6)); QCOMPARE(zone3.time(), QTime(3, 40, 0)); KADateTime zone4 = zone1.addMonths(6); QCOMPARE(zone4.timeZone(), london); QCOMPARE(zone4.date(), zone3.date()); QCOMPARE(zone4.time(), zone3.time()); KADateTime zone5 = zone1.addYears(4); QCOMPARE(zone5.timeZone(), london); QCOMPARE(zone5.date(), QDate(2009, 7, 6)); QCOMPARE(zone5.time(), QTime(3, 40, 0)); QCOMPARE(zone1.secsTo(zone2), 184 * 86400); QCOMPARE(zone1.secsTo(zone3), 184 * 86400 + 3600); QCOMPARE(zone1.daysTo(zone2), 184); QCOMPARE(zone1.daysTo(zone3), 184); QVERIFY(zone1 < zone2); QVERIFY(!(zone2 < zone1)); QVERIFY(!(zone2 == zone3)); // Local zone KADateTime local1(QDate(2005, 7, 6), QTime(3, 40, 0), KADateTime::LocalZone); KADateTime local2 = local1.addSecs(184 * 86400); QVERIFY(local2.isLocalZone()); QCOMPARE(local2.timeZone(), losAngeles); QCOMPARE(local2.date(), QDate(2006, 1, 6)); QCOMPARE(local2.time(), QTime(2, 40, 0)); KADateTime local3 = local1.addDays(184); QVERIFY(local3.isLocalZone()); QCOMPARE(local3.date(), QDate(2006, 1, 6)); QCOMPARE(local3.time(), QTime(3, 40, 0)); KADateTime local4 = local1.addMonths(6); QVERIFY(local4.isLocalZone()); QCOMPARE(local4.date(), local3.date()); QCOMPARE(local4.time(), local3.time()); KADateTime local5 = local1.addYears(4); QVERIFY(local5.isLocalZone()); QCOMPARE(local5.date(), QDate(2009, 7, 6)); QCOMPARE(local5.time(), QTime(3, 40, 0)); QCOMPARE(local1.secsTo(local2), 184 * 86400); QCOMPARE(local1.secsTo(local3), 184 * 86400 + 3600); QCOMPARE(local1.daysTo(local2), 184); QCOMPARE(local1.daysTo(local3), 184); QVERIFY(local1 < local2); QVERIFY(!(local2 < local1)); QVERIFY(!(local2 == local3)); // Mixed timeSpecs QCOMPARE(utc1.secsTo(offset1), 5400); QCOMPARE(utc1.secsTo(offset2), 184 * 86400 + 5400); QCOMPARE(offset2.secsTo(utc1), -(184 * 86400 + 5400)); QVERIFY(utc1 < offset1); QVERIFY(utc1 <= offset1); QVERIFY(!(offset1 < utc1)); QVERIFY(!(offset1 <= utc1)); QCOMPARE(utc1.secsTo(zone1), -3600); QCOMPARE(utc1.secsTo(zone2), 184 * 86400 - 3600); QCOMPARE(zone2.secsTo(utc1), -(184 * 86400 - 3600)); QVERIFY(utc1 > zone1); QVERIFY(utc1 >= zone1); QVERIFY(!(zone1 > utc1)); QVERIFY(!(zone1 >= utc1)); QCOMPARE(utc1.secsTo(local1), 7 * 3600); QCOMPARE(utc1.secsTo(local2), 184 * 86400 + 7 * 3600); QCOMPARE(local2.secsTo(utc1), -(184 * 86400 + 7 * 3600)); QVERIFY(utc1 < local1); QVERIFY(utc1 <= local1); QVERIFY(!(local1 < utc1)); QVERIFY(!(local1 <= utc1)); QCOMPARE(offset1.secsTo(zone1), -9000); QCOMPARE(offset1.secsTo(zone2), 184 * 86400 - 9000); QCOMPARE(zone2.secsTo(offset1), -(184 * 86400 - 9000)); QVERIFY(offset1 > zone1); QVERIFY(offset1 >= zone1); QVERIFY(!(zone1 > offset1)); QVERIFY(!(zone1 >= offset1)); QCOMPARE(offset1.secsTo(local1), 7 * 3600 - 5400); QCOMPARE(offset1.secsTo(local2), 184 * 86400 + 7 * 3600 - 5400); QCOMPARE(local2.secsTo(offset1), -(184 * 86400 + 7 * 3600 - 5400)); QVERIFY(offset1 < local1); QVERIFY(offset1 <= local1); QVERIFY(!(local1 < offset1)); QVERIFY(!(local1 <= offset1)); QCOMPARE(zone1.secsTo(local1), 8 * 3600); QCOMPARE(zone1.secsTo(local2), 184 * 86400 + 8 * 3600); QCOMPARE(local2.secsTo(zone1), -(184 * 86400 + 8 * 3600)); QVERIFY(zone1 < local1); QVERIFY(zone1 <= local1); QVERIFY(!(local1 < zone1)); QVERIFY(!(local1 <= zone1)); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::addMSecs() { QTimeZone london("Europe/London"); QTimeZone losAngeles("America/Los_Angeles"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // UTC KADateTime utc1(QDate(2005, 7, 6), QTime(23, 59, 0, 100), KADateTime::UTC); KADateTime utc2 = utc1.addMSecs(59899); QVERIFY(utc2.isUtc()); QCOMPARE(utc2.date(), QDate(2005, 7, 6)); QCOMPARE(utc2.time(), QTime(23, 59, 59, 999)); + QCOMPARE(utc1.msecsTo(utc2), 59899); utc2 = utc1.addMSecs(59900); QVERIFY(utc2.isUtc()); QCOMPARE(utc2.date(), QDate(2005, 7, 7)); QCOMPARE(utc2.time(), QTime(0, 0, 0, 0)); + QCOMPARE(utc1.msecsTo(utc2), 59900); KADateTime utc1a(QDate(2005, 7, 6), QTime(0, 0, 5, 100), KADateTime::UTC); utc2 = utc1a.addMSecs(-5100); QVERIFY(utc2.isUtc()); QCOMPARE(utc2.date(), QDate(2005, 7, 6)); QCOMPARE(utc2.time(), QTime(0, 0, 0, 0)); + QCOMPARE(utc1a.msecsTo(utc2), -5100); utc2 = utc1a.addMSecs(-5101); QVERIFY(utc2.isUtc()); QCOMPARE(utc2.date(), QDate(2005, 7, 5)); QCOMPARE(utc2.time(), QTime(23, 59, 59, 999)); + QCOMPARE(utc1a.msecsTo(utc2), -5101); // UTC offset KADateTime offset1(QDate(2005, 7, 6), QTime(3, 40, 0, 100), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 KADateTime offset2 = offset1.addMSecs(5899); QVERIFY(offset2.isOffsetFromUtc()); QCOMPARE(offset2.utcOffset(), -5400); QCOMPARE(offset2.date(), QDate(2005, 7, 6)); QCOMPARE(offset2.time(), QTime(3, 40, 5, 999)); offset2 = offset1.addMSecs(5900); QVERIFY(offset2.isOffsetFromUtc()); QCOMPARE(offset2.utcOffset(), -5400); QCOMPARE(offset2.date(), QDate(2005, 7, 6)); QCOMPARE(offset2.time(), QTime(3, 40, 6, 0)); offset2 = offset1.addMSecs(-5100); QVERIFY(offset2.isOffsetFromUtc()); QCOMPARE(offset2.utcOffset(), -5400); QCOMPARE(offset2.date(), QDate(2005, 7, 6)); QCOMPARE(offset2.time(), QTime(3, 39, 55, 0)); offset2 = offset1.addMSecs(-5101); QVERIFY(offset2.isOffsetFromUtc()); QCOMPARE(offset2.utcOffset(), -5400); QCOMPARE(offset2.date(), QDate(2005, 7, 6)); QCOMPARE(offset2.time(), QTime(3, 39, 54, 999)); // Zone KADateTime zone1(QDate(2002, 3, 31), QTime(0, 40, 0, 100), london); // time changes at 01:00 UTC KADateTime zone2 = zone1.addMSecs(3600 * 1000 + 899); QCOMPARE(zone2.timeZone(), london); QCOMPARE(zone2.date(), QDate(2002, 3, 31)); QCOMPARE(zone2.time(), QTime(2, 40, 0, 999)); zone2 = zone1.addMSecs(3600 * 1000 + 900); QCOMPARE(zone2.timeZone(), london); QCOMPARE(zone2.date(), QDate(2002, 3, 31)); QCOMPARE(zone2.time(), QTime(2, 40, 1, 0)); KADateTime zone1a(QDate(2002, 3, 31), QTime(2, 40, 0, 100), london); // time changes at 01:00 UTC zone2 = zone1a.addMSecs(-(3600 * 1000 + 100)); QCOMPARE(zone2.timeZone(), london); QCOMPARE(zone2.date(), QDate(2002, 3, 31)); QCOMPARE(zone2.time(), QTime(0, 40, 0, 0)); zone2 = zone1a.addMSecs(-(3600 * 1000 + 101)); QCOMPARE(zone2.timeZone(), london); QCOMPARE(zone2.date(), QDate(2002, 3, 31)); QCOMPARE(zone2.time(), QTime(0, 39, 59, 999)); // Local zone KADateTime local1(QDate(2002, 4, 7), QTime(1, 59, 0, 100), KADateTime::LocalZone); // time changes at 02:00 local KADateTime local2 = local1.addMSecs(59899); QVERIFY(local2.isLocalZone()); QCOMPARE(local2.timeZone(), losAngeles); QCOMPARE(local2.date(), QDate(2002, 4, 7)); QCOMPARE(local2.time(), QTime(1, 59, 59, 999)); local2 = local1.addMSecs(59900); QVERIFY(local2.isLocalZone()); QCOMPARE(local2.timeZone(), losAngeles); QCOMPARE(local2.date(), QDate(2002, 4, 7)); QCOMPARE(local2.time(), QTime(3, 0, 0, 0)); KADateTime local1a(QDate(2002, 4, 7), QTime(3, 0, 0, 100), KADateTime::LocalZone); // time changes at 02:00 local local2 = local1a.addMSecs(-100); QVERIFY(local2.isLocalZone()); QCOMPARE(local2.timeZone(), losAngeles); QCOMPARE(local2.date(), QDate(2002, 4, 7)); QCOMPARE(local2.time(), QTime(3, 0, 0, 0)); local2 = local1a.addMSecs(-101); QVERIFY(local2.isLocalZone()); QCOMPARE(local2.timeZone(), losAngeles); QCOMPARE(local2.date(), QDate(2002, 4, 7)); QCOMPARE(local2.time(), QTime(1, 59, 59, 999)); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::addSubtractDate() { QTimeZone london("Europe/London"); QTimeZone losAngeles("America/Los_Angeles"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // UTC KADateTime utc1(QDate(2005, 7, 6), KADateTime::Spec(KADateTime::UTC)); KADateTime utc2 = utc1.addSecs(184 * 86400 + 100); QVERIFY(utc2.isUtc()); QVERIFY(utc2.isDateOnly()); QCOMPARE(utc2.date(), QDate(2006, 1, 6)); QCOMPARE(utc2.time(), QTime(0, 0, 0)); KADateTime utc3 = utc1.addDays(184); QVERIFY(utc3.isUtc()); QVERIFY(utc3.isDateOnly()); QCOMPARE(utc2.date(), utc3.date()); QCOMPARE(utc2.time(), utc3.time()); KADateTime utc4 = utc1.addMonths(6); QVERIFY(utc4.isUtc()); QVERIFY(utc4.isDateOnly()); QCOMPARE(utc2.date(), utc4.date()); QCOMPARE(utc2.time(), utc4.time()); KADateTime utc5 = utc1.addYears(4); QVERIFY(utc5.isUtc()); QVERIFY(utc5.isDateOnly()); QCOMPARE(utc5.date(), QDate(2009, 7, 6)); QCOMPARE(utc5.time(), QTime(0, 0, 0)); QCOMPARE(utc1.secsTo(utc2), 184 * 86400); QCOMPARE(utc1.secsTo(utc3), 184 * 86400); QCOMPARE(utc1.daysTo(utc2), 184); QVERIFY(utc1 < utc2); QVERIFY(!(utc2 < utc1)); QVERIFY(utc2 == utc3); // UTC offset KADateTime offset1(QDate(2005, 7, 6), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 KADateTime offset2 = offset1.addSecs(184 * 86400); QVERIFY(offset2.isDateOnly()); QVERIFY(offset2.isOffsetFromUtc()); QCOMPARE(offset2.utcOffset(), -5400); QCOMPARE(offset2.date(), QDate(2006, 1, 6)); QCOMPARE(offset2.time(), QTime(0, 0, 0)); KADateTime offset3 = offset1.addDays(184); QVERIFY(offset3.isDateOnly()); QVERIFY(offset3.isOffsetFromUtc()); QCOMPARE(offset3.utcOffset(), -5400); QCOMPARE(offset2.date(), offset3.date()); QCOMPARE(offset2.time(), offset3.time()); KADateTime offset4 = offset1.addMonths(6); QVERIFY(offset4.isDateOnly()); QVERIFY(offset4.isOffsetFromUtc()); QCOMPARE(offset4.utcOffset(), -5400); QCOMPARE(offset2.date(), offset4.date()); QCOMPARE(offset2.time(), offset4.time()); KADateTime offset5 = offset1.addYears(4); QVERIFY(offset5.isDateOnly()); QVERIFY(offset5.isOffsetFromUtc()); QCOMPARE(offset5.utcOffset(), -5400); QCOMPARE(offset5.date(), QDate(2009, 7, 6)); QCOMPARE(offset5.time(), QTime(0, 0, 0)); QCOMPARE(offset1.secsTo(offset2), 184 * 86400); QCOMPARE(offset1.secsTo(offset3), 184 * 86400); QCOMPARE(offset1.daysTo(offset2), 184); QVERIFY(offset1 < offset2); QVERIFY(!(offset2 < offset1)); QVERIFY(offset2 == offset3); // Zone KADateTime zone1(QDate(2005, 7, 6), london); KADateTime zone2 = zone1.addSecs(184 * 86400); QVERIFY(zone2.isDateOnly()); QCOMPARE(zone2.timeZone(), london); QCOMPARE(zone2.date(), QDate(2006, 1, 6)); QCOMPARE(zone2.time(), QTime(0, 0, 0)); KADateTime zone3 = zone1.addDays(184); QVERIFY(zone3.isDateOnly()); QCOMPARE(zone3.timeZone(), london); QCOMPARE(zone3.date(), QDate(2006, 1, 6)); QCOMPARE(zone3.time(), QTime(0, 0, 0)); KADateTime zone4 = zone1.addMonths(6); QVERIFY(zone4.isDateOnly()); QCOMPARE(zone4.timeZone(), london); QCOMPARE(zone4.date(), zone3.date()); QCOMPARE(zone4.time(), zone3.time()); KADateTime zone5 = zone1.addYears(4); QVERIFY(zone5.isDateOnly()); QCOMPARE(zone5.timeZone(), london); QCOMPARE(zone5.date(), QDate(2009, 7, 6)); QCOMPARE(zone5.time(), QTime(0, 0, 0)); QCOMPARE(zone1.secsTo(zone2), 184 * 86400); QCOMPARE(zone1.secsTo(zone3), 184 * 86400); QCOMPARE(zone1.daysTo(zone2), 184); QCOMPARE(zone1.daysTo(zone3), 184); QVERIFY(zone1 < zone2); QVERIFY(!(zone2 < zone1)); QVERIFY(zone2 == zone3); // Local zone KADateTime local1(QDate(2005, 7, 6), KADateTime::Spec(KADateTime::LocalZone)); KADateTime local2 = local1.addSecs(184 * 86400); QVERIFY(local2.isDateOnly()); QVERIFY(local2.isLocalZone()); QCOMPARE(local2.timeZone(), losAngeles); QCOMPARE(local2.date(), QDate(2006, 1, 6)); QCOMPARE(local2.time(), QTime(0, 0, 0)); KADateTime local3 = local1.addDays(184); QVERIFY(local3.isDateOnly()); QVERIFY(local3.isLocalZone()); QCOMPARE(local3.date(), QDate(2006, 1, 6)); QCOMPARE(local3.time(), QTime(0, 0, 0)); KADateTime local4 = local1.addMonths(6); QVERIFY(local4.isDateOnly()); QVERIFY(local4.isLocalZone()); QCOMPARE(local4.date(), local3.date()); QCOMPARE(local4.time(), local3.time()); KADateTime local5 = local1.addYears(4); QVERIFY(local5.isDateOnly()); QVERIFY(local5.isLocalZone()); QCOMPARE(local5.date(), QDate(2009, 7, 6)); QCOMPARE(local5.time(), QTime(0, 0, 0)); QCOMPARE(local1.secsTo(local2), 184 * 86400); QCOMPARE(local1.secsTo(local3), 184 * 86400); QCOMPARE(local1.daysTo(local2), 184); QCOMPARE(local1.daysTo(local3), 184); QVERIFY(local1 < local2); QVERIFY(!(local2 < local1)); QVERIFY(local2 == local3); // Mixed timeSpecs QCOMPARE(utc1.secsTo(offset1), 0); QCOMPARE(utc1.secsTo(offset2), 184 * 86400); QCOMPARE(offset2.secsTo(utc1), -(184 * 86400)); QVERIFY(!(utc1 < offset1)); QVERIFY(utc1 <= offset1); QVERIFY(!(offset1 < utc1)); QVERIFY(offset1 <= utc1); QCOMPARE(utc1.secsTo(zone1), 0); QCOMPARE(utc1.secsTo(zone2), 184 * 86400); QCOMPARE(zone2.secsTo(utc1), -(184 * 86400)); QVERIFY(!(utc1 > zone1)); QVERIFY(utc1 >= zone1); QVERIFY(!(zone1 > utc1)); QVERIFY(zone1 >= utc1); QCOMPARE(utc1.secsTo(local1), 0); QCOMPARE(utc1.secsTo(local2), 184 * 86400); QCOMPARE(local2.secsTo(utc1), -(184 * 86400)); QVERIFY(!(utc1 < local1)); QVERIFY(utc1 <= local1); QVERIFY(!(local1 < utc1)); QVERIFY(local1 <= utc1); QCOMPARE(offset1.secsTo(zone1), 0); QCOMPARE(offset1.secsTo(zone2), 184 * 86400); QCOMPARE(zone2.secsTo(offset1), -(184 * 86400)); QVERIFY(!(offset1 > zone1)); QVERIFY(offset1 >= zone1); QVERIFY(!(zone1 > offset1)); QVERIFY(zone1 >= offset1); QCOMPARE(offset1.secsTo(local1), 0); QCOMPARE(offset1.secsTo(local2), 184 * 86400); QCOMPARE(local2.secsTo(offset1), -(184 * 86400)); QVERIFY(!(offset1 < local1)); QVERIFY(offset1 <= local1); QVERIFY(!(local1 < offset1)); QVERIFY(local1 <= offset1); QCOMPARE(zone1.secsTo(local1), 0); QCOMPARE(zone1.secsTo(local2), 184 * 86400); QCOMPARE(local2.secsTo(zone1), -(184 * 86400)); QVERIFY(!(zone1 < local1)); QVERIFY(zone1 <= local1); QVERIFY(!(local1 < zone1)); QVERIFY(local1 <= zone1); // Mixed date/time and date-only // UTC utc3.setTime(QTime(13, 14, 15)); QVERIFY(!utc3.isDateOnly()); QCOMPARE(utc3.time(), QTime(13, 14, 15)); QCOMPARE(utc1.secsTo(utc3), 184 * 86400); KADateTime utc1t(QDate(2005, 7, 6), QTime(3, 40, 0), KADateTime::UTC); QCOMPARE(utc1t.secsTo(utc2), 184 * 86400); // UTC offset offset3.setTime(QTime(13, 14, 15)); QVERIFY(!offset3.isDateOnly()); QCOMPARE(offset3.time(), QTime(13, 14, 15)); QCOMPARE(offset1.secsTo(offset3), 184 * 86400); KADateTime offset1t(QDate(2005, 7, 6), QTime(3, 40, 0), KADateTime::Spec::OffsetFromUTC(-5400)); // -0130 QCOMPARE(offset1t.secsTo(offset2), 184 * 86400); KADateTime offset2t(QDate(2005, 7, 6), QTime(0, 40, 0), KADateTime::Spec::OffsetFromUTC(5400)); // +0130 // Zone zone3.setTime(QTime(13, 14, 15)); QVERIFY(!zone3.isDateOnly()); QCOMPARE(zone3.time(), QTime(13, 14, 15)); QCOMPARE(zone1.secsTo(zone3), 184 * 86400); KADateTime zone1t(QDate(2005, 7, 6), QTime(3, 40, 0), london); QCOMPARE(zone1t.secsTo(zone2), 184 * 86400); // Local zone local3.setTime(QTime(13, 14, 15)); QVERIFY(!local3.isDateOnly()); QCOMPARE(local3.time(), QTime(13, 14, 15)); QCOMPARE(local1.secsTo(local3), 184 * 86400); KADateTime local1t(QDate(2005, 7, 6), QTime(3, 40, 0), KADateTime::LocalZone); QCOMPARE(local1t.secsTo(local2), 184 * 86400); KADateTime local2t(QDate(2005, 7, 5), QTime(23, 40, 0), KADateTime::LocalZone); // Mixed timeSpecs QCOMPARE(utc1t.secsTo(offset1), 0); QVERIFY(utc1t != offset1); QVERIFY(offset1 != utc1t); QVERIFY(!(utc1t < offset1)); QVERIFY(utc1t <= offset1); QVERIFY(!(offset1 < utc1t)); QVERIFY(offset1 <= utc1t); QCOMPARE(utc1.secsTo(offset2t), -86400); QCOMPARE(offset2t.secsTo(utc1), 86400); QVERIFY(utc1 != offset2t); QVERIFY(offset2t != utc1); QVERIFY(utc1 > offset2t); QVERIFY(utc1 >= offset2t); QVERIFY(offset2t < utc1); QVERIFY(offset2t <= utc1); QCOMPARE(utc1t.secsTo(offset2), 184 * 86400); QCOMPARE(offset2.secsTo(utc1t), -(184 * 86400)); QCOMPARE(utc1t.secsTo(zone1), 0); QVERIFY(utc1t != zone1); QVERIFY(zone1 != utc1t); QVERIFY(!(utc1t < zone1)); QVERIFY(!(utc1t > zone1)); QVERIFY(!(zone1 < utc1t)); QVERIFY(!(zone1 > utc1t)); QCOMPARE(utc1t.secsTo(zone2), 184 * 86400); QCOMPARE(zone2.secsTo(utc1t), -(184 * 86400)); QVERIFY(utc1t != zone2); QVERIFY(zone2 != utc1t); QVERIFY(utc1t < zone2); QVERIFY(utc1t <= zone2); QVERIFY(!(zone2 < utc1t)); QVERIFY(!(zone2 <= utc1t)); QCOMPARE(utc1t.secsTo(local1), 86400); QCOMPARE(utc1t.secsTo(local2), 185 * 86400); QCOMPARE(local2.secsTo(utc1t), -(185 * 86400)); QVERIFY(utc1t != local1); QVERIFY(local1 != utc1t); QVERIFY(utc1t < local1); QVERIFY(utc1t <= local1); QVERIFY(!(local1 < utc1t)); QVERIFY(!(local1 <= utc1t)); QCOMPARE(utc1.secsTo(local2t), 0); QCOMPARE(local2t.secsTo(utc1), 0); QVERIFY(utc1 != local2t); QVERIFY(local2t != utc1); QVERIFY(!(utc1 < local2t)); QVERIFY(utc1 <= local2t); QVERIFY(!(local2t < utc1)); QVERIFY(local2t <= utc1); QCOMPARE(offset1t.secsTo(zone1), 0); QCOMPARE(offset1t.secsTo(zone2), 184 * 86400); QCOMPARE(zone2.secsTo(offset1t), -(184 * 86400)); QVERIFY(offset1t != zone1); QVERIFY(zone1 != offset1t); QVERIFY(!(offset1t > zone1)); QVERIFY(offset1t >= zone1); QVERIFY(!(zone1 > offset1t)); QVERIFY(zone1 >= offset1t); QCOMPARE(offset1t.secsTo(local1), 86400); QCOMPARE(offset1t.secsTo(local2), 185 * 86400); QCOMPARE(local2.secsTo(offset1t), -(185 * 86400)); QVERIFY(offset1t != local1); QVERIFY(local1 != offset1t); QVERIFY(offset1t < local1); QVERIFY(offset1t <= local1); QVERIFY(!(local1 < offset1t)); QVERIFY(!(local1 <= offset1t)); QCOMPARE(zone1t.secsTo(local1), 86400); QCOMPARE(zone1t.secsTo(local2), 185 * 86400); QCOMPARE(local2.secsTo(zone1t), -(185 * 86400)); QVERIFY(zone1t != local1); QVERIFY(local1 != zone1t); QVERIFY(zone1t < local1); QVERIFY(zone1t <= local1); QVERIFY(!(local1 < zone1t)); QVERIFY(!(local1 <= zone1t)); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } /////////////////////////////////////////// // Tests around daylight saving time shifts /////////////////////////////////////////// void KADateTimeTest::dstShifts() { QTimeZone london("Europe/London"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Shift from DST to standard time for the UK in 2005 was at 2005-10-30 01:00 UTC. QDateTime qdt(QDate(2005, 10, 29), QTime(23, 59, 59), Qt::UTC); KADateTime dt(qdt, london); QVERIFY(!dt.isSecondOccurrence()); QCOMPARE(dt.date(), QDate(2005, 10, 30)); QCOMPARE(dt.time(), QTime(0, 59, 59)); dt = KADateTime(QDateTime(QDate(2005, 10, 30), QTime(0, 0, 0), Qt::UTC), london); QVERIFY(!dt.isSecondOccurrence()); QCOMPARE(dt.date(), QDate(2005, 10, 30)); QCOMPARE(dt.time(), QTime(1, 0, 0)); dt = KADateTime(QDateTime(QDate(2005, 10, 30), QTime(0, 59, 59), Qt::UTC), london); QVERIFY(!dt.isSecondOccurrence()); QCOMPARE(dt.date(), QDate(2005, 10, 30)); QCOMPARE(dt.time(), QTime(1, 59, 59)); dt = KADateTime(QDateTime(QDate(2005, 10, 30), QTime(1, 0, 0), Qt::UTC), london); QVERIFY(dt.isSecondOccurrence()); QCOMPARE(dt.date(), QDate(2005, 10, 30)); QCOMPARE(dt.time(), QTime(1, 0, 0)); dt = KADateTime(QDateTime(QDate(2005, 10, 30), QTime(1, 59, 59), Qt::UTC), london); QVERIFY(dt.isSecondOccurrence()); QCOMPARE(dt.date(), QDate(2005, 10, 30)); QCOMPARE(dt.time(), QTime(1, 59, 59)); dt = KADateTime(QDateTime(QDate(2005, 10, 30), QTime(2, 0, 0), Qt::UTC), london); QVERIFY(!dt.isSecondOccurrence()); QCOMPARE(dt.date(), QDate(2005, 10, 30)); QCOMPARE(dt.time(), QTime(2, 0, 0)); dt = KADateTime(QDate(2005, 10, 30), QTime(0, 59, 59), london); dt.setSecondOccurrence(true); // this has no effect QCOMPARE(dt.toUtc().date(), QDate(2005, 10, 29)); QCOMPARE(dt.toUtc().time(), QTime(23, 59, 59)); dt = KADateTime(QDate(2005, 10, 30), QTime(1, 0, 0), london); QVERIFY(!dt.isSecondOccurrence()); QCOMPARE(dt.toUtc().date(), QDate(2005, 10, 30)); QCOMPARE(dt.toUtc().time(), QTime(0, 0, 0)); dt = KADateTime(QDate(2005, 10, 30), QTime(1, 59, 59), london); QVERIFY(!dt.isSecondOccurrence()); QCOMPARE(dt.toUtc().date(), QDate(2005, 10, 30)); QCOMPARE(dt.toUtc().time(), QTime(0, 59, 59)); dt = KADateTime(QDate(2005, 10, 30), QTime(1, 0, 0), london); dt.setSecondOccurrence(true); QCOMPARE(dt.toUtc().date(), QDate(2005, 10, 30)); QCOMPARE(dt.toUtc().time(), QTime(1, 0, 0)); QVERIFY(dt.isSecondOccurrence()); dt = KADateTime(QDate(2005, 10, 30), QTime(1, 59, 59), london); dt.setSecondOccurrence(true); QCOMPARE(dt.toUtc().date(), QDate(2005, 10, 30)); QCOMPARE(dt.toUtc().time(), QTime(1, 59, 59)); QVERIFY(dt.isSecondOccurrence()); dt = KADateTime(QDate(2005, 10, 30), QTime(2, 0, 0), london); dt.setSecondOccurrence(true); // this has no effect QVERIFY(!dt.isSecondOccurrence()); QCOMPARE(dt.toUtc().date(), QDate(2005, 10, 30)); QCOMPARE(dt.toUtc().time(), QTime(2, 0, 0)); dt = KADateTime(QDate(2005, 10, 30), QTime(0, 59, 59), london); KADateTime dt1 = dt.addSecs(1); // local time 01:00:00 QVERIFY(!dt1.isSecondOccurrence()); dt1 = dt.addSecs(3600); // local time 01:59:59 QVERIFY(!dt1.isSecondOccurrence()); dt1 = dt.addSecs(3601); // local time 01:00:00 QVERIFY(dt1.isSecondOccurrence()); dt1 = dt.addSecs(7200); // local time 01:59:59 QVERIFY(dt1.isSecondOccurrence()); dt1 = dt.addSecs(7201); // local time 02:00:00 QVERIFY(!dt1.isSecondOccurrence()); QVERIFY(KADateTime(QDate(2005, 10, 29), london) == KADateTime(QDate(2005, 10, 29), KADateTime::Spec::OffsetFromUTC(3600))); QVERIFY(KADateTime(QDate(2005, 10, 30), london) != KADateTime(QDate(2005, 10, 30), KADateTime::Spec::OffsetFromUTC(3600))); QVERIFY(KADateTime(QDate(2005, 10, 30), london) != KADateTime(QDate(2005, 10, 30), KADateTime::Spec::OffsetFromUTC(0))); QVERIFY(KADateTime(QDate(2005, 10, 31), london) == KADateTime(QDate(2005, 10, 31), KADateTime::Spec::OffsetFromUTC(0))); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } //////////////////// // String conversion //////////////////// void KADateTimeTest::strings_iso8601() { QTimeZone london("Europe/London"); bool decpt = QLocale().decimalPoint() == QLatin1Char('.'); // whether this locale uses '.' as decimal symbol // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); KADateTime dtlocal(QDate(1999, 12, 11), QTime(3, 45, 6, 12), KADateTime::LocalZone); QString s = dtlocal.toString(KADateTime::ISODate); if (decpt) { QCOMPARE(s, QStringLiteral("1999-12-11T03:45:06.012")); } else { QCOMPARE(s, QStringLiteral("1999-12-11T03:45:06,012")); } KADateTime dtlocal1 = KADateTime::fromString(s, KADateTime::ISODate); QCOMPARE(dtlocal1.qDateTime().toUTC(), dtlocal.qDateTime().toUTC()); QCOMPARE(dtlocal1.timeType(), KADateTime::LocalZone); QCOMPARE(dtlocal1.utcOffset(), -8 * 3600); QVERIFY(dtlocal1 == dtlocal); s = dtlocal.toString(KADateTime::ISODateFull); if (decpt) { QCOMPARE(s, QStringLiteral("1999-12-11T03:45:06.012-08:00")); } else { QCOMPARE(s, QStringLiteral("1999-12-11T03:45:06,012-08:00")); } dtlocal1 = KADateTime::fromString(s, KADateTime::ISODate); QCOMPARE(dtlocal1.qDateTime().toUTC(), dtlocal.qDateTime().toUTC()); QCOMPARE(dtlocal1.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dtlocal1.utcOffset(), -8 * 3600); QVERIFY(dtlocal1 == dtlocal); dtlocal.setDateOnly(true); s = dtlocal.toString(KADateTime::ISODate); QCOMPARE(s, QStringLiteral("1999-12-11")); KADateTime dtzone(QDate(1999, 6, 11), QTime(3, 45, 6, 12), london); s = dtzone.toString(KADateTime::ISODate); if (decpt) { QCOMPARE(s, QStringLiteral("1999-06-11T03:45:06.012+01:00")); } else { QCOMPARE(s, QStringLiteral("1999-06-11T03:45:06,012+01:00")); } KADateTime dtzone1 = KADateTime::fromString(s, KADateTime::ISODate); QCOMPARE(dtzone1.qDateTime().toUTC(), dtzone.qDateTime().toUTC()); QCOMPARE(dtzone1.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dtzone1.utcOffset(), 3600); QVERIFY(dtzone1 == dtzone); dtzone.setDateOnly(true); s = dtzone.toString(KADateTime::ISODate); QCOMPARE(s, QStringLiteral("1999-06-11T00:00:00+01:00")); KADateTime dtutc(QDate(1999, 12, 11), QTime(3, 45, 0), KADateTime::UTC); s = dtutc.toString(KADateTime::ISODate); QCOMPARE(s, QStringLiteral("1999-12-11T03:45:00Z")); KADateTime dtutc1 = KADateTime::fromString(s, KADateTime::ISODate); QCOMPARE(dtutc1.date(), dtutc.date()); QCOMPARE(dtutc1.time(), dtutc.time()); QCOMPARE(dtutc1.timeType(), KADateTime::UTC); QVERIFY(dtutc1 == dtutc); dtutc.setDateOnly(true); s = dtutc.toString(KADateTime::ISODate); QCOMPARE(s, QStringLiteral("1999-12-11T00:00:00Z")); // Check signed years KADateTime dtneg(QDate(-1999, 12, 11), QTime(3, 45, 6), KADateTime::LocalZone); s = dtneg.toString(KADateTime::ISODate); QCOMPARE(s, QStringLiteral("-1999-12-11T03:45:06")); KADateTime dtneg1 = KADateTime::fromString(s, KADateTime::ISODate); QCOMPARE(dtneg1.date(), dtneg.date()); QCOMPARE(dtneg1.time(), dtneg.time()); QCOMPARE(dtneg1.timeType(), KADateTime::LocalZone); QVERIFY(dtneg1 == dtneg); KADateTime dtneg2 = KADateTime::fromString(QStringLiteral("-19991211T034506"), KADateTime::ISODate); QVERIFY(dtneg2 == dtneg); dtneg.setDateOnly(true); s = dtneg.toString(KADateTime::ISODate); QCOMPARE(s, QStringLiteral("-1999-12-11")); dtneg1 = KADateTime::fromString(s, KADateTime::ISODate); QVERIFY(dtneg1.isDateOnly()); QCOMPARE(dtneg1.timeType(), KADateTime::LocalZone); QCOMPARE(dtneg1.date(), QDate(-1999, 12, 11)); dtneg2 = KADateTime::fromString(QStringLiteral("-19991211"), KADateTime::ISODate); QVERIFY(dtneg2 == dtneg1); s = QStringLiteral("+1999-12-11T03:45:06"); KADateTime dtpos = KADateTime::fromString(s, KADateTime::ISODate); QCOMPARE(dtpos.date(), QDate(1999, 12, 11)); QCOMPARE(dtpos.time(), QTime(3, 45, 6)); QCOMPARE(dtpos.timeType(), KADateTime::LocalZone); KADateTime dtpos2 = KADateTime::fromString(QStringLiteral("+19991211T034506"), KADateTime::ISODate); QVERIFY(dtpos2 == dtpos); dtpos.setDateOnly(true); s = QStringLiteral("+1999-12-11"); dtpos = KADateTime::fromString(s, KADateTime::ISODate); QVERIFY(dtpos.isDateOnly()); QCOMPARE(dtpos.timeType(), KADateTime::LocalZone); QCOMPARE(dtpos.date(), QDate(1999, 12, 11)); dtpos2 = KADateTime::fromString(QStringLiteral("+19991211"), KADateTime::ISODate); QVERIFY(dtpos2 == dtpos); // Check years with >4 digits KADateTime dtbig(QDate(123456, 12, 11), QTime(3, 45, 06), KADateTime::LocalZone); s = dtbig.toString(KADateTime::ISODate); QCOMPARE(s, QStringLiteral("123456-12-11T03:45:06")); KADateTime dtbig1 = KADateTime::fromString(s, KADateTime::ISODate); QCOMPARE(dtbig1.date(), dtbig.date()); QCOMPARE(dtbig1.time(), dtbig.time()); QCOMPARE(dtbig1.timeType(), KADateTime::LocalZone); QVERIFY(dtbig1 == dtbig); KADateTime dtbig2 = KADateTime::fromString(QStringLiteral("1234561211T034506"), KADateTime::ISODate); QVERIFY(dtbig2 == dtbig); dtbig.setDateOnly(true); s = dtbig.toString(KADateTime::ISODate); QCOMPARE(s, QStringLiteral("123456-12-11")); dtbig1 = KADateTime::fromString(s, KADateTime::ISODate); QVERIFY(dtbig1.isDateOnly()); QCOMPARE(dtbig1.timeType(), KADateTime::LocalZone); QCOMPARE(dtbig1.date(), QDate(123456, 12, 11)); dtbig2 = KADateTime::fromString(QStringLiteral("1234561211"), KADateTime::ISODate); QVERIFY(dtbig2 == dtbig1); // Check basic format strings bool negZero = true; KADateTime dt = KADateTime::fromString(QStringLiteral("20000301T1213"), KADateTime::ISODate, &negZero); QVERIFY(dt.timeType() == KADateTime::LocalZone); QVERIFY(!dt.isDateOnly()); QVERIFY(!negZero); QCOMPARE(dt.date(), QDate(2000, 3, 1)); QCOMPARE(dt.time(), QTime(12, 13, 0)); dt = KADateTime::fromString(QStringLiteral("20000301"), KADateTime::ISODate, &negZero); QVERIFY(dt.timeType() == KADateTime::LocalZone); QVERIFY(dt.isDateOnly()); QVERIFY(!negZero); QCOMPARE(dt.date(), QDate(2000, 3, 1)); KADateTime::setFromStringDefault(KADateTime::UTC); dt = KADateTime::fromString(QStringLiteral("20000301T1213"), KADateTime::ISODate); QVERIFY(dt.timeType() == KADateTime::UTC); QCOMPARE(dt.date(), QDate(2000, 3, 1)); QCOMPARE(dt.time(), QTime(12, 13, 0)); KADateTime::setFromStringDefault(KADateTime::LocalZone); dt = KADateTime::fromString(QStringLiteral("20000301T1213"), KADateTime::ISODate); QVERIFY(dt.timeSpec() == KADateTime::Spec::LocalZone()); QCOMPARE(dt.date(), QDate(2000, 3, 1)); QCOMPARE(dt.time(), QTime(12, 13, 0)); KADateTime::setFromStringDefault(london); dt = KADateTime::fromString(QStringLiteral("20000301T1213"), KADateTime::ISODate); QVERIFY(dt.timeType() == KADateTime::TimeZone); QCOMPARE(dt.date(), QDate(2000, 3, 1)); QCOMPARE(dt.time(), QTime(12, 13, 0)); KADateTime::setFromStringDefault(KADateTime::Spec::OffsetFromUTC(5000)); // = +01:23:20 dt = KADateTime::fromString(QStringLiteral("20000601T1213"), KADateTime::ISODate); QVERIFY(dt.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), 5000); QCOMPARE(dt.toUtc().date(), QDate(2000, 6, 1)); QCOMPARE(dt.toUtc().time(), QTime(10, 49, 40)); KADateTime::setFromStringDefault(KADateTime::LocalZone); dt = KADateTime::fromString(QStringLiteral("6543210301T1213"), KADateTime::ISODate, &negZero); QVERIFY(dt.timeType() == KADateTime::LocalZone); QVERIFY(!dt.isDateOnly()); QVERIFY(!negZero); QCOMPARE(dt.date(), QDate(654321, 3, 1)); QCOMPARE(dt.time(), QTime(12, 13, 0)); dt = KADateTime::fromString(QStringLiteral("6543210301"), KADateTime::ISODate, &negZero); QVERIFY(dt.isDateOnly()); QVERIFY(!negZero); QCOMPARE(dt.date(), QDate(654321, 3, 1)); dt = KADateTime::fromString(QStringLiteral("-47120301T1213"), KADateTime::ISODate, &negZero); QVERIFY(dt.timeType() == KADateTime::LocalZone); QVERIFY(!dt.isDateOnly()); QVERIFY(!negZero); QCOMPARE(dt.date(), QDate(-4712, 3, 1)); QCOMPARE(dt.time(), QTime(12, 13, 0)); dt = KADateTime::fromString(QStringLiteral("-47120301"), KADateTime::ISODate, &negZero); QVERIFY(dt.isDateOnly()); QVERIFY(!negZero); QCOMPARE(dt.date(), QDate(-4712, 3, 1)); // Check strings containing day-of-the-year dt = KADateTime::fromString(QStringLiteral("1999-060T19:20:21.06-11:20"), KADateTime::ISODate); QVERIFY(dt.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), -11 * 3600 - 20 * 60); QCOMPARE(dt.date(), QDate(1999, 3, 1)); QCOMPARE(dt.time(), QTime(19, 20, 21, 60)); dt = KADateTime::fromString(QStringLiteral("1999-060T19:20:21,06-11:20"), KADateTime::ISODate); QVERIFY(dt.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), -11 * 3600 - 20 * 60); QCOMPARE(dt.date(), QDate(1999, 3, 1)); QCOMPARE(dt.time(), QTime(19, 20, 21, 60)); dt = KADateTime::fromString(QStringLiteral("1999060T192021.06-1120"), KADateTime::ISODate); QVERIFY(dt.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), -11 * 3600 - 20 * 60); QCOMPARE(dt.date(), QDate(1999, 3, 1)); QCOMPARE(dt.time(), QTime(19, 20, 21, 60)); dt = KADateTime::fromString(QStringLiteral("1999-060"), KADateTime::ISODate); QVERIFY(dt.timeType() == KADateTime::LocalZone); QVERIFY(dt.isDateOnly()); QCOMPARE(dt.date(), QDate(1999, 3, 1)); // Check 24:00:00 dt = KADateTime::fromString(QStringLiteral("1999-06-11T24:00:00+03:00"), KADateTime::ISODate); QCOMPARE(dt.date(), QDate(1999, 6, 12)); QCOMPARE(dt.time(), QTime(0, 0, 0)); dt = KADateTime::fromString(QStringLiteral("1999-06-11T24:00:01+03:00"), KADateTime::ISODate); QVERIFY(!dt.isValid()); // Check leap second dt = KADateTime::fromString(QStringLiteral("1999-06-11T23:59:60Z"), KADateTime::ISODate); QCOMPARE(dt.date(), QDate(1999, 6, 11)); QCOMPARE(dt.time(), QTime(23, 59, 59)); dt = KADateTime::fromString(QStringLiteral("1999-06-11T13:59:60Z"), KADateTime::ISODate); QVERIFY(!dt.isValid()); dt = KADateTime::fromString(QStringLiteral("1999-06-11T13:59:60-10:00"), KADateTime::ISODate); QCOMPARE(dt.date(), QDate(1999, 6, 11)); QCOMPARE(dt.time(), QTime(13, 59, 59)); dt = KADateTime::fromString(QStringLiteral("1999-06-11T23:59:60-10:00"), KADateTime::ISODate); QVERIFY(!dt.isValid()); // Check negZero dt = KADateTime::fromString(QStringLiteral("1999-060T19:20:21.06-00:00"), KADateTime::ISODate, &negZero); QVERIFY(negZero); dt = KADateTime::fromString(QStringLiteral("1999-060T19:20:21.06+00:00"), KADateTime::ISODate, &negZero); QVERIFY(!negZero); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::strings_rfc2822() { QTimeZone london("Europe/London"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); bool negZero = true; KADateTime dtlocal(QDate(1999, 12, 11), QTime(3, 45, 6), KADateTime::LocalZone); QString s = dtlocal.toString(KADateTime::RFCDate); QCOMPARE(s, QStringLiteral("11 Dec 1999 03:45:06 -0800")); KADateTime dtlocal1 = KADateTime::fromString(s, KADateTime::RFCDate, &negZero); QCOMPARE(dtlocal1.qDateTime().toUTC(), dtlocal.qDateTime().toUTC()); QCOMPARE(dtlocal1.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dtlocal1.utcOffset(), -8 * 3600); QVERIFY(dtlocal1 == dtlocal); QVERIFY(!negZero); KADateTime dtlocal2 = KADateTime::fromString(s, KADateTime::RFCDateDay); QVERIFY(!dtlocal2.isValid()); s = dtlocal.toString(KADateTime::RFCDateDay); QCOMPARE(s, QStringLiteral("Sat, 11 Dec 1999 03:45:06 -0800")); dtlocal2 = KADateTime::fromString(s, KADateTime::RFCDate); QVERIFY(dtlocal1 == dtlocal2); QCOMPARE(dtlocal1.date(), dtlocal2.date()); QCOMPARE(dtlocal1.time(), dtlocal2.time()); dtlocal2 = KADateTime::fromString(s, KADateTime::RFCDateDay); QVERIFY(dtlocal1 == dtlocal2); dtlocal2 = KADateTime::fromString(QStringLiteral("Saturday, 11-Dec-99 03:45:06 -0800"), KADateTime::RFCDate); QVERIFY(dtlocal1 == dtlocal2); dtlocal2 = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 PST"), KADateTime::RFCDate, &negZero); QVERIFY(dtlocal1 == dtlocal2); QVERIFY(!negZero); dtlocal.setDateOnly(true); s = dtlocal.toString(KADateTime::RFCDate); QCOMPARE(s, QStringLiteral("11 Dec 1999 00:00 -0800")); s = dtlocal.toString(KADateTime::RFCDateDay); QCOMPARE(s, QStringLiteral("Sat, 11 Dec 1999 00:00 -0800")); KADateTime dtzone(QDate(1999, 6, 11), QTime(3, 45, 6), london); s = dtzone.toString(KADateTime::RFCDate); QCOMPARE(s, QStringLiteral("11 Jun 1999 03:45:06 +0100")); KADateTime dtzone1 = KADateTime::fromString(s, KADateTime::RFCDate); QCOMPARE(dtzone1.qDateTime().toUTC(), dtzone.qDateTime().toUTC()); QCOMPARE(dtzone1.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dtzone1.utcOffset(), 3600); QVERIFY(dtzone1 == dtzone); KADateTime dtzone2 = KADateTime::fromString(s, KADateTime::RFCDateDay); QVERIFY(!dtzone2.isValid()); s = dtzone.toString(KADateTime::RFCDateDay); QCOMPARE(s, QStringLiteral("Fri, 11 Jun 1999 03:45:06 +0100")); dtzone2 = KADateTime::fromString(s, KADateTime::RFCDate); QVERIFY(dtzone1 == dtzone2); QCOMPARE(dtzone1.date(), dtzone2.date()); QCOMPARE(dtzone1.time(), dtzone2.time()); dtzone2 = KADateTime::fromString(s, KADateTime::RFCDateDay, &negZero); QVERIFY(dtzone1 == dtzone2); QVERIFY(!negZero); dtzone2 = KADateTime::fromString(QStringLiteral("Friday, 11-Jun-99 03:45:06 +0100"), KADateTime::RFCDateDay); QVERIFY(dtzone1 == dtzone2); dtzone.setDateOnly(true); s = dtzone.toString(KADateTime::RFCDate); QCOMPARE(s, QStringLiteral("11 Jun 1999 00:00 +0100")); s = dtzone.toString(KADateTime::RFCDateDay); QCOMPARE(s, QStringLiteral("Fri, 11 Jun 1999 00:00 +0100")); KADateTime dtutc(QDate(1999, 12, 11), QTime(3, 45, 00), KADateTime::UTC); s = dtutc.toString(KADateTime::RFCDate); QCOMPARE(s, QStringLiteral("11 Dec 1999 03:45 +0000")); KADateTime dtutc1 = KADateTime::fromString(s, KADateTime::RFCDate, &negZero); QCOMPARE(dtutc1.date(), dtutc.date()); QCOMPARE(dtutc1.time(), dtutc.time()); QCOMPARE(dtutc1.timeType(), KADateTime::UTC); QVERIFY(dtutc1 == dtutc); QVERIFY(!negZero); KADateTime dtutc2 = KADateTime::fromString(s, KADateTime::RFCDateDay); QVERIFY(!dtutc2.isValid()); s = dtutc.toString(KADateTime::RFCDateDay); QCOMPARE(s, QStringLiteral("Sat, 11 Dec 1999 03:45 +0000")); dtutc2 = KADateTime::fromString(s, KADateTime::RFCDate); QVERIFY(dtutc1 == dtutc2); QCOMPARE(dtutc1.date(), dtutc2.date()); QCOMPARE(dtutc1.time(), dtutc2.time()); dtutc2 = KADateTime::fromString(s, KADateTime::RFCDateDay); QVERIFY(dtutc1 == dtutc2); dtutc2 = KADateTime::fromString(QStringLiteral("Saturday, 11-Dec-99 03:45 +0000"), KADateTime::RFCDate); QVERIFY(dtutc1 == dtutc2); dtutc.setDateOnly(true); s = dtutc.toString(KADateTime::RFCDate); QCOMPARE(s, QStringLiteral("11 Dec 1999 00:00 +0000")); s = dtutc.toString(KADateTime::RFCDateDay); QCOMPARE(s, QStringLiteral("Sat, 11 Dec 1999 00:00 +0000")); // Check '-0000' and unknown/invalid time zone names dtutc2 = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45 -0000"), KADateTime::RFCDate, &negZero); QVERIFY(dtutc1 == dtutc2); QVERIFY(negZero); dtutc2 = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45 B"), KADateTime::RFCDate, &negZero); QVERIFY(dtutc1 == dtutc2); QVERIFY(negZero); dtutc2 = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45 BCDE"), KADateTime::RFCDate, &negZero); QVERIFY(dtutc1 == dtutc2); QVERIFY(negZero); // Check named time offsets KADateTime dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 UT"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::UTC); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 GMT"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::UTC); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 EDT"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dtzname.utcOffset(), -4 * 3600); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 EST"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dtzname.utcOffset(), -5 * 3600); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 CDT"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dtzname.utcOffset(), -5 * 3600); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 CST"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dtzname.utcOffset(), -6 * 3600); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 MDT"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dtzname.utcOffset(), -6 * 3600); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 MST"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dtzname.utcOffset(), -7 * 3600); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 PDT"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dtzname.utcOffset(), -7 * 3600); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); dtzname = KADateTime::fromString(QStringLiteral("11 Dec 1999 03:45:06 PST"), KADateTime::RFCDate, &negZero); QVERIFY(dtzname.timeType() == KADateTime::OffsetFromUTC); QCOMPARE(dtzname.utcOffset(), -8 * 3600); QCOMPARE(dtzname.date(), QDate(1999, 12, 11)); QCOMPARE(dtzname.time(), QTime(3, 45, 6)); QVERIFY(!negZero); // Check leap second KADateTime dt = KADateTime::fromString(QStringLiteral("11 Dec 1999 23:59:60 -0000"), KADateTime::RFCDate); QCOMPARE(dt.date(), QDate(1999, 12, 11)); QCOMPARE(dt.time(), QTime(23, 59, 59)); dt = KADateTime::fromString(QStringLiteral("11 Dec 1999 13:59:60 -0000"), KADateTime::RFCDate); QVERIFY(!dt.isValid()); dt = KADateTime::fromString(QStringLiteral("11 Jun 1999 13:59:60 -1000"), KADateTime::RFCDate); QCOMPARE(dt.date(), QDate(1999, 6, 11)); QCOMPARE(dt.time(), QTime(13, 59, 59)); dt = KADateTime::fromString(QStringLiteral("11 Dec 1999 23:59:60 -1000"), KADateTime::RFCDate); QVERIFY(!dt.isValid()); // Check erroneous strings: dtutc2 = KADateTime::fromString(QStringLiteral("11 Dec 1999 23:59:60 -00:00"), KADateTime::RFCDate); QVERIFY(!dtutc2.isValid()); // colon in UTC offset dtutc2 = KADateTime::fromString(QStringLiteral("Sun, 11 Dec 1999 03:45 +0000"), KADateTime::RFCDate); QVERIFY(!dtutc2.isValid()); // wrong weekday dtutc2 = KADateTime::fromString(QStringLiteral("Satu, 11 Dec 1999 03:45 +0000"), KADateTime::RFCDate); QVERIFY(!dtutc2.isValid()); // bad weekday dtutc2 = KADateTime::fromString(QStringLiteral("11 Dece 1999 03:45 +0000"), KADateTime::RFCDate); QVERIFY(!dtutc2.isValid()); // bad month dtutc2 = KADateTime::fromString(QStringLiteral("11-Dec 1999 03:45 +0000"), KADateTime::RFCDate); QVERIFY(!dtutc2.isValid()); // only one hyphen in date // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::strings_rfc3339() { QTimeZone london("Europe/London"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); bool negZero = true; KADateTime dtlocal(QDate(1999, 2, 9), QTime(3, 45, 6), KADateTime::LocalZone); QString s = dtlocal.toString(KADateTime::RFC3339Date); QCOMPARE(s, QStringLiteral("1999-02-09T03:45:06-08:00")); KADateTime dtlocal1 = KADateTime::fromString(s, KADateTime::RFC3339Date, &negZero); QCOMPARE(dtlocal1.qDateTime().toUTC(), dtlocal.qDateTime().toUTC()); QCOMPARE(dtlocal1.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dtlocal1.utcOffset(), -8 * 3600); QVERIFY(dtlocal1 == dtlocal); QVERIFY(!negZero); dtlocal.setDateOnly(true); s = dtlocal.toString(KADateTime::RFC3339Date); QCOMPARE(s, QStringLiteral("1999-02-09T00:00:00-08:00")); KADateTime dtzone(QDate(1999, 6, 9), QTime(3, 45, 06), london); s = dtzone.toString(KADateTime::RFC3339Date); QCOMPARE(s, QStringLiteral("1999-06-09T03:45:06+01:00")); KADateTime dtzone1 = KADateTime::fromString(s, KADateTime::RFC3339Date); QCOMPARE(dtzone1.qDateTime().toUTC(), dtzone.qDateTime().toUTC()); QCOMPARE(dtzone1.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dtzone1.utcOffset(), 3600); QVERIFY(dtzone1 == dtzone); dtzone.setDateOnly(true); s = dtzone.toString(KADateTime::RFC3339Date); QCOMPARE(s, QStringLiteral("1999-06-09T00:00:00+01:00")); KADateTime dtutc(QDate(1999, 2, 9), QTime(3, 45, 00), KADateTime::UTC); s = dtutc.toString(KADateTime::RFC3339Date); QCOMPARE(s, QStringLiteral("1999-02-09T03:45:00Z")); KADateTime dtutc1 = KADateTime::fromString(s, KADateTime::RFC3339Date, &negZero); QCOMPARE(dtutc1.date(), dtutc.date()); QCOMPARE(dtutc1.time(), dtutc.time()); QCOMPARE(dtutc1.timeType(), KADateTime::UTC); QVERIFY(dtutc1 == dtutc); QVERIFY(!negZero); KADateTime dtutc2 = KADateTime::fromString(QStringLiteral("1999-02-09t03:45:00z"), KADateTime::RFC3339Date, &negZero); QVERIFY(dtutc1 == dtutc2); dtutc.setDateOnly(true); s = dtutc.toString(KADateTime::RFC3339Date); QCOMPARE(s, QStringLiteral("1999-02-09T00:00:00Z")); // Check '-00:00' (specifies unknown local offset) dtutc2 = KADateTime::fromString(QStringLiteral("1999-02-09T03:45:00-00:00"), KADateTime::RFC3339Date, &negZero); QVERIFY(dtutc1 == dtutc2); QVERIFY(negZero); dtutc2 = KADateTime::fromString(QStringLiteral("1999-02-09T03:45:00+00:00"), KADateTime::RFC3339Date, &negZero); QVERIFY(dtutc1 == dtutc2); QVERIFY(!negZero); // Check leap second KADateTime dt = KADateTime::fromString(QStringLiteral("1999-02-09T23:59:60z"), KADateTime::RFC3339Date); QCOMPARE(dt.date(), QDate(1999, 2, 9)); QCOMPARE(dt.time(), QTime(23, 59, 59)); dt = KADateTime::fromString(QStringLiteral("1999-02-09T23:59:60+00:00"), KADateTime::RFC3339Date); QCOMPARE(dt.toUtc().date(), QDate(1999, 2, 9)); QCOMPARE(dt.toUtc().time(), QTime(23, 59, 59)); dt = KADateTime::fromString(QStringLiteral("1999-02-09T13:59:60-00:00"), KADateTime::RFC3339Date); QVERIFY(!dt.isValid()); dt = KADateTime::fromString(QStringLiteral("1999-06-11T13:59:60-10:00"), KADateTime::RFC3339Date); QCOMPARE(dt.toUtc().date(), QDate(1999, 6, 11)); QCOMPARE(dt.toUtc().time(), QTime(23, 59, 59)); dt = KADateTime::fromString(QStringLiteral("1999-12-11T23:59:60-10:00"), KADateTime::RFC3339Date); QVERIFY(!dt.isValid()); // Check erroneous strings: dtutc2 = KADateTime::fromString(QStringLiteral("1999-02-09 03:45:00"), KADateTime::RFC3339Date, &negZero); QVERIFY(!dtutc2.isValid()); dtutc2 = KADateTime::fromString(QStringLiteral("1999-02-09T03:45:00B"), KADateTime::RFC3339Date, &negZero); QVERIFY(!dtutc2.isValid()); dtutc2 = KADateTime::fromString(QStringLiteral("1999-02-09T23:59:60-0000"), KADateTime::RFC3339Date); QVERIFY(!dtutc2.isValid()); // no colon in UTC offset dtutc2 = KADateTime::fromString(QStringLiteral("19990-12-10T03:45:01+00:00"), KADateTime::RFC3339Date); QVERIFY(!dtutc2.isValid()); // bad year dtutc2 = KADateTime::fromString(QStringLiteral("1999-13-10T03:45:01+00:00"), KADateTime::RFC3339Date); QVERIFY(!dtutc2.isValid()); // bad month dtutc2 = KADateTime::fromString(QStringLiteral("1999-10-32T03:45:01+00:00"), KADateTime::RFC3339Date); QVERIFY(!dtutc2.isValid()); // bad day dtutc2 = KADateTime::fromString(QStringLiteral("1999-1209T03:45:00+00:00"), KADateTime::RFC3339Date); QVERIFY(!dtutc2.isValid()); // only one hyphen in date dtutc2 = KADateTime::fromString(QStringLiteral("1999-12T03:45:00+00:00"), KADateTime::RFC3339Date); QVERIFY(!dtutc2.isValid()); // no day of month // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::strings_qttextdate() { QTimeZone london("Europe/London"); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); bool negZero = true; KADateTime dtlocal(QDate(1999, 12, 11), QTime(3, 45, 6), KADateTime::LocalZone); QString s = dtlocal.toString(KADateTime::QtTextDate); QCOMPARE(s, QStringLiteral("Sat Dec 11 03:45:06 1999")); KADateTime dtlocal1 = KADateTime::fromString(s, KADateTime::QtTextDate, &negZero); QCOMPARE(dtlocal1.qDateTime().toUTC(), dtlocal.qDateTime().toUTC()); QCOMPARE(dtlocal1.timeType(), KADateTime::LocalZone); QCOMPARE(dtlocal1.utcOffset(), -8 * 3600); QVERIFY(dtlocal1 == dtlocal); QVERIFY(!dtlocal1.isDateOnly()); QVERIFY(!negZero); dtlocal.setDateOnly(true); s = dtlocal.toString(KADateTime::QtTextDate); QCOMPARE(s, QStringLiteral("Sat Dec 11 1999")); dtlocal1 = KADateTime::fromString(s, KADateTime::QtTextDate, &negZero); QVERIFY(dtlocal1.isDateOnly()); QCOMPARE(dtlocal1.date(), QDate(1999, 12, 11)); QCOMPARE(dtlocal1.timeType(), KADateTime::LocalZone); QCOMPARE(dtlocal1.utcOffset(), -8 * 3600); KADateTime dtzone(QDate(1999, 6, 11), QTime(3, 45, 6), london); s = dtzone.toString(KADateTime::QtTextDate); QCOMPARE(s, QStringLiteral("Fri Jun 11 03:45:06 1999 +0100")); KADateTime dtzone1 = KADateTime::fromString(s, KADateTime::QtTextDate); QCOMPARE(dtzone1.qDateTime().toUTC(), dtzone.qDateTime().toUTC()); QCOMPARE(dtzone1.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dtzone1.utcOffset(), 3600); QVERIFY(!dtzone1.isDateOnly()); QVERIFY(dtzone1 == dtzone); KADateTime dtzone2 = KADateTime::fromString(s, KADateTime::QtTextDate, &negZero); QVERIFY(dtzone1 == dtzone2); QVERIFY(!negZero); dtzone.setDateOnly(true); s = dtzone.toString(KADateTime::QtTextDate); QCOMPARE(s, QStringLiteral("Fri Jun 11 1999 +0100")); dtzone1 = KADateTime::fromString(s, KADateTime::QtTextDate, &negZero); QVERIFY(dtzone1.isDateOnly()); QCOMPARE(dtzone1.date(), QDate(1999, 6, 11)); QCOMPARE(dtzone1.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dtzone1.utcOffset(), 3600); KADateTime dtutc(QDate(1999, 12, 11), QTime(3, 45, 0), KADateTime::UTC); s = dtutc.toString(KADateTime::QtTextDate); QCOMPARE(s, QStringLiteral("Sat Dec 11 03:45:00 1999 +0000")); KADateTime dtutc1 = KADateTime::fromString(s, KADateTime::QtTextDate, &negZero); QCOMPARE(dtutc1.date(), dtutc.date()); QCOMPARE(dtutc1.time(), dtutc.time()); QCOMPARE(dtutc1.timeType(), KADateTime::UTC); QVERIFY(dtutc1 == dtutc); QVERIFY(!dtutc1.isDateOnly()); QVERIFY(!negZero); dtutc.setDateOnly(true); s = dtutc.toString(KADateTime::QtTextDate); QCOMPARE(s, QStringLiteral("Sat Dec 11 1999 +0000")); dtutc1 = KADateTime::fromString(s, KADateTime::QtTextDate, &negZero); QVERIFY(dtutc1.isDateOnly()); QCOMPARE(dtutc1.date(), QDate(1999, 12, 11)); QCOMPARE(dtutc1.timeType(), KADateTime::UTC); // Check '-0000' KADateTime dtutc2 = KADateTime::fromString(QStringLiteral("Sat Dec 11 03:45:00 1999 -0000"), KADateTime::QtTextDate, &negZero); QVERIFY(dtutc1 != dtutc2); QVERIFY(negZero); // Check erroneous strings dtutc2 = KADateTime::fromString(QStringLiteral("Sat Dec 11 03:45:00 1999 GMT"), KADateTime::QtTextDate, &negZero); QVERIFY(!dtutc2.isValid()); dtutc2 = KADateTime::fromString(QStringLiteral("Sun Dec 11 03:45:00 1999 +0000"), KADateTime::QtTextDate); QVERIFY(dtutc2.isValid()); // wrong weekday: accepted by Qt!! dtutc2 = KADateTime::fromString(QStringLiteral("Satu, Dec 11 03:45:00 1999 +0000"), KADateTime::QtTextDate); QVERIFY(dtutc2.isValid()); // bad weekday, accepted by Qt (since 4.3) dtutc2 = KADateTime::fromString(QStringLiteral("Sat Dece 11 03:45:00 1999 +0000"), KADateTime::QtTextDate); QVERIFY(!dtutc2.isValid()); // bad month, not accepted by Qt anymore (since 4.3) // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::strings_format() { QTimeZone london("Europe/London"); QTimeZone paris("Europe/Paris"); QTimeZone berlin("Europe/Berlin"); QTimeZone cairo("Africa/Cairo"); QList zones; zones.append(london); zones.append(paris); zones.append(berlin); zones.append(cairo); // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // toString() QString all = QStringLiteral("%Y.%y.%m.%:m.%B.%b.%d.%e.%A.%a-%H.%k.%I.%l.%M.%S?%:s?%P.%p.%:u.%z.%Z.%:Z.%:A.%:a.%:B.%:b/%:S.%:z.%%."); KADateTime dt(QDate(1999, 2, 3), QTime(6, 5, 0), KADateTime::LocalZone); QString s = dt.toString(all); QCOMPARE(s, QStringLiteral("1999.99.02.2.%1.%2.03.3.%3.%4-06.6.06.6.05.00?000?am.AM.-08.-0800.PST.America/Los_Angeles.Wednesday.Wed.February.Feb/.-08:00.%.") .arg(QDate::longMonthName(2)) .arg(QDate::shortMonthName(2)) .arg(QDate::longDayName(3)) .arg(QDate::shortDayName(3))); KADateTime dtzone(QDate(1970, 4, 30), QTime(12, 45, 16, 25), london); s = dtzone.toString(all); QCOMPARE(s, QStringLiteral("1970.70.04.4.%1.%2.30.30.%3.%4-12.12.12.12.45.16?025?pm.PM.+01.+0100.BST.Europe/London.Thursday.Thu.April.Apr/:16.+01:00.%.") .arg(QDate::longMonthName(4)) .arg(QDate::shortMonthName(4)) .arg(QDate::longDayName(4)) .arg(QDate::shortDayName(4))); KADateTime dtutc(QDate(2000, 12, 31), QTime(13, 45, 16, 100), KADateTime::UTC); s = dtutc.toString(all); QCOMPARE(s, QStringLiteral("2000.00.12.12.%1.%2.31.31.%3.%4-13.13.01.1.45.16?100?pm.PM.+00.+0000.UTC.UTC.Sunday.Sun.December.Dec/:16.+00:00.%.") .arg(QDate::longMonthName(12)) .arg(QDate::shortMonthName(12)) .arg(QDate::longDayName(7)) .arg(QDate::shortDayName(7))); // fromString() without QList parameter dt = KADateTime::fromString(QStringLiteral("2005/9/05/20:2,03"), QStringLiteral("%Y/%:m/%d/%S:%k,%M")); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(2, 3, 20)); QCOMPARE(dt.timeType(), KADateTime::LocalZone); dt = KADateTime::fromString(QStringLiteral("%1pm05ab%2t/052/20:2,03+10") .arg(QDate::longDayName(1)) .arg(QDate::longMonthName(9)), QStringLiteral("%a%p%yab%Bt/%e2/%S:%l,%M %z")); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 3, 20)); QCOMPARE(dt.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), 10 * 3600); dt = KADateTime::fromString(QStringLiteral("%1pm05ab%2t/052/20:2,03+10") .arg(QDate::shortDayName(1)) .arg(QDate::shortMonthName(9)), QStringLiteral("%a%p%yab%Bt/%d2/%s:%l,%:M %z")); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 3, 20)); QCOMPARE(dt.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), 10 * 3600); dt = KADateTime::fromString(QStringLiteral("monpm05absEpt/052/20:2,03+10"), QStringLiteral("%a%p%yab%Bt/%d2/%S:%l,%M %z")); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 3, 20)); QCOMPARE(dt.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), 10 * 3600); dt = KADateTime::fromString(QStringLiteral("monDAYpm05absEptemBert/052/20:2,03+10"), QStringLiteral("%a%p%yab%Bt/%e2/%S:%l,%M %z")); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 3, 20)); QCOMPARE(dt.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), 10 * 3600); dt = KADateTime::fromString(QStringLiteral("monDAYpm05abmzatemer/052/20:2,03+10"), QStringLiteral("%a%p%yab%B/%e2/%S:%l,%M %z")); QVERIFY(!dt.isValid()); // invalid month name dt = KADateTime::fromString(QStringLiteral("monDApm05absep/052/20:2,03+10"), QStringLiteral("%a%p%yab%B/%e2/%S:%l,%M %z")); QVERIFY(!dt.isValid()); // invalid day name dt = KADateTime::fromString(QStringLiteral("mONdAYPM2005absEpt/052/20:02,03+1000"), QStringLiteral("%:A%:p%Yab%Bt/%d2/%S:%I,%M %:u")); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 3, 20)); QCOMPARE(dt.utcOffset(), 10 * 3600); QCOMPARE(dt.timeType(), KADateTime::OffsetFromUTC); KADateTime dtlocal = KADateTime::fromString(QStringLiteral("mONdAYPM2005abSept/052/20:02,03+100"), QStringLiteral("%:A%:p%Yab%Bt/%e2/%S:%l,%M %:u")); QVERIFY(!dtlocal.isValid()); // wrong number of digits in UTC offset dtlocal = KADateTime::fromString(QStringLiteral("mONdAYPM2005abSept/052/20:02,03+1"), QStringLiteral("%:A%:p%Yab%Bt/%d2/%S:%I,%M %z")); QVERIFY(!dtlocal.isValid()); // wrong number of digits in UTC offset dtlocal = KADateTime::fromString(QStringLiteral("mONdAYPM2005absEpt/052/20:13,03+1000"), QStringLiteral("%:A%:p%Yab%Bt/%d2/%S:%I,%M %:u")); QVERIFY(!dtlocal.isValid()); // hours out of range for am/pm dtlocal = KADateTime::fromString(QStringLiteral("mONdAYPM2005absEpt/052/20:00,03+1000"), QStringLiteral("%:A%:p%Yab%Bt/%d2/%S:%I,%M %:u")); QVERIFY(!dtlocal.isValid()); // hours out of range for am/pm // fromString() with QList parameter dt = KADateTime::fromString(QStringLiteral("mon 2005/9/05/20:2,03"), QStringLiteral("%:a %Y/%:m/%e/%S:%k,%M"), &zones); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(2, 3, 20)); QCOMPARE(dt.timeType(), KADateTime::LocalZone); dt = KADateTime::fromString(QStringLiteral("tue 2005/9/05/20:2,03"), QStringLiteral("%:a %Y/%:m/%d/%S:%k,%M"), &zones); QVERIFY(!dt.isValid()); // wrong day-of-week dt = KADateTime::fromString(QStringLiteral("pm2005absEpt/05monday/20:2,03+03:00"), QStringLiteral("%p%Yab%Bt/%e%:A/%S:%l,%M %:z"), &zones); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 3, 20)); QCOMPARE(dt.timeType(), KADateTime::TimeZone); QCOMPARE(dt.utcOffset(), 3 * 3600); QCOMPARE(dt.timeZone(), cairo); dt = KADateTime::fromString(QStringLiteral("pm2005absEpt/05sunday/20:2,03+03:00"), QStringLiteral("%p%Yab%Bt/%d%A/%S:%l,%M %:z"), &zones); QVERIFY(!dt.isValid()); // wrong day-of-week dtutc = KADateTime::fromString(QStringLiteral("2000-01-01T00:00:00.000+0000"), QStringLiteral("%Y-%m-%dT%H:%M%:S%:s%z")); QVERIFY(dtutc.isValid()); dt = KADateTime::fromString(QStringLiteral("2000-01-01T05:00:00.000+0500"), QStringLiteral("%Y-%m-%dT%H:%M%:S%:s%z")); QVERIFY(dt.isValid()); QVERIFY(dtutc == dt); dt = KADateTime::fromString(QStringLiteral("1999-12-31T20:30:00.000-0330"), QStringLiteral("%Y-%m-%dT%H:%M%:S%:s%z")); QVERIFY(dt.isValid()); QVERIFY(dtutc == dt); dt = KADateTime::fromString(QStringLiteral("200509051430:01.3+0100"), QStringLiteral("%Y%m%d%H%M%:S%:s%z"), &zones, true); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 30, 01, 300)); QCOMPARE(dt.timeType(), KADateTime::TimeZone); QCOMPARE(dt.timeZone(), london); QCOMPARE(dt.utcOffset(), 3600); dt = KADateTime::fromString(QStringLiteral("200509051430:01.3+0500"), QStringLiteral("%Y%m%d%H%M%:S%:s%z"), &zones, false); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 30, 01, 300)); QCOMPARE(dt.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), 5 * 3600); dt = KADateTime::fromString(QStringLiteral("200509051430:01.3+0200"), QStringLiteral("%Y%m%d%H%M%:S%:s%z"), &zones, true); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 30, 01, 300)); QCOMPARE(dt.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), 2 * 3600); dt = KADateTime::fromString(QStringLiteral("200509051430:01.3+0200"), QStringLiteral("%Y%m%d%H%M%:S%:s%z"), &zones, false); QVERIFY(!dt.isValid()); // matches paris and berlin dt = KADateTime::fromString(QStringLiteral("2005September051430 CEST"), QStringLiteral("%Y%:B%d%H%M%:S %Z"), &zones, true); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(14, 30, 0)); QCOMPARE(dt.timeType(), KADateTime::OffsetFromUTC); QCOMPARE(dt.utcOffset(), 2 * 3600); dt = KADateTime::fromString(QStringLiteral("2005September051430 CEST"), QStringLiteral("%Y%:B%d%H%M%:S %Z"), &zones, false); QVERIFY(!dt.isValid()); // matches paris and berlin dt = KADateTime::fromString(QStringLiteral("pm05absEptembeRt/ 052/ 20:12,03+0100"), QStringLiteral("%:P%yab%:bt/ %e2/%t%S:%l,%M %z"), &zones); QCOMPARE(dt.date(), QDate(2005, 9, 5)); QCOMPARE(dt.time(), QTime(12, 3, 20)); QCOMPARE(dt.timeType(), KADateTime::TimeZone); QCOMPARE(dt.utcOffset(), 3600); QCOMPARE(dt.timeZone(), london); dt = KADateTime::fromString(QStringLiteral("2005absEpt/042sun/20.0123456:12Am,3Africa/Cairo%"), QStringLiteral("%Yab%bt/%e2%a/%S%:s:%I%P,%:M %:Z%%"), &zones); QCOMPARE(dt.date(), QDate(2005, 9, 4)); QCOMPARE(dt.time(), QTime(0, 3, 20, 12)); QCOMPARE(dt.timeType(), KADateTime::TimeZone); QCOMPARE(dt.timeZone(), cairo); QCOMPARE(dt.utcOffset(), 3 * 3600); // Test large and minimum date values dt = KADateTime(QDate(-2005, 9, 5), QTime(0, 0, 06, 1), KADateTime::LocalZone); s = dt.toString(QStringLiteral("%Y")); QCOMPARE(s, QStringLiteral("-2005")); dt = KADateTime(QDate(-15, 9, 5), QTime(0, 0, 06, 1), KADateTime::LocalZone); s = dt.toString(QStringLiteral("%Y")); QCOMPARE(s, QStringLiteral("-0015")); dt = KADateTime::fromString(QStringLiteral("-471209051430:01.3+0500"), QStringLiteral("%Y%m%d%H%M%:S%:s%z")); QCOMPARE(dt.date(), QDate(-4712, 9, 5)); QCOMPARE(dt.time(), QTime(14, 30, 1, 300)); QCOMPARE(dt.utcOffset(), 5 * 3600); QVERIFY(dt.isValid()); dt = KADateTime::fromString(QStringLiteral("999909051430:01.3+0500"), QStringLiteral("%Y%m%d%H%M%:S%:s%z")); QCOMPARE(dt.date(), QDate(9999, 9, 5)); QCOMPARE(dt.time(), QTime(14, 30, 1, 300)); QCOMPARE(dt.utcOffset(), 5 * 3600); QVERIFY(dt.isValid()); dt = KADateTime::fromString(QStringLiteral("123456.09051430:01.3+0500"), QStringLiteral("%:Y.%m%d%H%M%:S%:s%z")); QCOMPARE(dt.date(), QDate(123456, 9, 5)); QCOMPARE(dt.time(), QTime(14, 30, 1, 300)); QCOMPARE(dt.utcOffset(), 5 * 3600); QVERIFY(dt.isValid()); s = dt.toString(QStringLiteral("%Y")); QCOMPARE(s, QStringLiteral("123456")); dt = KADateTime::fromString(QStringLiteral("-471411231430:01.3+0500"), QStringLiteral("%Y%m%d%H%M%:S%:s%z")); QVERIFY(dt.isValid()); QVERIFY(dt.date().toJulianDay() == -1); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } #ifdef COMPILING_TESTS // This test requires a specially-modified kalarmcal2, so use the same compile guard here // as used in kalarmcal2/src/kadatetime.cpp void KADateTimeTest::cache() { QTimeZone london("Europe/London"); QTimeZone losAngeles("America/Los_Angeles"); QTimeZone cairo("Africa/Cairo"); QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":Europe/London"); ::tzset(); // Ensure that local time is different from UTC and different from 'london' qputenv("TZ", ":America/Los_Angeles"); ::tzset(); int utcHit = KADateTime_utcCacheHit; int zoneHit = KADateTime_zoneCacheHit; KADateTime local(QDate(2005, 6, 1), QTime(12, 0, 0), KADateTime::LocalZone); QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt1 = local.toZone(london); QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime cai = local.toZone(cairo); ++utcHit; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt2a = local.toZone(london); ++utcHit; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt2 = local.toZone(london); ++zoneHit; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt3 = dt2; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt4 = dt2.toZone(losAngeles); ++zoneHit; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt4a = dt3.toZone(losAngeles); ++zoneHit; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt5 = dt2.toZone(losAngeles); ++zoneHit; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt5a = dt3.toZone(losAngeles); ++zoneHit; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt6 = dt2.toZone(cairo); ++utcHit; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt6a = dt3.toZone(cairo); ++zoneHit; QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); dt3.detach(); KADateTime dt7 = dt2.toZone(london); QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); KADateTime dt7a = dt3.toZone(london); QCOMPARE(KADateTime_utcCacheHit, utcHit); QCOMPARE(KADateTime_zoneCacheHit, zoneHit); // Check that cached time zone conversions are cleared correctly KADateTime utc1(QDate(2005, 7, 6), QTime(3, 40, 0), KADateTime::UTC); KADateTime la1 = utc1.toTimeSpec(losAngeles); KADateTime utc2 = utc1.addDays(1); KADateTime la2 = utc2.toTimeSpec(losAngeles); QVERIFY(la1 != la2); QCOMPARE(la1.secsTo(la2), 86400); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } #endif /* COMPILING_TESTS */ void KADateTimeTest::stream() { // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); // Ensure that the original contents of the KADateTime receiving a streamed value // don't affect the new contents. QByteArray data; QDataStream ds(&data, QIODevice::ReadWrite); KADateTime testdt, result; data.clear(); testdt = KADateTime(QDate(2005, 6, 1), QTime(12, 0, 0), KADateTime::LocalZone); result = KADateTime::currentUtcDateTime(); ds << testdt; ds.device()->seek(0); ds >> result; QCOMPARE(result, testdt); data.clear(); testdt = KADateTime(QDate(2005, 6, 1), QTime(12, 0, 0), KADateTime::LocalZone); result = KADateTime::currentLocalDateTime(); ds.device()->seek(0); ds << testdt; ds.device()->seek(0); ds >> result; QCOMPARE(result, testdt); data.clear(); testdt = KADateTime(QDate(2006, 8, 30), QTime(7, 0, 0), KADateTime::UTC); result = KADateTime::currentUtcDateTime(); ds.device()->seek(0); ds << testdt; ds.device()->seek(0); ds >> result; QCOMPARE(result, testdt); data.clear(); testdt = KADateTime(QDate(2006, 8, 30), QTime(7, 0, 0), KADateTime::UTC); result = KADateTime::currentLocalDateTime(); ds.device()->seek(0); ds << testdt; ds.device()->seek(0); ds >> result; QCOMPARE(result, testdt); // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } void KADateTimeTest::misc() { // Ensure that local time is different from UTC and different from 'london' QByteArray originalZone = qgetenv("TZ"); // save the original local time zone qputenv("TZ", ":America/Los_Angeles"); ::tzset(); KADateTime local = KADateTime::currentLocalDateTime(); KADateTime utc = KADateTime::currentUtcDateTime(); QDateTime qcurrent = QDateTime::currentDateTime(); // Because 3 calls to fetch the current time were made, they will differ slightly KADateTime localUtc = local.toUtc(); int diff = localUtc.secsTo(utc); if (diff > 1 || diff < 0) { QCOMPARE(local.toUtc().date(), utc.date()); QCOMPARE(local.toUtc().time(), utc.time()); } diff = local.qDateTime().secsTo(qcurrent); if (diff > 1 || diff < 0) { QCOMPARE(local.qDateTime(), qcurrent); } // Restore the original local time zone if (originalZone.isEmpty()) { ::unsetenv("TZ"); } else { qputenv("TZ", originalZone); } ::tzset(); } // vim: et sw=4: diff --git a/src/kaevent.cpp b/src/kaevent.cpp index 41519cf..4de49a9 100644 --- a/src/kaevent.cpp +++ b/src/kaevent.cpp @@ -1,5962 +1,5962 @@ /* * kaevent.cpp - represents calendar events * This file is part of kalarmcal library, which provides access to KAlarm * calendar data. * Copyright © 2001-2016 by David Jarvie * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public * License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #include "kaevent.h" #include "alarmtext.h" #include "identities.h" #include "version.h" #include #include #include #include #include "kalarmcal_debug.h" using namespace KCalCore; using namespace KHolidays; namespace KAlarmCal { //============================================================================= typedef KCalCore::Person EmailAddress; class EmailAddressList : public KCalCore::Person::List { public: EmailAddressList() : KCalCore::Person::List() { } EmailAddressList(const KCalCore::Person::List &list) { operator=(list); } EmailAddressList &operator=(const KCalCore::Person::List &); operator QStringList() const; QString join(const QString &separator) const; QStringList pureAddresses() const; QString pureAddresses(const QString &separator) const; private: QString address(int index) const; }; //============================================================================= class Q_DECL_HIDDEN KAAlarm::Private { public: Private(); Action mActionType; // alarm action type Type mType; // alarm type DateTime mNextMainDateTime; // next time to display the alarm, excluding repetitions Repetition mRepetition; // sub-repetition count and interval int mNextRepeat; // repetition count of next due sub-repetition bool mRepeatAtLogin; // whether to repeat the alarm at every login bool mRecurs; // there is a recurrence rule for the alarm bool mDeferred; // whether the alarm is an extra deferred/deferred-reminder alarm bool mTimedDeferral; // if mDeferred = true: true if the deferral is timed, false if date-only }; //============================================================================= class Q_DECL_HIDDEN KAEventPrivate : public QSharedData { public: // Read-only internal flags additional to KAEvent::Flags enum values. // NOTE: If any values are added to those in KAEvent::Flags, ensure // that these values don't overlap them. enum { REMINDER = 0x100000, DEFERRAL = 0x200000, TIMED_FLAG = 0x400000, DATE_DEFERRAL = DEFERRAL, TIME_DEFERRAL = DEFERRAL | TIMED_FLAG, DISPLAYING_ = 0x800000, READ_ONLY_FLAGS = 0xF00000 //!< mask for all read-only internal values }; enum ReminderType { // current active state of reminder NO_REMINDER, // reminder is not due ACTIVE_REMINDER, // reminder is due HIDDEN_REMINDER // reminder-after is disabled due to main alarm being deferred past it }; enum DeferType { NO_DEFERRAL = 0, // there is no deferred alarm NORMAL_DEFERRAL, // the main alarm, a recurrence or a repeat is deferred REMINDER_DEFERRAL // a reminder alarm is deferred }; // Alarm types. // This uses the same scheme as KAAlarm::Type, with some extra values. // Note that the actual enum values need not be the same as in KAAlarm::Type. enum AlarmType { INVALID_ALARM = 0, // Not an alarm MAIN_ALARM = 1, // THE real alarm. Must be the first in the enumeration. REMINDER_ALARM = 0x02, // Reminder in advance of/after the main alarm DEFERRED_ALARM = 0x04, // Deferred alarm DEFERRED_REMINDER_ALARM = REMINDER_ALARM | DEFERRED_ALARM, // Deferred reminder alarm // The following values must be greater than the preceding ones, to // ensure that in ordered processing they are processed afterwards. AT_LOGIN_ALARM = 0x10, // Additional repeat-at-login trigger DISPLAYING_ALARM = 0x20, // Copy of the alarm currently being displayed // The following are extra internal KAEvent values AUDIO_ALARM = 0x30, // sound to play when displaying the alarm PRE_ACTION_ALARM = 0x40, // command to execute before displaying the alarm POST_ACTION_ALARM = 0x50 // command to execute after the alarm window is closed }; struct AlarmData { Alarm::Ptr alarm; QString cleanText; // text or audio file name QFont font; QColor bgColour, fgColour; float soundVolume; float fadeVolume; int fadeSeconds; int repeatSoundPause; int nextRepeat; uint emailFromId; KAEventPrivate::AlarmType type; KAAlarm::Action action; int displayingFlags; KAEvent::ExtraActionOptions extraActionOptions; bool speak; bool defaultFont; bool isEmailText; bool commandScript; bool timedDeferral; bool hiddenReminder; }; typedef QMap AlarmMap; KAEventPrivate(); KAEventPrivate(const KADateTime &, const QString &message, const QColor &bg, const QColor &fg, const QFont &f, KAEvent::SubAction, int lateCancel, KAEvent::Flags flags, bool changesPending = false); explicit KAEventPrivate(const KCalCore::Event::Ptr &); KAEventPrivate(const KAEventPrivate &); ~KAEventPrivate() { delete mRecurrence; } KAEventPrivate &operator=(const KAEventPrivate &e) { if (&e != this) { copy(e); } return *this; } void set(const KCalCore::Event::Ptr &); void set(const KADateTime &, const QString &message, const QColor &bg, const QColor &fg, const QFont &, KAEvent::SubAction, int lateCancel, KAEvent::Flags flags, bool changesPending = false); void setAudioFile(const QString &filename, float volume, float fadeVolume, int fadeSeconds, int repeatPause, bool allowEmptyFile); KAEvent::OccurType setNextOccurrence(const KADateTime &preDateTime); void setFirstRecurrence(); void setCategory(CalEvent::Type); void setRepeatAtLogin(bool); void setRepeatAtLoginTrue(bool clearReminder); void setReminder(int minutes, bool onceOnly); void activateReminderAfter(const DateTime &mainAlarmTime); void defer(const DateTime &, bool reminder, bool adjustRecurrence = false); void cancelDefer(); bool setDisplaying(const KAEventPrivate &, KAAlarm::Type, Akonadi::Collection::Id, const KADateTime &dt, bool showEdit, bool showDefer); void reinstateFromDisplaying(const KCalCore::Event::Ptr &, Akonadi::Collection::Id &, bool &showEdit, bool &showDefer); void startChanges() { ++mChangeCount; } void endChanges(); void removeExpiredAlarm(KAAlarm::Type); KAAlarm alarm(KAAlarm::Type) const; KAAlarm firstAlarm() const; KAAlarm nextAlarm(KAAlarm::Type) const; bool updateKCalEvent(const KCalCore::Event::Ptr &, KAEvent::UidAction, bool setCustomProperties = true) const; DateTime mainDateTime(bool withRepeats = false) const { return (withRepeats && mNextRepeat && mRepetition) ? DateTime(mRepetition.duration(mNextRepeat).end(mNextMainDateTime.qDateTime())) : mNextMainDateTime; } DateTime mainEndRepeatTime() const { return mRepetition ? DateTime(mRepetition.duration().end(mNextMainDateTime.qDateTime())) : mNextMainDateTime; } DateTime deferralLimit(KAEvent::DeferLimitType * = nullptr) const; KAEvent::Flags flags() const; bool isWorkingTime(const KADateTime &) const; bool setRepetition(const Repetition &); bool occursAfter(const KADateTime &preDateTime, bool includeRepetitions) const; KAEvent::OccurType nextOccurrence(const KADateTime &preDateTime, DateTime &result, KAEvent::OccurOption = KAEvent::IGNORE_REPETITION) const; KAEvent::OccurType previousOccurrence(const KADateTime &afterDateTime, DateTime &result, bool includeRepetitions = false) const; void setRecurrence(const KARecurrence &); bool setRecur(KCalCore::RecurrenceRule::PeriodType, int freq, int count, const QDate &end, KARecurrence::Feb29Type = KARecurrence::Feb29_None); bool setRecur(KCalCore::RecurrenceRule::PeriodType, int freq, int count, const KADateTime &end, KARecurrence::Feb29Type = KARecurrence::Feb29_None); KARecurrence::Type checkRecur() const; void clearRecur(); void calcTriggerTimes() const; #ifdef KDE_NO_DEBUG_OUTPUT void dumpDebug() const { } #else void dumpDebug() const; #endif static bool convertRepetition(const KCalCore::Event::Ptr &); static bool convertStartOfDay(const KCalCore::Event::Ptr &); static DateTime readDateTime(const KCalCore::Event::Ptr &, bool localZone, bool dateOnly, DateTime &start); static void readAlarms(const KCalCore::Event::Ptr &, AlarmMap *, bool cmdDisplay = false); static void readAlarm(const KCalCore::Alarm::Ptr &, AlarmData &, bool audioMain, bool cmdDisplay = false); static QSharedPointer holidays(); private: void copy(const KAEventPrivate &); bool mayOccurDailyDuringWork(const KADateTime &) const; int nextWorkRepetition(const KADateTime &pre) const; void calcNextWorkingTime(const DateTime &nextTrigger) const; DateTime nextWorkingTime() const; KAEvent::OccurType nextRecurrence(const KADateTime &preDateTime, DateTime &result) const; void setAudioAlarm(const KCalCore::Alarm::Ptr &) const; KCalCore::Alarm::Ptr initKCalAlarm(const KCalCore::Event::Ptr &, const DateTime &, const QStringList &types, AlarmType = INVALID_ALARM) const; KCalCore::Alarm::Ptr initKCalAlarm(const KCalCore::Event::Ptr &, int startOffsetSecs, const QStringList &types, AlarmType = INVALID_ALARM) const; inline void set_deferral(DeferType); inline void activate_reminder(bool activate); static int transitionIndex(const QDateTime &utc, const QTimeZone::OffsetDataList& transitions); public: static QFont mDefaultFont; // default alarm message font static QSharedPointer mHolidays; // holiday region to use static QBitArray mWorkDays; // working days of the week static QTime mWorkDayStart; // start time of the working day static QTime mWorkDayEnd; // end time of the working day static int mWorkTimeIndex; // incremented every time working days/times are changed mutable DateTime mAllTrigger; // next trigger time, including reminders, ignoring working hours mutable DateTime mMainTrigger; // next trigger time, ignoring reminders and working hours mutable DateTime mAllWorkTrigger; // next trigger time, taking account of reminders and working hours mutable DateTime mMainWorkTrigger; // next trigger time, ignoring reminders but taking account of working hours mutable KAEvent::CmdErrType mCommandError; // command execution error last time the alarm triggered QString mEventID; // UID: KCal::Event unique ID QString mTemplateName; // alarm template's name, or null if normal event QMap mCustomProperties; // KCal::Event's non-KAlarm custom properties Akonadi::Item::Id mItemId; // Akonadi::Item ID for this event mutable Akonadi::Collection::Id mCollectionId; // ID of collection containing the event, or for a displaying event, // saved collection ID (not the collection the event is in) QString mText; // message text, file URL, command, email body [or audio file for KAAlarm] QString mAudioFile; // ATTACH: audio file to play QString mPreAction; // command to execute before alarm is displayed QString mPostAction; // command to execute after alarm window is closed DateTime mStartDateTime; // DTSTART and DTEND: start and end time for event KADateTime mCreatedDateTime; // CREATED: date event was created, or saved in archive calendar DateTime mNextMainDateTime; // next time to display the alarm, excluding repetitions KADateTime mAtLoginDateTime; // repeat-at-login end time DateTime mDeferralTime; // extra time to trigger alarm (if alarm or reminder deferred) DateTime mDisplayingTime; // date/time shown in the alarm currently being displayed int mDisplayingFlags; // type of alarm which is currently being displayed (for display alarm) int mReminderMinutes; // how long in advance reminder is to be, or 0 if none (<0 for reminder AFTER the alarm) DateTime mReminderAfterTime; // if mReminderActive true, time to trigger reminder AFTER the main alarm, or invalid if not pending ReminderType mReminderActive; // whether a reminder is due (before next, or after last, main alarm/recurrence) int mDeferDefaultMinutes; // default number of minutes for deferral dialog, or 0 to select time control bool mDeferDefaultDateOnly;// select date-only by default in deferral dialog int mRevision; // SEQUENCE: revision number of the original alarm, or 0 KARecurrence *mRecurrence; // RECUR: recurrence specification, or 0 if none Repetition mRepetition; // sub-repetition count and interval int mNextRepeat; // repetition count of next due sub-repetition int mAlarmCount; // number of alarms: count of !mMainExpired, mRepeatAtLogin, mDeferral, mReminderActive, mDisplaying DeferType mDeferral; // whether the alarm is an extra deferred/deferred-reminder alarm unsigned long mKMailSerialNumber; // if email text, message's KMail serial number int mTemplateAfterTime; // time not specified: use n minutes after default time, or -1 (applies to templates only) QColor mBgColour; // background colour of alarm message QColor mFgColour; // foreground colour of alarm message, or invalid for default QFont mFont; // font of alarm message (ignored if mUseDefaultFont true) uint mEmailFromIdentity; // standard email identity uoid for 'From' field, or empty EmailAddressList mEmailAddresses; // ATTENDEE: addresses to send email to QString mEmailSubject; // SUMMARY: subject line of email QStringList mEmailAttachments; // ATTACH: email attachment file names mutable int mChangeCount; // >0 = inhibit calling calcTriggerTimes() mutable bool mTriggerChanged; // true if need to recalculate trigger times QString mLogFile; // alarm output is to be logged to this URL float mSoundVolume; // volume for sound file (range 0 - 1), or < 0 for unspecified float mFadeVolume; // initial volume for sound file (range 0 - 1), or < 0 for no fade int mFadeSeconds; // fade time (seconds) for sound file, or 0 if none int mRepeatSoundPause; // seconds to pause between sound file repetitions, or -1 if no repetition int mLateCancel; // how many minutes late will cancel the alarm, or 0 for no cancellation bool mExcludeHolidays; // don't trigger alarms on holidays mutable QSharedPointer mExcludeHolidayRegion; // holiday region used to exclude alarms on holidays (= mHolidays when trigger calculated) mutable int mWorkTimeOnly; // non-zero to trigger alarm only during working hours (= mWorkTimeIndex when trigger calculated) KAEvent::SubAction mActionSubType; // sub-action type for the event's main alarm CalEvent::Type mCategory; // event category (active, archived, template, ...) KAEvent::ExtraActionOptions mExtraActionOptions;// options for pre- or post-alarm actions KACalendar::Compat mCompatibility; // event's storage format compatibility bool mReadOnly; // event is read-only in its original calendar file bool mConfirmAck; // alarm acknowledgement requires confirmation by user bool mUseDefaultFont; // use default message font, not mFont bool mCommandScript; // the command text is a script, not a shell command line bool mCommandXterm; // command alarm is to be executed in a terminal window bool mCommandDisplay; // command output is to be displayed in an alarm window bool mEmailBcc; // blind copy the email to the user bool mBeep; // whether to beep when the alarm is displayed bool mSpeak; // whether to speak the message when the alarm is displayed bool mCopyToKOrganizer; // KOrganizer should hold a copy of the event bool mReminderOnceOnly; // the reminder is output only for the first recurrence bool mAutoClose; // whether to close the alarm window after the late-cancel period bool mMainExpired; // main alarm has expired (in which case a deferral alarm will exist) bool mRepeatAtLogin; // whether to repeat the alarm at every login bool mArchiveRepeatAtLogin; // if now archived, original event was repeat-at-login bool mArchive; // event has triggered in the past, so archive it when closed bool mDisplaying; // whether the alarm is currently being displayed (i.e. in displaying calendar) bool mDisplayingDefer; // show Defer button (applies to displaying calendar only) bool mDisplayingEdit; // show Edit button (applies to displaying calendar only) bool mEnabled; // false if event is disabled public: static const QByteArray FLAGS_PROPERTY; static const QString DATE_ONLY_FLAG; static const QString LOCAL_ZONE_FLAG; static const QString EMAIL_BCC_FLAG; static const QString CONFIRM_ACK_FLAG; static const QString KORGANIZER_FLAG; static const QString EXCLUDE_HOLIDAYS_FLAG; static const QString WORK_TIME_ONLY_FLAG; static const QString REMINDER_ONCE_FLAG; static const QString DEFER_FLAG; static const QString LATE_CANCEL_FLAG; static const QString AUTO_CLOSE_FLAG; static const QString TEMPL_AFTER_TIME_FLAG; static const QString KMAIL_SERNUM_FLAG; static const QString ARCHIVE_FLAG; static const QByteArray NEXT_RECUR_PROPERTY; static const QByteArray REPEAT_PROPERTY; static const QByteArray LOG_PROPERTY; static const QString xtermURL; static const QString displayURL; static const QByteArray TYPE_PROPERTY; static const QString FILE_TYPE; static const QString AT_LOGIN_TYPE; static const QString REMINDER_TYPE; static const QString REMINDER_ONCE_TYPE; static const QString TIME_DEFERRAL_TYPE; static const QString DATE_DEFERRAL_TYPE; static const QString DISPLAYING_TYPE; static const QString PRE_ACTION_TYPE; static const QString POST_ACTION_TYPE; static const QString SOUND_REPEAT_TYPE; static const QByteArray NEXT_REPEAT_PROPERTY; static const QString HIDDEN_REMINDER_FLAG; static const QByteArray FONT_COLOUR_PROPERTY; static const QByteArray VOLUME_PROPERTY; static const QString EMAIL_ID_FLAG; static const QString SPEAK_FLAG; static const QString EXEC_ON_DEFERRAL_FLAG; static const QString CANCEL_ON_ERROR_FLAG; static const QString DONT_SHOW_ERROR_FLAG; static const QString DISABLED_STATUS; static const QString DISP_DEFER; static const QString DISP_EDIT; static const QString CMD_ERROR_VALUE; static const QString CMD_ERROR_PRE_VALUE; static const QString CMD_ERROR_POST_VALUE; static const QString SC; }; //============================================================================= // KAlarm version which first used the current calendar/event format. // If this changes, KAEvent::convertKCalEvents() must be changed correspondingly. // The string version is the KAlarm version string used in the calendar file. //QT5 Fix library version QByteArray KAEvent::currentCalendarVersionString() { return QByteArray("2.7.0"); } int KAEvent::currentCalendarVersion() { return Version(2, 7, 0); } // Custom calendar properties. // Note that all custom property names are prefixed with X-KDE-KALARM- in the calendar file. // Event properties const QByteArray KAEventPrivate::FLAGS_PROPERTY("FLAGS"); // X-KDE-KALARM-FLAGS property const QString KAEventPrivate::DATE_ONLY_FLAG = QStringLiteral("DATE"); const QString KAEventPrivate::LOCAL_ZONE_FLAG = QStringLiteral("LOCAL"); const QString KAEventPrivate::EMAIL_BCC_FLAG = QStringLiteral("BCC"); const QString KAEventPrivate::CONFIRM_ACK_FLAG = QStringLiteral("ACKCONF"); const QString KAEventPrivate::KORGANIZER_FLAG = QStringLiteral("KORG"); const QString KAEventPrivate::EXCLUDE_HOLIDAYS_FLAG = QStringLiteral("EXHOLIDAYS"); const QString KAEventPrivate::WORK_TIME_ONLY_FLAG = QStringLiteral("WORKTIME"); const QString KAEventPrivate::REMINDER_ONCE_FLAG = QStringLiteral("ONCE"); const QString KAEventPrivate::DEFER_FLAG = QStringLiteral("DEFER"); // default defer interval for this alarm const QString KAEventPrivate::LATE_CANCEL_FLAG = QStringLiteral("LATECANCEL"); const QString KAEventPrivate::AUTO_CLOSE_FLAG = QStringLiteral("LATECLOSE"); const QString KAEventPrivate::TEMPL_AFTER_TIME_FLAG = QStringLiteral("TMPLAFTTIME"); const QString KAEventPrivate::KMAIL_SERNUM_FLAG = QStringLiteral("KMAIL"); const QString KAEventPrivate::ARCHIVE_FLAG = QStringLiteral("ARCHIVE"); const QByteArray KAEventPrivate::NEXT_RECUR_PROPERTY("NEXTRECUR"); // X-KDE-KALARM-NEXTRECUR property const QByteArray KAEventPrivate::REPEAT_PROPERTY("REPEAT"); // X-KDE-KALARM-REPEAT property const QByteArray KAEventPrivate::LOG_PROPERTY("LOG"); // X-KDE-KALARM-LOG property const QString KAEventPrivate::xtermURL = QStringLiteral("xterm:"); const QString KAEventPrivate::displayURL = QStringLiteral("display:"); // - General alarm properties const QByteArray KAEventPrivate::TYPE_PROPERTY("TYPE"); // X-KDE-KALARM-TYPE property const QString KAEventPrivate::FILE_TYPE = QStringLiteral("FILE"); const QString KAEventPrivate::AT_LOGIN_TYPE = QStringLiteral("LOGIN"); const QString KAEventPrivate::REMINDER_TYPE = QStringLiteral("REMINDER"); const QString KAEventPrivate::TIME_DEFERRAL_TYPE = QStringLiteral("DEFERRAL"); const QString KAEventPrivate::DATE_DEFERRAL_TYPE = QStringLiteral("DATE_DEFERRAL"); const QString KAEventPrivate::DISPLAYING_TYPE = QStringLiteral("DISPLAYING"); // used only in displaying calendar const QString KAEventPrivate::PRE_ACTION_TYPE = QStringLiteral("PRE"); const QString KAEventPrivate::POST_ACTION_TYPE = QStringLiteral("POST"); const QString KAEventPrivate::SOUND_REPEAT_TYPE = QStringLiteral("SOUNDREPEAT"); const QByteArray KAEventPrivate::NEXT_REPEAT_PROPERTY("NEXTREPEAT"); // X-KDE-KALARM-NEXTREPEAT property const QString KAEventPrivate::HIDDEN_REMINDER_FLAG = QStringLiteral("HIDE"); // - Display alarm properties const QByteArray KAEventPrivate::FONT_COLOUR_PROPERTY("FONTCOLOR"); // X-KDE-KALARM-FONTCOLOR property // - Email alarm properties const QString KAEventPrivate::EMAIL_ID_FLAG = QStringLiteral("EMAILID"); // - Audio alarm properties const QByteArray KAEventPrivate::VOLUME_PROPERTY("VOLUME"); // X-KDE-KALARM-VOLUME property const QString KAEventPrivate::SPEAK_FLAG = QStringLiteral("SPEAK"); // - Command alarm properties const QString KAEventPrivate::EXEC_ON_DEFERRAL_FLAG = QStringLiteral("EXECDEFER"); const QString KAEventPrivate::CANCEL_ON_ERROR_FLAG = QStringLiteral("ERRCANCEL"); const QString KAEventPrivate::DONT_SHOW_ERROR_FLAG = QStringLiteral("ERRNOSHOW"); // Event status strings const QString KAEventPrivate::DISABLED_STATUS = QStringLiteral("DISABLED"); // Displaying event ID identifier const QString KAEventPrivate::DISP_DEFER = QStringLiteral("DEFER"); const QString KAEventPrivate::DISP_EDIT = QStringLiteral("EDIT"); // Command error strings const QString KAEventPrivate::CMD_ERROR_VALUE = QStringLiteral("MAIN"); const QString KAEventPrivate::CMD_ERROR_PRE_VALUE = QStringLiteral("PRE"); const QString KAEventPrivate::CMD_ERROR_POST_VALUE = QStringLiteral("POST"); const QString KAEventPrivate::SC = QStringLiteral(";"); QFont KAEventPrivate::mDefaultFont; QSharedPointer KAEventPrivate::mHolidays; QBitArray KAEventPrivate::mWorkDays(7); QTime KAEventPrivate::mWorkDayStart(9, 0, 0); QTime KAEventPrivate::mWorkDayEnd(17, 0, 0); int KAEventPrivate::mWorkTimeIndex = 1; static void setProcedureAlarm(const Alarm::Ptr &, const QString &commandLine); static QString reminderToString(int minutes); /*============================================================================= = Class KAEvent = Corresponds to a KCal::Event instance. =============================================================================*/ inline void KAEventPrivate::activate_reminder(bool activate) { if (activate && mReminderActive != ACTIVE_REMINDER && mReminderMinutes) { if (mReminderActive == NO_REMINDER) { ++mAlarmCount; } mReminderActive = ACTIVE_REMINDER; } else if (!activate && mReminderActive != NO_REMINDER) { mReminderActive = NO_REMINDER; mReminderAfterTime = DateTime(); --mAlarmCount; } } Q_GLOBAL_STATIC_WITH_ARGS(QSharedDataPointer, emptyKAEventPrivate, (new KAEventPrivate)) KAEvent::KAEvent() : d(*emptyKAEventPrivate) { } KAEventPrivate::KAEventPrivate() : mCommandError(KAEvent::CMD_NO_ERROR), mItemId(-1), mCollectionId(-1), mReminderMinutes(0), mReminderActive(NO_REMINDER), mRevision(0), mRecurrence(nullptr), mNextRepeat(0), mAlarmCount(0), mDeferral(NO_DEFERRAL), mChangeCount(0), mTriggerChanged(false), mLateCancel(0), mWorkTimeOnly(0), mCategory(CalEvent::EMPTY), mCompatibility(KACalendar::Current), mReadOnly(false), mConfirmAck(false), mEmailBcc(false), mBeep(false), mAutoClose(false), mRepeatAtLogin(false), mDisplaying(false) { } KAEvent::KAEvent(const KADateTime &dt, const QString &message, const QColor &bg, const QColor &fg, const QFont &f, SubAction action, int lateCancel, Flags flags, bool changesPending) : d(new KAEventPrivate(dt, message, bg, fg, f, action, lateCancel, flags, changesPending)) { } KAEventPrivate::KAEventPrivate(const KADateTime &dt, const QString &message, const QColor &bg, const QColor &fg, const QFont &f, KAEvent::SubAction action, int lateCancel, KAEvent::Flags flags, bool changesPending) : mRevision(0), mRecurrence(nullptr) { set(dt, message, bg, fg, f, action, lateCancel, flags, changesPending); } KAEvent::KAEvent(const KCalCore::Event::Ptr &e) : d(new KAEventPrivate(e)) { } KAEventPrivate::KAEventPrivate(const KCalCore::Event::Ptr &e) : mRecurrence(nullptr) { set(e); } KAEventPrivate::KAEventPrivate(const KAEventPrivate &e) : QSharedData(e), mRecurrence(nullptr) { copy(e); } KAEvent::KAEvent(const KAEvent &other) : d(other.d) { } KAEvent::~KAEvent() { } KAEvent &KAEvent::operator=(const KAEvent &other) { if (&other != this) { d = other.d; } return *this; } /****************************************************************************** * Copies the data from another instance. */ void KAEventPrivate::copy(const KAEventPrivate &event) { mAllTrigger = event.mAllTrigger; mMainTrigger = event.mMainTrigger; mAllWorkTrigger = event.mAllWorkTrigger; mMainWorkTrigger = event.mMainWorkTrigger; mCommandError = event.mCommandError; mEventID = event.mEventID; mTemplateName = event.mTemplateName; mCustomProperties = event.mCustomProperties; mItemId = event.mItemId; mCollectionId = event.mCollectionId; mText = event.mText; mAudioFile = event.mAudioFile; mPreAction = event.mPreAction; mPostAction = event.mPostAction; mStartDateTime = event.mStartDateTime; mCreatedDateTime = event.mCreatedDateTime; mNextMainDateTime = event.mNextMainDateTime; mAtLoginDateTime = event.mAtLoginDateTime; mDeferralTime = event.mDeferralTime; mDisplayingTime = event.mDisplayingTime; mDisplayingFlags = event.mDisplayingFlags; mReminderMinutes = event.mReminderMinutes; mReminderAfterTime = event.mReminderAfterTime; mReminderActive = event.mReminderActive; mDeferDefaultMinutes = event.mDeferDefaultMinutes; mDeferDefaultDateOnly = event.mDeferDefaultDateOnly; mRevision = event.mRevision; mRepetition = event.mRepetition; mNextRepeat = event.mNextRepeat; mAlarmCount = event.mAlarmCount; mDeferral = event.mDeferral; mKMailSerialNumber = event.mKMailSerialNumber; mTemplateAfterTime = event.mTemplateAfterTime; mBgColour = event.mBgColour; mFgColour = event.mFgColour; mFont = event.mFont; mEmailFromIdentity = event.mEmailFromIdentity; mEmailAddresses = event.mEmailAddresses; mEmailSubject = event.mEmailSubject; mEmailAttachments = event.mEmailAttachments; mLogFile = event.mLogFile; mSoundVolume = event.mSoundVolume; mFadeVolume = event.mFadeVolume; mFadeSeconds = event.mFadeSeconds; mRepeatSoundPause = event.mRepeatSoundPause; mLateCancel = event.mLateCancel; mExcludeHolidays = event.mExcludeHolidays; mExcludeHolidayRegion = event.mExcludeHolidayRegion; mWorkTimeOnly = event.mWorkTimeOnly; mActionSubType = event.mActionSubType; mCategory = event.mCategory; mExtraActionOptions = event.mExtraActionOptions; mCompatibility = event.mCompatibility; mReadOnly = event.mReadOnly; mConfirmAck = event.mConfirmAck; mUseDefaultFont = event.mUseDefaultFont; mCommandScript = event.mCommandScript; mCommandXterm = event.mCommandXterm; mCommandDisplay = event.mCommandDisplay; mEmailBcc = event.mEmailBcc; mBeep = event.mBeep; mSpeak = event.mSpeak; mCopyToKOrganizer = event.mCopyToKOrganizer; mReminderOnceOnly = event.mReminderOnceOnly; mAutoClose = event.mAutoClose; mMainExpired = event.mMainExpired; mRepeatAtLogin = event.mRepeatAtLogin; mArchiveRepeatAtLogin = event.mArchiveRepeatAtLogin; mArchive = event.mArchive; mDisplaying = event.mDisplaying; mDisplayingDefer = event.mDisplayingDefer; mDisplayingEdit = event.mDisplayingEdit; mEnabled = event.mEnabled; mChangeCount = 0; mTriggerChanged = event.mTriggerChanged; delete mRecurrence; if (event.mRecurrence) { mRecurrence = new KARecurrence(*event.mRecurrence); } else { mRecurrence = nullptr; } } void KAEvent::set(const KCalCore::Event::Ptr &e) { d->set(e); } /****************************************************************************** * Initialise the KAEventPrivate from a KCalCore::Event. */ void KAEventPrivate::set(const KCalCore::Event::Ptr &event) { startChanges(); // Extract status from the event mCommandError = KAEvent::CMD_NO_ERROR; mEventID = event->uid(); mRevision = event->revision(); mTemplateName.clear(); mLogFile.clear(); mItemId = -1; mCollectionId = -1; mTemplateAfterTime = -1; mBeep = false; mSpeak = false; mEmailBcc = false; mCommandXterm = false; mCommandDisplay = false; mCopyToKOrganizer = false; mConfirmAck = false; mArchive = false; mReminderOnceOnly = false; mAutoClose = false; mArchiveRepeatAtLogin = false; mDisplayingDefer = false; mDisplayingEdit = false; mDeferDefaultDateOnly = false; mReminderActive = NO_REMINDER; mReminderMinutes = 0; mDeferDefaultMinutes = 0; mLateCancel = 0; mKMailSerialNumber = 0; mExcludeHolidays = false; mWorkTimeOnly = 0; mChangeCount = 0; mBgColour = QColor(255, 255, 255); // missing/invalid colour - return white background mFgColour = QColor(0, 0, 0); // and black foreground mCompatibility = KACalendar::Current; mReadOnly = event->isReadOnly(); mUseDefaultFont = true; mEnabled = true; clearRecur(); QString param; bool ok; mCategory = CalEvent::status(event, ¶m); if (mCategory == CalEvent::DISPLAYING) { // It's a displaying calendar event - set values specific to displaying alarms const QStringList params = param.split(SC, QString::KeepEmptyParts); int n = params.count(); if (n) { const qlonglong id = params[0].toLongLong(&ok); if (ok) { mCollectionId = id; // original collection ID which contained the event } for (int i = 1; i < n; ++i) { if (params[i] == DISP_DEFER) { mDisplayingDefer = true; } if (params[i] == DISP_EDIT) { mDisplayingEdit = true; } } } } // Store the non-KAlarm custom properties of the event const QByteArray kalarmKey = "X-KDE-" + KACalendar::APPNAME + '-'; mCustomProperties = event->customProperties(); for (QMap::Iterator it = mCustomProperties.begin(); it != mCustomProperties.end();) { if (it.key().startsWith(kalarmKey)) { it = mCustomProperties.erase(it); } else { ++it; } } bool dateOnly = false; bool localZone = false; QStringList flags = event->customProperty(KACalendar::APPNAME, FLAGS_PROPERTY).split(SC, QString::SkipEmptyParts); flags << QString() << QString(); // to avoid having to check for end of list for (int i = 0, end = flags.count() - 1; i < end; ++i) { QString flag = flags.at(i); if (flag == DATE_ONLY_FLAG) { dateOnly = true; } else if (flag == LOCAL_ZONE_FLAG) { localZone = true; } else if (flag == CONFIRM_ACK_FLAG) { mConfirmAck = true; } else if (flag == EMAIL_BCC_FLAG) { mEmailBcc = true; } else if (flag == KORGANIZER_FLAG) { mCopyToKOrganizer = true; } else if (flag == EXCLUDE_HOLIDAYS_FLAG) { mExcludeHolidays = true; mExcludeHolidayRegion = holidays(); } else if (flag == WORK_TIME_ONLY_FLAG) { mWorkTimeOnly = 1; } else if (flag == KMAIL_SERNUM_FLAG) { const unsigned long n = flags.at(i + 1).toULong(&ok); if (!ok) { continue; } mKMailSerialNumber = n; ++i; } else if (flag == KAEventPrivate::ARCHIVE_FLAG) { mArchive = true; } else if (flag == KAEventPrivate::AT_LOGIN_TYPE) { mArchiveRepeatAtLogin = true; } else if (flag == KAEventPrivate::REMINDER_TYPE) { flag = flags.at(++i); if (flag == KAEventPrivate::REMINDER_ONCE_FLAG) { mReminderOnceOnly = true; flag = flags.at(++i); } const int len = flag.length() - 1; mReminderMinutes = -flag.leftRef(len).toInt(); // -> 0 if conversion fails switch (flag.at(len).toLatin1()) { case 'M': break; case 'H': mReminderMinutes *= 60; break; case 'D': mReminderMinutes *= 1440; break; default: mReminderMinutes = 0; break; } } else if (flag == DEFER_FLAG) { QString mins = flags.at(i + 1); if (mins.endsWith(QLatin1Char('D'))) { mDeferDefaultDateOnly = true; mins.truncate(mins.length() - 1); } const int n = static_cast(mins.toUInt(&ok)); if (!ok) { continue; } mDeferDefaultMinutes = n; ++i; } else if (flag == TEMPL_AFTER_TIME_FLAG) { const int n = static_cast(flags.at(i + 1).toUInt(&ok)); if (!ok) { continue; } mTemplateAfterTime = n; ++i; } else if (flag == LATE_CANCEL_FLAG) { mLateCancel = static_cast(flags.at(i + 1).toUInt(&ok)); if (ok) { ++i; } if (!ok || !mLateCancel) { mLateCancel = 1; // invalid parameter defaults to 1 minute } } else if (flag == AUTO_CLOSE_FLAG) { mLateCancel = static_cast(flags.at(i + 1).toUInt(&ok)); if (ok) { ++i; } if (!ok || !mLateCancel) { mLateCancel = 1; // invalid parameter defaults to 1 minute } mAutoClose = true; } } QString prop = event->customProperty(KACalendar::APPNAME, LOG_PROPERTY); if (!prop.isEmpty()) { if (prop == xtermURL) { mCommandXterm = true; } else if (prop == displayURL) { mCommandDisplay = true; } else { mLogFile = prop; } } prop = event->customProperty(KACalendar::APPNAME, REPEAT_PROPERTY); if (!prop.isEmpty()) { // This property is used only when the main alarm has expired. // If a main alarm is found, this property is ignored (see below). const QStringList list = prop.split(QLatin1Char(':')); if (list.count() >= 2) { const int interval = static_cast(list[0].toUInt()); const int count = static_cast(list[1].toUInt()); if (interval && count) { if (interval % (24 * 60)) { mRepetition.set(Duration(interval * 60, Duration::Seconds), count); } else { mRepetition.set(Duration(interval / (24 * 60), Duration::Days), count); } } } } mNextMainDateTime = readDateTime(event, localZone, dateOnly, mStartDateTime); mCreatedDateTime = KADateTime(event->created()); if (dateOnly && !mRepetition.isDaily()) { mRepetition.set(Duration(mRepetition.intervalDays(), Duration::Days)); } if (mCategory == CalEvent::TEMPLATE) { mTemplateName = event->summary(); } if (event->customStatus() == DISABLED_STATUS) { mEnabled = false; } // Extract status from the event's alarms. // First set up defaults. mActionSubType = KAEvent::MESSAGE; mMainExpired = true; mRepeatAtLogin = false; mDisplaying = false; mCommandScript = false; mExtraActionOptions = {}; mDeferral = NO_DEFERRAL; mSoundVolume = -1; mFadeVolume = -1; mRepeatSoundPause = -1; mFadeSeconds = 0; mEmailFromIdentity = 0; mReminderAfterTime = DateTime(); mText.clear(); mAudioFile.clear(); mPreAction.clear(); mPostAction.clear(); mEmailSubject.clear(); mEmailAddresses.clear(); mEmailAttachments.clear(); // Extract data from all the event's alarms and index the alarms by sequence number AlarmMap alarmMap; readAlarms(event, &alarmMap, mCommandDisplay); // Incorporate the alarms' details into the overall event mAlarmCount = 0; // initialise as invalid DateTime alTime; bool set = false; bool isEmailText = false; bool setDeferralTime = false; Duration deferralOffset; for (AlarmMap::ConstIterator it = alarmMap.constBegin(); it != alarmMap.constEnd(); ++it) { const AlarmData &data = it.value(); const DateTime dateTime(data.alarm->hasStartOffset() ? data.alarm->startOffset().end(mNextMainDateTime.effectiveDateTime()) : data.alarm->time()); switch (data.type) { case MAIN_ALARM: mMainExpired = false; alTime = dateTime; alTime.setDateOnly(mStartDateTime.isDateOnly()); mRepetition.set(0, 0); // ignore X-KDE-KALARM-REPEAT if main alarm exists if (data.alarm->repeatCount() && !data.alarm->snoozeTime().isNull()) { mRepetition.set(data.alarm->snoozeTime(), data.alarm->repeatCount()); // values may be adjusted in setRecurrence() mNextRepeat = data.nextRepeat; } if (data.action != KAAlarm::AUDIO) { break; } // Fall through to AUDIO_ALARM Q_FALLTHROUGH(); case AUDIO_ALARM: mAudioFile = data.cleanText; mSpeak = data.speak && mAudioFile.isEmpty(); mBeep = !mSpeak && mAudioFile.isEmpty(); mSoundVolume = (!mBeep && !mSpeak) ? data.soundVolume : -1; mFadeVolume = (mSoundVolume >= 0 && data.fadeSeconds > 0) ? data.fadeVolume : -1; mFadeSeconds = (mFadeVolume >= 0) ? data.fadeSeconds : 0; mRepeatSoundPause = (!mBeep && !mSpeak) ? data.repeatSoundPause : -1; break; case AT_LOGIN_ALARM: mRepeatAtLogin = true; mAtLoginDateTime = dateTime.kDateTime(); alTime = mAtLoginDateTime; break; case REMINDER_ALARM: // N.B. there can be a start offset but no valid date/time (e.g. in template) if (data.alarm->startOffset().asSeconds() / 60) { mReminderActive = ACTIVE_REMINDER; if (mReminderMinutes < 0) { mReminderAfterTime = dateTime; // the reminder is AFTER the main alarm mReminderAfterTime.setDateOnly(dateOnly); if (data.hiddenReminder) { mReminderActive = HIDDEN_REMINDER; } } } break; case DEFERRED_REMINDER_ALARM: case DEFERRED_ALARM: mDeferral = (data.type == DEFERRED_REMINDER_ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL; if (data.timedDeferral) { // Don't use start-of-day time for applying timed deferral alarm offset mDeferralTime = DateTime(data.alarm->hasStartOffset() ? data.alarm->startOffset().end(mNextMainDateTime.calendarDateTime()) : data.alarm->time()); } else { mDeferralTime = dateTime; mDeferralTime.setDateOnly(true); } if (data.alarm->hasStartOffset()) { deferralOffset = data.alarm->startOffset(); } break; case DISPLAYING_ALARM: { mDisplaying = true; mDisplayingFlags = data.displayingFlags; const bool dateOnly = (mDisplayingFlags & DEFERRAL) ? !(mDisplayingFlags & TIMED_FLAG) : mStartDateTime.isDateOnly(); mDisplayingTime = dateTime; mDisplayingTime.setDateOnly(dateOnly); alTime = mDisplayingTime; break; } case PRE_ACTION_ALARM: mPreAction = data.cleanText; mExtraActionOptions = data.extraActionOptions; break; case POST_ACTION_ALARM: mPostAction = data.cleanText; break; case INVALID_ALARM: default: break; } bool noSetNextTime = false; switch (data.type) { case DEFERRED_REMINDER_ALARM: case DEFERRED_ALARM: if (!set) { // The recurrence has to be evaluated before we can // calculate the time of a deferral alarm. setDeferralTime = true; noSetNextTime = true; } // fall through to REMINDER_ALARM Q_FALLTHROUGH(); case REMINDER_ALARM: case AT_LOGIN_ALARM: case DISPLAYING_ALARM: if (!set && !noSetNextTime) { mNextMainDateTime = alTime; } // fall through to MAIN_ALARM Q_FALLTHROUGH(); case MAIN_ALARM: // Ensure that the basic fields are set up even if there is no main // alarm in the event (if it has expired and then been deferred) if (!set) { mActionSubType = static_cast(data.action); mText = (mActionSubType == KAEvent::COMMAND) ? data.cleanText.trimmed() : data.cleanText; switch (data.action) { case KAAlarm::COMMAND: mCommandScript = data.commandScript; if (!mCommandDisplay) { break; } // fall through to MESSAGE Q_FALLTHROUGH(); case KAAlarm::MESSAGE: mFont = data.font; mUseDefaultFont = data.defaultFont; if (data.isEmailText) { isEmailText = true; } // fall through to FILE Q_FALLTHROUGH(); case KAAlarm::FILE: mBgColour = data.bgColour; mFgColour = data.fgColour; break; case KAAlarm::EMAIL: mEmailFromIdentity = data.emailFromId; mEmailAddresses = data.alarm->mailAddresses(); mEmailSubject = data.alarm->mailSubject(); mEmailAttachments = data.alarm->mailAttachments(); break; case KAAlarm::AUDIO: // Already mostly handled above mRepeatSoundPause = data.repeatSoundPause; break; default: break; } set = true; } if (data.action == KAAlarm::FILE && mActionSubType == KAEvent::MESSAGE) { mActionSubType = KAEvent::FILE; } ++mAlarmCount; break; case AUDIO_ALARM: case PRE_ACTION_ALARM: case POST_ACTION_ALARM: case INVALID_ALARM: default: break; } } if (!isEmailText) { mKMailSerialNumber = 0; } Recurrence *recur = event->recurrence(); if (recur && recur->recurs()) { const int nextRepeat = mNextRepeat; // setRecurrence() clears mNextRepeat setRecurrence(*recur); if (nextRepeat <= mRepetition.count()) { mNextRepeat = nextRepeat; } } else if (mRepetition) { // Convert a repetition with no recurrence into a recurrence if (mRepetition.isDaily()) { setRecur(RecurrenceRule::rDaily, mRepetition.intervalDays(), mRepetition.count() + 1, QDate()); } else { setRecur(RecurrenceRule::rMinutely, mRepetition.intervalMinutes(), mRepetition.count() + 1, KADateTime()); } mRepetition.set(0, 0); mTriggerChanged = true; } if (mRepeatAtLogin) { mArchiveRepeatAtLogin = false; if (mReminderMinutes > 0) { mReminderMinutes = 0; // pre-alarm reminder not allowed for at-login alarm mReminderActive = NO_REMINDER; } setRepeatAtLoginTrue(false); // clear other incompatible statuses } if (mMainExpired && !deferralOffset.isNull() && checkRecur() != KARecurrence::NO_RECUR) { // Adjust the deferral time for an expired recurrence, since the // offset is relative to the first actual occurrence. DateTime dt = mRecurrence->getNextDateTime(mStartDateTime.addDays(-1).kDateTime()); dt.setDateOnly(mStartDateTime.isDateOnly()); if (mDeferralTime.isDateOnly()) { mDeferralTime = DateTime(deferralOffset.end(dt.qDateTime())); mDeferralTime.setDateOnly(true); } else { mDeferralTime = DateTime(deferralOffset.end(dt.effectiveDateTime())); } } if (mDeferral != NO_DEFERRAL) { if (setDeferralTime) { mNextMainDateTime = mDeferralTime; } } mTriggerChanged = true; endChanges(); } void KAEvent::set(const KADateTime &dt, const QString &message, const QColor &bg, const QColor &fg, const QFont &f, SubAction act, int lateCancel, Flags flags, bool changesPending) { d->set(dt, message, bg, fg, f, act, lateCancel, flags, changesPending); } /****************************************************************************** * Initialise the instance with the specified parameters. */ void KAEventPrivate::set(const KADateTime &dateTime, const QString &text, const QColor &bg, const QColor &fg, const QFont &font, KAEvent::SubAction action, int lateCancel, KAEvent::Flags flags, bool changesPending) { clearRecur(); mStartDateTime = dateTime; if (flags & KAEvent::ANY_TIME) mStartDateTime.setDateOnly(true); mNextMainDateTime = mStartDateTime; switch (action) { case KAEvent::MESSAGE: case KAEvent::FILE: case KAEvent::COMMAND: case KAEvent::EMAIL: case KAEvent::AUDIO: mActionSubType = static_cast(action); break; default: mActionSubType = KAEvent::MESSAGE; break; } mEventID.clear(); mTemplateName.clear(); mItemId = -1; mCollectionId = -1; mPreAction.clear(); mPostAction.clear(); mText = (mActionSubType == KAEvent::COMMAND) ? text.trimmed() : (mActionSubType == KAEvent::AUDIO) ? QString() : text; mCategory = CalEvent::ACTIVE; mAudioFile = (mActionSubType == KAEvent::AUDIO) ? text : QString(); mSoundVolume = -1; mFadeVolume = -1; mTemplateAfterTime = -1; mFadeSeconds = 0; mBgColour = bg; mFgColour = fg; mFont = font; mAlarmCount = 1; mLateCancel = lateCancel; // do this before setting flags mDeferral = NO_DEFERRAL; // do this before setting flags set_deferral((flags & DEFERRAL) ? NORMAL_DEFERRAL : NO_DEFERRAL); mRepeatAtLogin = flags & KAEvent::REPEAT_AT_LOGIN; mConfirmAck = flags & KAEvent::CONFIRM_ACK; mUseDefaultFont = flags & KAEvent::DEFAULT_FONT; mCommandScript = flags & KAEvent::SCRIPT; mCommandXterm = flags & KAEvent::EXEC_IN_XTERM; mCommandDisplay = flags & KAEvent::DISPLAY_COMMAND; mCopyToKOrganizer = flags & KAEvent::COPY_KORGANIZER; mExcludeHolidays = flags & KAEvent::EXCL_HOLIDAYS; mExcludeHolidayRegion = holidays(); mWorkTimeOnly = flags & KAEvent::WORK_TIME_ONLY; mEmailBcc = flags & KAEvent::EMAIL_BCC; mEnabled = !(flags & KAEvent::DISABLED); mDisplaying = flags & DISPLAYING_; mReminderOnceOnly = flags & KAEvent::REMINDER_ONCE; mAutoClose = (flags & KAEvent::AUTO_CLOSE) && mLateCancel; mRepeatSoundPause = (flags & KAEvent::REPEAT_SOUND) ? 0 : -1; mSpeak = (flags & KAEvent::SPEAK) && action != KAEvent::AUDIO; mBeep = (flags & KAEvent::BEEP) && action != KAEvent::AUDIO && !mSpeak; if (mRepeatAtLogin) { // do this after setting other flags ++mAlarmCount; setRepeatAtLoginTrue(false); } mKMailSerialNumber = 0; mReminderMinutes = 0; mDeferDefaultMinutes = 0; mDeferDefaultDateOnly = false; mArchiveRepeatAtLogin = false; mReminderActive = NO_REMINDER; mDisplaying = false; mMainExpired = false; mDisplayingDefer = false; mDisplayingEdit = false; mArchive = false; mReminderAfterTime = DateTime(); mExtraActionOptions = {}; mCompatibility = KACalendar::Current; mReadOnly = false; mCommandError = KAEvent::CMD_NO_ERROR; mChangeCount = changesPending ? 1 : 0; mTriggerChanged = true; } /****************************************************************************** * Update an existing KCal::Event with the KAEventPrivate data. * If 'setCustomProperties' is true, all the KCal::Event's existing custom * properties are cleared and replaced with the KAEvent's custom properties. If * false, the KCal::Event's non-KAlarm custom properties are left untouched. */ bool KAEvent::updateKCalEvent(const KCalCore::Event::Ptr &e, UidAction u, bool setCustomProperties) const { return d->updateKCalEvent(e, u, setCustomProperties); } bool KAEventPrivate::updateKCalEvent(const Event::Ptr &ev, KAEvent::UidAction uidact, bool setCustomProperties) const { // If it's an archived event, the event start date/time will be adjusted to its original // value instead of its next occurrence, and the expired main alarm will be reinstated. const bool archived = (mCategory == CalEvent::ARCHIVED); if (!ev || (uidact == KAEvent::UID_CHECK && !mEventID.isEmpty() && mEventID != ev->uid()) || (!mAlarmCount && (!archived || !mMainExpired))) { return false; } ev->startUpdates(); // prevent multiple update notifications checkRecur(); // ensure recurrence/repetition data is consistent const bool readOnly = ev->isReadOnly(); if (uidact == KAEvent::UID_SET) { ev->setUid(mEventID); } ev->setReadOnly(mReadOnly); ev->setTransparency(Event::Transparent); // Set up event-specific data // Set up custom properties. if (setCustomProperties) { ev->setCustomProperties(mCustomProperties); } ev->removeCustomProperty(KACalendar::APPNAME, FLAGS_PROPERTY); ev->removeCustomProperty(KACalendar::APPNAME, NEXT_RECUR_PROPERTY); ev->removeCustomProperty(KACalendar::APPNAME, REPEAT_PROPERTY); ev->removeCustomProperty(KACalendar::APPNAME, LOG_PROPERTY); QString param; if (mCategory == CalEvent::DISPLAYING) { param = QString::number(mCollectionId); // original collection ID which contained the event if (mDisplayingDefer) { param += SC + DISP_DEFER; } if (mDisplayingEdit) { param += SC + DISP_EDIT; } } CalEvent::setStatus(ev, mCategory, param); QStringList flags; if (mStartDateTime.isDateOnly()) { flags += DATE_ONLY_FLAG; } if (mStartDateTime.timeType() == KADateTime::LocalZone) { flags += LOCAL_ZONE_FLAG; } if (mConfirmAck) { flags += CONFIRM_ACK_FLAG; } if (mEmailBcc) { flags += EMAIL_BCC_FLAG; } if (mCopyToKOrganizer) { flags += KORGANIZER_FLAG; } if (mExcludeHolidays) { flags += EXCLUDE_HOLIDAYS_FLAG; } if (mWorkTimeOnly) { flags += WORK_TIME_ONLY_FLAG; } if (mLateCancel) { (flags += (mAutoClose ? AUTO_CLOSE_FLAG : LATE_CANCEL_FLAG)) += QString::number(mLateCancel); } if (mReminderMinutes) { flags += REMINDER_TYPE; if (mReminderOnceOnly) { flags += REMINDER_ONCE_FLAG; } flags += reminderToString(-mReminderMinutes); } if (mDeferDefaultMinutes) { QString param = QString::number(mDeferDefaultMinutes); if (mDeferDefaultDateOnly) { param += QLatin1Char('D'); } (flags += DEFER_FLAG) += param; } if (!mTemplateName.isEmpty() && mTemplateAfterTime >= 0) { (flags += TEMPL_AFTER_TIME_FLAG) += QString::number(mTemplateAfterTime); } if (mKMailSerialNumber) { (flags += KMAIL_SERNUM_FLAG) += QString::number(mKMailSerialNumber); } if (mArchive && !archived) { flags += ARCHIVE_FLAG; if (mArchiveRepeatAtLogin) { flags += AT_LOGIN_TYPE; } } if (!flags.isEmpty()) { ev->setCustomProperty(KACalendar::APPNAME, FLAGS_PROPERTY, flags.join(SC)); } if (mCommandXterm) { ev->setCustomProperty(KACalendar::APPNAME, LOG_PROPERTY, xtermURL); } else if (mCommandDisplay) { ev->setCustomProperty(KACalendar::APPNAME, LOG_PROPERTY, displayURL); } else if (!mLogFile.isEmpty()) { ev->setCustomProperty(KACalendar::APPNAME, LOG_PROPERTY, mLogFile); } ev->setCustomStatus(mEnabled ? QString() : DISABLED_STATUS); ev->setRevision(mRevision); ev->clearAlarms(); /* Always set DTSTART as date/time, and use the category "DATE" to indicate * a date-only event, instead of calling setAllDay(). This is necessary to * allow a time zone to be specified for a date-only event. Also, KAlarm * allows the alarm to float within the 24-hour period defined by the * start-of-day time (which is user-dependent and therefore can't be * written into the calendar) rather than midnight to midnight, and there * is no RFC2445 conformant way to specify this. * RFC2445 states that alarm trigger times specified in absolute terms * (rather than relative to DTSTART or DTEND) can only be specified as a * UTC DATE-TIME value. So always use a time relative to DTSTART instead of * an absolute time. */ ev->setDtStart(mStartDateTime.calendarDateTime()); ev->setAllDay(false); ev->setDtEnd(QDateTime()); const DateTime dtMain = archived ? mStartDateTime : mNextMainDateTime; int ancillaryType = 0; // 0 = invalid, 1 = time, 2 = offset DateTime ancillaryTime; // time for ancillary alarms (pre-action, extra audio, etc) int ancillaryOffset = 0; // start offset for ancillary alarms if (!mMainExpired || archived) { /* The alarm offset must always be zero for the main alarm. To determine * which recurrence is due, the property X-KDE-KALARM_NEXTRECUR is used. * If the alarm offset was non-zero, exception dates and rules would not * work since they apply to the event time, not the alarm time. */ if (!archived && checkRecur() != KARecurrence::NO_RECUR) { QDateTime dt = mNextMainDateTime.kDateTime().toTimeSpec(mStartDateTime.timeSpec()).qDateTime(); ev->setCustomProperty(KACalendar::APPNAME, NEXT_RECUR_PROPERTY, dt.toString(mNextMainDateTime.isDateOnly() ? QStringLiteral("yyyyMMdd") : QStringLiteral("yyyyMMddThhmmss"))); } // Add the main alarm initKCalAlarm(ev, 0, QStringList(), MAIN_ALARM); ancillaryOffset = 0; ancillaryType = dtMain.isValid() ? 2 : 0; } else if (mRepetition) { // Alarm repetition is normally held in the main alarm, but since // the main alarm has expired, store in a custom property. const QString param = QStringLiteral("%1:%2").arg(mRepetition.intervalMinutes()).arg(mRepetition.count()); ev->setCustomProperty(KACalendar::APPNAME, REPEAT_PROPERTY, param); } // Add subsidiary alarms if (mRepeatAtLogin || (mArchiveRepeatAtLogin && archived)) { DateTime dtl; if (mArchiveRepeatAtLogin) { dtl = mStartDateTime.calendarKDateTime().addDays(-1); } else if (mAtLoginDateTime.isValid()) { dtl = mAtLoginDateTime; } else if (mStartDateTime.isDateOnly()) { dtl = DateTime(KADateTime::currentLocalDate().addDays(-1), mStartDateTime.timeSpec()); } else { dtl = KADateTime::currentUtcDateTime(); } initKCalAlarm(ev, dtl, QStringList(AT_LOGIN_TYPE)); if (!ancillaryType && dtl.isValid()) { ancillaryTime = dtl; ancillaryType = 1; } } // Find the base date/time for calculating alarm offsets DateTime nextDateTime = mNextMainDateTime; if (mMainExpired) { if (checkRecur() == KARecurrence::NO_RECUR) { nextDateTime = mStartDateTime; } else if (!archived) { // It's a deferral of an expired recurrence. // Need to ensure that the alarm offset is to an occurrence // which isn't excluded by an exception - otherwise, it will // never be triggered. So choose the first recurrence which // isn't an exception. KADateTime dt = mRecurrence->getNextDateTime(mStartDateTime.addDays(-1).kDateTime()); dt.setDateOnly(mStartDateTime.isDateOnly()); nextDateTime = dt; } } if (mReminderMinutes && (mReminderActive != NO_REMINDER || archived)) { int startOffset; if (mReminderMinutes < 0 && mReminderActive != NO_REMINDER) { // A reminder AFTER the main alarm is active or disabled startOffset = nextDateTime.calendarKDateTime().secsTo(mReminderAfterTime.calendarKDateTime()); } else { // A reminder BEFORE the main alarm is active startOffset = -mReminderMinutes * 60; } initKCalAlarm(ev, startOffset, QStringList(REMINDER_TYPE)); // Don't set ancillary time if the reminder AFTER is hidden by a deferral if (!ancillaryType && (mReminderActive == ACTIVE_REMINDER || archived)) { ancillaryOffset = startOffset; ancillaryType = 2; } } if (mDeferral != NO_DEFERRAL) { int startOffset; QStringList list; if (mDeferralTime.isDateOnly()) { startOffset = nextDateTime.secsTo(mDeferralTime.calendarKDateTime()); list += DATE_DEFERRAL_TYPE; } else { startOffset = nextDateTime.calendarKDateTime().secsTo(mDeferralTime.calendarKDateTime()); list += TIME_DEFERRAL_TYPE; } if (mDeferral == REMINDER_DEFERRAL) { list += REMINDER_TYPE; } initKCalAlarm(ev, startOffset, list); if (!ancillaryType && mDeferralTime.isValid()) { ancillaryOffset = startOffset; ancillaryType = 2; } } if (!mTemplateName.isEmpty()) { ev->setSummary(mTemplateName); } else if (mDisplaying) { QStringList list(DISPLAYING_TYPE); if (mDisplayingFlags & KAEvent::REPEAT_AT_LOGIN) { list += AT_LOGIN_TYPE; } else if (mDisplayingFlags & DEFERRAL) { if (mDisplayingFlags & TIMED_FLAG) { list += TIME_DEFERRAL_TYPE; } else { list += DATE_DEFERRAL_TYPE; } } if (mDisplayingFlags & REMINDER) { list += REMINDER_TYPE; } initKCalAlarm(ev, mDisplayingTime, list); if (!ancillaryType && mDisplayingTime.isValid()) { ancillaryTime = mDisplayingTime; ancillaryType = 1; } } if ((mBeep || mSpeak || !mAudioFile.isEmpty()) && mActionSubType != KAEvent::AUDIO) { // A sound is specified if (ancillaryType == 2) { initKCalAlarm(ev, ancillaryOffset, QStringList(), AUDIO_ALARM); } else { initKCalAlarm(ev, ancillaryTime, QStringList(), AUDIO_ALARM); } } if (!mPreAction.isEmpty()) { // A pre-display action is specified if (ancillaryType == 2) { initKCalAlarm(ev, ancillaryOffset, QStringList(PRE_ACTION_TYPE), PRE_ACTION_ALARM); } else { initKCalAlarm(ev, ancillaryTime, QStringList(PRE_ACTION_TYPE), PRE_ACTION_ALARM); } } if (!mPostAction.isEmpty()) { // A post-display action is specified if (ancillaryType == 2) { initKCalAlarm(ev, ancillaryOffset, QStringList(POST_ACTION_TYPE), POST_ACTION_ALARM); } else { initKCalAlarm(ev, ancillaryTime, QStringList(POST_ACTION_TYPE), POST_ACTION_ALARM); } } if (mRecurrence) { mRecurrence->writeRecurrence(*ev->recurrence()); } else { ev->clearRecurrence(); } if (mCreatedDateTime.isValid()) { ev->setCreated(mCreatedDateTime.qDateTime()); } ev->setReadOnly(readOnly); ev->endUpdates(); // finally issue an update notification return true; } /****************************************************************************** * Create a new alarm for a libkcal event, and initialise it according to the * alarm action. If 'types' is non-null, it is appended to the X-KDE-KALARM-TYPE * property value list. * NOTE: The variant taking a DateTime calculates the offset from mStartDateTime, * which is not suitable for an alarm in a recurring event. */ Alarm::Ptr KAEventPrivate::initKCalAlarm(const KCalCore::Event::Ptr &event, const DateTime &dt, const QStringList &types, AlarmType type) const { const int startOffset = dt.isDateOnly() ? mStartDateTime.secsTo(dt) : mStartDateTime.calendarKDateTime().secsTo(dt.calendarKDateTime()); return initKCalAlarm(event, startOffset, types, type); } Alarm::Ptr KAEventPrivate::initKCalAlarm(const KCalCore::Event::Ptr &event, int startOffsetSecs, const QStringList &types, AlarmType type) const { QStringList alltypes; QStringList flags; Alarm::Ptr alarm = event->newAlarm(); alarm->setEnabled(true); if (type != MAIN_ALARM) { // RFC2445 specifies that absolute alarm times must be stored as a UTC DATE-TIME value. // Set the alarm time as an offset to DTSTART for the reasons described in updateKCalEvent(). alarm->setStartOffset(startOffsetSecs); } switch (type) { case AUDIO_ALARM: setAudioAlarm(alarm); if (mSpeak) { flags << KAEventPrivate::SPEAK_FLAG; } if (mRepeatSoundPause >= 0) { // Alarm::setSnoozeTime() sets 5 seconds if duration parameter is zero, // so repeat count = -1 represents 0 pause, -2 represents non-zero pause. alarm->setRepeatCount(mRepeatSoundPause ? -2 : -1); alarm->setSnoozeTime(Duration(mRepeatSoundPause, Duration::Seconds)); } break; case PRE_ACTION_ALARM: setProcedureAlarm(alarm, mPreAction); if (mExtraActionOptions & KAEvent::ExecPreActOnDeferral) { flags << KAEventPrivate::EXEC_ON_DEFERRAL_FLAG; } if (mExtraActionOptions & KAEvent::CancelOnPreActError) { flags << KAEventPrivate::CANCEL_ON_ERROR_FLAG; } if (mExtraActionOptions & KAEvent::DontShowPreActError) { flags << KAEventPrivate::DONT_SHOW_ERROR_FLAG; } break; case POST_ACTION_ALARM: setProcedureAlarm(alarm, mPostAction); break; case MAIN_ALARM: alarm->setSnoozeTime(mRepetition.interval()); alarm->setRepeatCount(mRepetition.count()); if (mRepetition) alarm->setCustomProperty(KACalendar::APPNAME, NEXT_REPEAT_PROPERTY, QString::number(mNextRepeat)); // fall through to INVALID_ALARM Q_FALLTHROUGH(); case REMINDER_ALARM: case INVALID_ALARM: { if (types == QStringList(REMINDER_TYPE) && mReminderMinutes < 0 && mReminderActive == HIDDEN_REMINDER) { // It's a reminder AFTER the alarm which is currently disabled // due to the main alarm being deferred past it. flags << HIDDEN_REMINDER_FLAG; } bool display = false; switch (mActionSubType) { case KAEvent::FILE: alltypes += FILE_TYPE; // fall through to MESSAGE Q_FALLTHROUGH(); case KAEvent::MESSAGE: alarm->setDisplayAlarm(AlarmText::toCalendarText(mText)); display = true; break; case KAEvent::COMMAND: if (mCommandScript) { alarm->setProcedureAlarm(QString(), mText); } else { setProcedureAlarm(alarm, mText); } display = mCommandDisplay; break; case KAEvent::EMAIL: alarm->setEmailAlarm(mEmailSubject, mText, mEmailAddresses, mEmailAttachments); if (mEmailFromIdentity) { flags << KAEventPrivate::EMAIL_ID_FLAG << QString::number(mEmailFromIdentity); } break; case KAEvent::AUDIO: setAudioAlarm(alarm); if (mRepeatSoundPause >= 0 && type == MAIN_ALARM) { // Indicate repeating sound in the main alarm by a non-standard // method, since it might have a sub-repetition too. alltypes << SOUND_REPEAT_TYPE << QString::number(mRepeatSoundPause); } break; } if (display) alarm->setCustomProperty(KACalendar::APPNAME, FONT_COLOUR_PROPERTY, QStringLiteral("%1;%2;%3").arg(mBgColour.name(), mFgColour.name(), mUseDefaultFont ? QString() : mFont.toString())); break; } case DEFERRED_ALARM: case DEFERRED_REMINDER_ALARM: case AT_LOGIN_ALARM: case DISPLAYING_ALARM: break; } alltypes += types; if (!alltypes.isEmpty()) { alarm->setCustomProperty(KACalendar::APPNAME, TYPE_PROPERTY, alltypes.join(QStringLiteral(","))); } if (!flags.isEmpty()) { alarm->setCustomProperty(KACalendar::APPNAME, FLAGS_PROPERTY, flags.join(SC)); } return alarm; } /****************************************************************************** * Find the index to the last daylight savings time transition at or before a * given UTC time. * Returns index, or -1 if before the first transition. */ int KAEventPrivate::transitionIndex(const QDateTime &utc, const QTimeZone::OffsetDataList& transitions) { if (utc.timeSpec() != Qt::UTC || transitions.isEmpty()) return -1; int start = 0; int end = transitions.size() - 1; while (start != end) { int i = (start + end + 1) / 2; if (transitions[i].atUtc == utc) return i; if (transitions[i].atUtc > utc) { end = i - 1; if (end < 0) return -1; } else start = i; } return start; } bool KAEvent::isValid() const { return d->mAlarmCount && (d->mAlarmCount != 1 || !d->mRepeatAtLogin); } void KAEvent::setEnabled(bool enable) { d->mEnabled = enable; } bool KAEvent::enabled() const { return d->mEnabled; } void KAEvent::setReadOnly(bool ro) { d->mReadOnly = ro; } bool KAEvent::isReadOnly() const { return d->mReadOnly; } void KAEvent::setArchive() { d->mArchive = true; } bool KAEvent::toBeArchived() const { return d->mArchive; } bool KAEvent::mainExpired() const { return d->mMainExpired; } bool KAEvent::expired() const { return (d->mDisplaying && d->mMainExpired) || d->mCategory == CalEvent::ARCHIVED; } KAEvent::Flags KAEvent::flags() const { return d->flags(); } KAEvent::Flags KAEventPrivate::flags() const { - KAEvent::Flags result({}); + KAEvent::Flags result{}; if (mBeep) { result |= KAEvent::BEEP; } if (mRepeatSoundPause >= 0) { result |= KAEvent::REPEAT_SOUND; } if (mEmailBcc) { result |= KAEvent::EMAIL_BCC; } if (mStartDateTime.isDateOnly()) { result |= KAEvent::ANY_TIME; } if (mSpeak) { result |= KAEvent::SPEAK; } if (mRepeatAtLogin) { result |= KAEvent::REPEAT_AT_LOGIN; } if (mConfirmAck) { result |= KAEvent::CONFIRM_ACK; } if (mUseDefaultFont) { result |= KAEvent::DEFAULT_FONT; } if (mCommandScript) { result |= KAEvent::SCRIPT; } if (mCommandXterm) { result |= KAEvent::EXEC_IN_XTERM; } if (mCommandDisplay) { result |= KAEvent::DISPLAY_COMMAND; } if (mCopyToKOrganizer) { result |= KAEvent::COPY_KORGANIZER; } if (mExcludeHolidays) { result |= KAEvent::EXCL_HOLIDAYS; } if (mWorkTimeOnly) { result |= KAEvent::WORK_TIME_ONLY; } if (mReminderOnceOnly) { result |= KAEvent::REMINDER_ONCE; } if (mAutoClose) { result |= KAEvent::AUTO_CLOSE; } if (!mEnabled) { result |= KAEvent::DISABLED; } return result; } /****************************************************************************** * Change the type of an event. * If it is being set to archived, set the archived indication in the event ID; * otherwise, remove the archived indication from the event ID. */ void KAEvent::setCategory(CalEvent::Type s) { d->setCategory(s); } void KAEventPrivate::setCategory(CalEvent::Type s) { if (s == mCategory) { return; } mEventID = CalEvent::uid(mEventID, s); mCategory = s; mTriggerChanged = true; // templates and archived don't have trigger times } CalEvent::Type KAEvent::category() const { return d->mCategory; } void KAEvent::setEventId(const QString &id) { d->mEventID = id; } QString KAEvent::id() const { return d->mEventID; } void KAEvent::incrementRevision() { ++d->mRevision; } int KAEvent::revision() const { return d->mRevision; } void KAEvent::setCollectionId(Akonadi::Collection::Id id) { d->mCollectionId = id; } void KAEvent::setCollectionId_const(Akonadi::Collection::Id id) const { d->mCollectionId = id; } Akonadi::Collection::Id KAEvent::collectionId() const { // A displaying alarm contains the event's original collection ID return d->mDisplaying ? -1 : d->mCollectionId; } void KAEvent::setItemId(Akonadi::Item::Id id) { d->mItemId = id; } Akonadi::Item::Id KAEvent::itemId() const { return d->mItemId; } /****************************************************************************** * Initialise an Item with the event. * Note that the event is not updated with the Item ID. * Reply = true if successful, * false if event's category does not match collection's mime types. */ bool KAEvent::setItemPayload(Akonadi::Item &item, const QStringList &collectionMimeTypes) const { QString mimetype; switch (d->mCategory) { case CalEvent::ACTIVE: mimetype = MIME_ACTIVE; break; case CalEvent::ARCHIVED: mimetype = MIME_ARCHIVED; break; case CalEvent::TEMPLATE: mimetype = MIME_TEMPLATE; break; default: Q_ASSERT(0); return false; } if (!collectionMimeTypes.contains(mimetype)) { return false; } item.setMimeType(mimetype); item.setPayload(*this); return true; } void KAEvent::setCompatibility(KACalendar::Compat c) { d->mCompatibility = c; } KACalendar::Compat KAEvent::compatibility() const { return d->mCompatibility; } QMap KAEvent::customProperties() const { return d->mCustomProperties; } KAEvent::SubAction KAEvent::actionSubType() const { return d->mActionSubType; } KAEvent::Actions KAEvent::actionTypes() const { switch (d->mActionSubType) { case MESSAGE: case FILE: return ACT_DISPLAY; case COMMAND: return d->mCommandDisplay ? ACT_DISPLAY_COMMAND : ACT_COMMAND; case EMAIL: return ACT_EMAIL; case AUDIO: return ACT_AUDIO; default: return ACT_NONE; } } void KAEvent::setLateCancel(int minutes) { if (d->mRepeatAtLogin) { minutes = 0; } d->mLateCancel = minutes; if (!minutes) { d->mAutoClose = false; } } int KAEvent::lateCancel() const { return d->mLateCancel; } void KAEvent::setAutoClose(bool ac) { d->mAutoClose = ac; } bool KAEvent::autoClose() const { return d->mAutoClose; } void KAEvent::setKMailSerialNumber(unsigned long n) { d->mKMailSerialNumber = n; } unsigned long KAEvent::kmailSerialNumber() const { return d->mKMailSerialNumber; } QString KAEvent::cleanText() const { return d->mText; } QString KAEvent::message() const { return (d->mActionSubType == MESSAGE || d->mActionSubType == EMAIL) ? d->mText : QString(); } QString KAEvent::displayMessage() const { return (d->mActionSubType == MESSAGE) ? d->mText : QString(); } QString KAEvent::fileName() const { return (d->mActionSubType == FILE) ? d->mText : QString(); } QColor KAEvent::bgColour() const { return d->mBgColour; } QColor KAEvent::fgColour() const { return d->mFgColour; } void KAEvent::setDefaultFont(const QFont &f) { KAEventPrivate::mDefaultFont = f; } bool KAEvent::useDefaultFont() const { return d->mUseDefaultFont; } QFont KAEvent::font() const { return d->mUseDefaultFont ? KAEventPrivate::mDefaultFont : d->mFont; } QString KAEvent::command() const { return (d->mActionSubType == COMMAND) ? d->mText : QString(); } bool KAEvent::commandScript() const { return d->mCommandScript; } bool KAEvent::commandXterm() const { return d->mCommandXterm; } bool KAEvent::commandDisplay() const { return d->mCommandDisplay; } void KAEvent::setCommandError(CmdErrType t) const { d->mCommandError = t; } KAEvent::CmdErrType KAEvent::commandError() const { return d->mCommandError; } void KAEvent::setLogFile(const QString &logfile) { d->mLogFile = logfile; if (!logfile.isEmpty()) { d->mCommandDisplay = d->mCommandXterm = false; } } QString KAEvent::logFile() const { return d->mLogFile; } bool KAEvent::confirmAck() const { return d->mConfirmAck; } bool KAEvent::copyToKOrganizer() const { return d->mCopyToKOrganizer; } void KAEvent::setEmail(uint from, const KCalCore::Person::List &addresses, const QString &subject, const QStringList &attachments) { d->mEmailFromIdentity = from; d->mEmailAddresses = addresses; d->mEmailSubject = subject; d->mEmailAttachments = attachments; } QString KAEvent::emailMessage() const { return (d->mActionSubType == EMAIL) ? d->mText : QString(); } uint KAEvent::emailFromId() const { return d->mEmailFromIdentity; } KCalCore::Person::List KAEvent::emailAddressees() const { return d->mEmailAddresses; } QStringList KAEvent::emailAddresses() const { return static_cast(d->mEmailAddresses); } QString KAEvent::emailAddresses(const QString &sep) const { return d->mEmailAddresses.join(sep); } QString KAEvent::joinEmailAddresses(const KCalCore::Person::List &addresses, const QString &separator) { return EmailAddressList(addresses).join(separator); } QStringList KAEvent::emailPureAddresses() const { return d->mEmailAddresses.pureAddresses(); } QString KAEvent::emailPureAddresses(const QString &sep) const { return d->mEmailAddresses.pureAddresses(sep); } QString KAEvent::emailSubject() const { return d->mEmailSubject; } QStringList KAEvent::emailAttachments() const { return d->mEmailAttachments; } QString KAEvent::emailAttachments(const QString &sep) const { return d->mEmailAttachments.join(sep); } bool KAEvent::emailBcc() const { return d->mEmailBcc; } void KAEvent::setAudioFile(const QString &filename, float volume, float fadeVolume, int fadeSeconds, int repeatPause, bool allowEmptyFile) { d->setAudioFile(filename, volume, fadeVolume, fadeSeconds, repeatPause, allowEmptyFile); } void KAEventPrivate::setAudioFile(const QString &filename, float volume, float fadeVolume, int fadeSeconds, int repeatPause, bool allowEmptyFile) { mAudioFile = filename; mSoundVolume = (!allowEmptyFile && filename.isEmpty()) ? -1 : volume; if (mSoundVolume >= 0) { mFadeVolume = (fadeSeconds > 0) ? fadeVolume : -1; mFadeSeconds = (mFadeVolume >= 0) ? fadeSeconds : 0; } else { mFadeVolume = -1; mFadeSeconds = 0; } mRepeatSoundPause = repeatPause; } QString KAEvent::audioFile() const { return d->mAudioFile; } float KAEvent::soundVolume() const { return d->mSoundVolume; } float KAEvent::fadeVolume() const { return d->mSoundVolume >= 0 && d->mFadeSeconds ? d->mFadeVolume : -1; } int KAEvent::fadeSeconds() const { return d->mSoundVolume >= 0 && d->mFadeVolume >= 0 ? d->mFadeSeconds : 0; } bool KAEvent::repeatSound() const { return d->mRepeatSoundPause >= 0; } int KAEvent::repeatSoundPause() const { return d->mRepeatSoundPause; } bool KAEvent::beep() const { return d->mBeep; } bool KAEvent::speak() const { return (d->mActionSubType == MESSAGE || (d->mActionSubType == COMMAND && d->mCommandDisplay)) && d->mSpeak; } /****************************************************************************** * Set the event to be an alarm template. */ void KAEvent::setTemplate(const QString &name, int afterTime) { d->setCategory(CalEvent::TEMPLATE); d->mTemplateName = name; d->mTemplateAfterTime = afterTime; d->mTriggerChanged = true; // templates and archived don't have trigger times } bool KAEvent::isTemplate() const { return !d->mTemplateName.isEmpty(); } QString KAEvent::templateName() const { return d->mTemplateName; } bool KAEvent::usingDefaultTime() const { return d->mTemplateAfterTime == 0; } int KAEvent::templateAfterTime() const { return d->mTemplateAfterTime; } void KAEvent::setActions(const QString &pre, const QString &post, ExtraActionOptions options) { d->mPreAction = pre; d->mPostAction = post; d->mExtraActionOptions = options; } QString KAEvent::preAction() const { return d->mPreAction; } QString KAEvent::postAction() const { return d->mPostAction; } KAEvent::ExtraActionOptions KAEvent::extraActionOptions() const { return d->mExtraActionOptions; } /****************************************************************************** * Set a reminder. * 'minutes' = number of minutes BEFORE the main alarm. */ void KAEvent::setReminder(int minutes, bool onceOnly) { d->setReminder(minutes, onceOnly); } void KAEventPrivate::setReminder(int minutes, bool onceOnly) { if (minutes > 0 && mRepeatAtLogin) { minutes = 0; } if (minutes != mReminderMinutes || (minutes && mReminderActive != ACTIVE_REMINDER)) { if (minutes && mReminderActive == NO_REMINDER) { ++mAlarmCount; } else if (!minutes && mReminderActive != NO_REMINDER) { --mAlarmCount; } mReminderMinutes = minutes; mReminderActive = minutes ? ACTIVE_REMINDER : NO_REMINDER; mReminderOnceOnly = onceOnly; mReminderAfterTime = DateTime(); mTriggerChanged = true; } } /****************************************************************************** * Activate the event's reminder which occurs AFTER the given main alarm time. * Reply = true if successful (i.e. reminder falls before the next main alarm). */ void KAEvent::activateReminderAfter(const DateTime &mainAlarmTime) { d->activateReminderAfter(mainAlarmTime); } void KAEventPrivate::activateReminderAfter(const DateTime &mainAlarmTime) { if (mReminderMinutes >= 0 || mReminderActive == ACTIVE_REMINDER || !mainAlarmTime.isValid()) { return; } // There is a reminder AFTER the main alarm. if (checkRecur() != KARecurrence::NO_RECUR) { // For a recurring alarm, the given alarm time must be a recurrence, not a sub-repetition. DateTime next; //???? For some unknown reason, addSecs(-1) returns the recurrence after the next, //???? so addSecs(-60) is used instead. if (nextRecurrence(mainAlarmTime.addSecs(-60).effectiveKDateTime(), next) == KAEvent::NO_OCCURRENCE || mainAlarmTime != next) { return; } } else if (!mRepeatAtLogin) { // For a non-recurring alarm, the given alarm time must be the main alarm time. if (mainAlarmTime != mStartDateTime) { return; } } const DateTime reminderTime = mainAlarmTime.addMins(-mReminderMinutes); DateTime next; if (nextOccurrence(mainAlarmTime.effectiveKDateTime(), next, KAEvent::RETURN_REPETITION) != KAEvent::NO_OCCURRENCE && reminderTime >= next) { return; // the reminder time is after the next occurrence of the main alarm } qCDebug(KALARMCAL_LOG) << "Setting reminder at" << reminderTime.effectiveKDateTime().toString(QStringLiteral("%Y-%m-%d %H:%M")); activate_reminder(true); mReminderAfterTime = reminderTime; } int KAEvent::reminderMinutes() const { return d->mReminderMinutes; } bool KAEvent::reminderActive() const { return d->mReminderActive == KAEventPrivate::ACTIVE_REMINDER; } bool KAEvent::reminderOnceOnly() const { return d->mReminderOnceOnly; } bool KAEvent::reminderDeferral() const { return d->mDeferral == KAEventPrivate::REMINDER_DEFERRAL; } /****************************************************************************** * Defer the event to the specified time. * If the main alarm time has passed, the main alarm is marked as expired. * If 'adjustRecurrence' is true, ensure that the next scheduled recurrence is * after the current time. */ void KAEvent::defer(const DateTime &dt, bool reminder, bool adjustRecurrence) { return d->defer(dt, reminder, adjustRecurrence); } void KAEventPrivate::defer(const DateTime &dateTime, bool reminder, bool adjustRecurrence) { startChanges(); // prevent multiple trigger time evaluation here bool setNextRepetition = false; bool checkRepetition = false; bool checkReminderAfter = false; if (checkRecur() == KARecurrence::NO_RECUR) { // Deferring a non-recurring alarm if (mReminderMinutes) { bool deferReminder = false; if (mReminderMinutes > 0) { // There's a reminder BEFORE the main alarm if (dateTime < mNextMainDateTime.effectiveKDateTime()) { deferReminder = true; } else if (mReminderActive == ACTIVE_REMINDER || mDeferral == REMINDER_DEFERRAL) { // Deferring past the main alarm time, so adjust any existing deferral set_deferral(NO_DEFERRAL); mTriggerChanged = true; } } else if (mReminderMinutes < 0 && reminder) { deferReminder = true; // deferring a reminder AFTER the main alarm } if (deferReminder) { set_deferral(REMINDER_DEFERRAL); // defer reminder alarm mDeferralTime = dateTime; mTriggerChanged = true; } if (mReminderActive == ACTIVE_REMINDER) { activate_reminder(false); mTriggerChanged = true; } } if (mDeferral != REMINDER_DEFERRAL) { // We're deferring the main alarm. // Main alarm has now expired. mNextMainDateTime = mDeferralTime = dateTime; set_deferral(NORMAL_DEFERRAL); mTriggerChanged = true; checkReminderAfter = true; if (!mMainExpired) { // Mark the alarm as expired now mMainExpired = true; --mAlarmCount; if (mRepeatAtLogin) { // Remove the repeat-at-login alarm, but keep a note of it for archiving purposes mArchiveRepeatAtLogin = true; mRepeatAtLogin = false; --mAlarmCount; } } } } else if (reminder) { // Deferring a reminder for a recurring alarm if (dateTime >= mNextMainDateTime.effectiveKDateTime()) { // Trying to defer it past the next main alarm (regardless of whether // the reminder triggered before or after the main alarm). set_deferral(NO_DEFERRAL); // (error) } else { set_deferral(REMINDER_DEFERRAL); mDeferralTime = dateTime; checkRepetition = true; } mTriggerChanged = true; } else { // Deferring a recurring alarm mDeferralTime = dateTime; if (mDeferral == NO_DEFERRAL) { set_deferral(NORMAL_DEFERRAL); } mTriggerChanged = true; checkReminderAfter = true; if (adjustRecurrence) { const KADateTime now = KADateTime::currentUtcDateTime(); if (mainEndRepeatTime() < now) { // The last repetition (if any) of the current recurrence has already passed. // Adjust to the next scheduled recurrence after now. if (!mMainExpired && setNextOccurrence(now) == KAEvent::NO_OCCURRENCE) { mMainExpired = true; --mAlarmCount; } } else { setNextRepetition = mRepetition; } } else { checkRepetition = true; } } if (checkReminderAfter && mReminderMinutes < 0 && mReminderActive != NO_REMINDER) { // Enable/disable the active reminder AFTER the main alarm, // depending on whether the deferral is before or after the reminder. mReminderActive = (mDeferralTime < mReminderAfterTime) ? ACTIVE_REMINDER : HIDDEN_REMINDER; } if (checkRepetition) { setNextRepetition = (mRepetition && mDeferralTime < mainEndRepeatTime()); } if (setNextRepetition) { // The alarm is repeated, and we're deferring to a time before the last repetition. // Set the next scheduled repetition to the one after the deferral. if (mNextMainDateTime >= mDeferralTime) { mNextRepeat = 0; } else { mNextRepeat = mRepetition.nextRepeatCount(mNextMainDateTime.kDateTime(), mDeferralTime.kDateTime()); } mTriggerChanged = true; } endChanges(); } /****************************************************************************** * Cancel any deferral alarm. */ void KAEvent::cancelDefer() { d->cancelDefer(); } void KAEventPrivate::cancelDefer() { if (mDeferral != NO_DEFERRAL) { mDeferralTime = DateTime(); set_deferral(NO_DEFERRAL); mTriggerChanged = true; } } void KAEvent::setDeferDefaultMinutes(int minutes, bool dateOnly) { d->mDeferDefaultMinutes = minutes; d->mDeferDefaultDateOnly = dateOnly; } bool KAEvent::deferred() const { return d->mDeferral > 0; } DateTime KAEvent::deferDateTime() const { return d->mDeferralTime; } /****************************************************************************** * Find the latest time which the alarm can currently be deferred to. */ DateTime KAEvent::deferralLimit(DeferLimitType *limitType) const { return d->deferralLimit(limitType); } DateTime KAEventPrivate::deferralLimit(KAEvent::DeferLimitType *limitType) const { KAEvent::DeferLimitType ltype = KAEvent::LIMIT_NONE; DateTime endTime; if (checkRecur() != KARecurrence::NO_RECUR) { // It's a recurring alarm. Find the latest time it can be deferred to: // it cannot be deferred past its next occurrence or sub-repetition, // or any advance reminder before that. DateTime reminderTime; const KADateTime now = KADateTime::currentUtcDateTime(); const KAEvent::OccurType type = nextOccurrence(now, endTime, KAEvent::RETURN_REPETITION); if (type & KAEvent::OCCURRENCE_REPEAT) { ltype = KAEvent::LIMIT_REPETITION; } else if (type == KAEvent::NO_OCCURRENCE) { ltype = KAEvent::LIMIT_NONE; } else if (mReminderActive == ACTIVE_REMINDER && mReminderMinutes > 0 && (now < (reminderTime = endTime.addMins(-mReminderMinutes)))) { endTime = reminderTime; ltype = KAEvent::LIMIT_REMINDER; } else { ltype = KAEvent::LIMIT_RECURRENCE; } } else if (mReminderMinutes < 0) { // There is a reminder alarm which occurs AFTER the main alarm. // Don't allow the reminder to be deferred past the next main alarm time. if (KADateTime::currentUtcDateTime() < mNextMainDateTime.effectiveKDateTime()) { endTime = mNextMainDateTime; ltype = KAEvent::LIMIT_MAIN; } } else if (mReminderMinutes > 0 && KADateTime::currentUtcDateTime() < mNextMainDateTime.effectiveKDateTime()) { // It's a reminder BEFORE the main alarm. // Don't allow it to be deferred past its main alarm time. endTime = mNextMainDateTime; ltype = KAEvent::LIMIT_MAIN; } if (ltype != KAEvent::LIMIT_NONE) { endTime = endTime.addMins(-1); } if (limitType) { *limitType = ltype; } return endTime; } int KAEvent::deferDefaultMinutes() const { return d->mDeferDefaultMinutes; } bool KAEvent::deferDefaultDateOnly() const { return d->mDeferDefaultDateOnly; } DateTime KAEvent::startDateTime() const { return d->mStartDateTime; } void KAEvent::setTime(const KADateTime &dt) { d->mNextMainDateTime = dt; d->mTriggerChanged = true; } DateTime KAEvent::mainDateTime(bool withRepeats) const { return d->mainDateTime(withRepeats); } QTime KAEvent::mainTime() const { return d->mNextMainDateTime.effectiveTime(); } DateTime KAEvent::mainEndRepeatTime() const { return d->mainEndRepeatTime(); } /****************************************************************************** * Set the start-of-day time for date-only alarms. */ void KAEvent::setStartOfDay(const QTime &startOfDay) { DateTime::setStartOfDay(startOfDay); #ifdef __GNUC__ #warning Does this need all trigger times for date-only alarms to be recalculated? #endif } /****************************************************************************** * Called when the user changes the start-of-day time. * Adjust the start time of the recurrence to match, for each date-only event in * a list. */ void KAEvent::adjustStartOfDay(const KAEvent::List &events) { for (int i = 0, end = events.count(); i < end; ++i) { KAEventPrivate *const p = events[i]->d; if (p->mStartDateTime.isDateOnly() && p->checkRecur() != KARecurrence::NO_RECUR) { p->mRecurrence->setStartDateTime(p->mStartDateTime.effectiveKDateTime(), true); } } } DateTime KAEvent::nextTrigger(TriggerType type) const { d->calcTriggerTimes(); switch (type) { case ALL_TRIGGER: return d->mAllTrigger; case MAIN_TRIGGER: return d->mMainTrigger; case ALL_WORK_TRIGGER: return d->mAllWorkTrigger; case WORK_TRIGGER: return d->mMainWorkTrigger; case DISPLAY_TRIGGER: { const bool reminderAfter = d->mMainExpired && d->mReminderActive && d->mReminderMinutes < 0; return d->checkRecur() != KARecurrence::NO_RECUR && (d->mWorkTimeOnly || d->mExcludeHolidays) ? (reminderAfter ? d->mAllWorkTrigger : d->mMainWorkTrigger) : (reminderAfter ? d->mAllTrigger : d->mMainTrigger); } default: return DateTime(); } } void KAEvent::setCreatedDateTime(const KADateTime &dt) { d->mCreatedDateTime = dt; } KADateTime KAEvent::createdDateTime() const { return d->mCreatedDateTime; } /****************************************************************************** * Set or clear repeat-at-login. */ void KAEvent::setRepeatAtLogin(bool rl) { d->setRepeatAtLogin(rl); } void KAEventPrivate::setRepeatAtLogin(bool rl) { if (rl && !mRepeatAtLogin) { setRepeatAtLoginTrue(true); // clear incompatible statuses ++mAlarmCount; } else if (!rl && mRepeatAtLogin) { --mAlarmCount; } mRepeatAtLogin = rl; mTriggerChanged = true; } /****************************************************************************** * Clear incompatible statuses when repeat-at-login is set. */ void KAEventPrivate::setRepeatAtLoginTrue(bool clearReminder) { clearRecur(); // clear recurrences if (mReminderMinutes >= 0 && clearReminder) { setReminder(0, false); // clear pre-alarm reminder } mLateCancel = 0; mAutoClose = false; mCopyToKOrganizer = false; } bool KAEvent::repeatAtLogin(bool includeArchived) const { return d->mRepeatAtLogin || (includeArchived && d->mArchiveRepeatAtLogin); } void KAEvent::setExcludeHolidays(bool ex) { d->mExcludeHolidays = ex; d->mExcludeHolidayRegion = KAEventPrivate::holidays(); // Option only affects recurring alarms d->mTriggerChanged = (d->checkRecur() != KARecurrence::NO_RECUR); } bool KAEvent::holidaysExcluded() const { return d->mExcludeHolidays; } /****************************************************************************** * Set a new holiday region. * Alarms which exclude holidays record the pointer to the holiday definition * at the time their next trigger times were last calculated. The change in * holiday definition pointer will cause their next trigger times to be * recalculated. */ void KAEvent::setHolidays(const HolidayRegion &h) { KAEventPrivate::mHolidays.reset(new HolidayRegion(h.regionCode())); } void KAEvent::setWorkTimeOnly(bool wto) { d->mWorkTimeOnly = wto; // Option only affects recurring alarms d->mTriggerChanged = (d->checkRecur() != KARecurrence::NO_RECUR); } bool KAEvent::workTimeOnly() const { return d->mWorkTimeOnly; } /****************************************************************************** * Check whether a date/time is during working hours and/or holidays, depending * on the flags set for the specified event. */ bool KAEvent::isWorkingTime(const KADateTime &dt) const { return d->isWorkingTime(dt); } bool KAEventPrivate::isWorkingTime(const KADateTime &dt) const { if ((mWorkTimeOnly && !mWorkDays.testBit(dt.date().dayOfWeek() - 1)) || (mExcludeHolidays && holidays()->isHoliday(dt.date()))) { return false; } if (!mWorkTimeOnly) { return true; } return dt.isDateOnly() || (dt.time() >= mWorkDayStart && dt.time() < mWorkDayEnd); } /****************************************************************************** * Set new working days and times. * Increment a counter so that working-time-only alarms can detect that they * need to update their next trigger time. */ void KAEvent::setWorkTime(const QBitArray &days, const QTime &start, const QTime &end) { if (days != KAEventPrivate::mWorkDays || start != KAEventPrivate::mWorkDayStart || end != KAEventPrivate::mWorkDayEnd) { KAEventPrivate::mWorkDays = days; KAEventPrivate::mWorkDayStart = start; KAEventPrivate::mWorkDayEnd = end; if (!++KAEventPrivate::mWorkTimeIndex) { ++KAEventPrivate::mWorkTimeIndex; } } } /****************************************************************************** * Clear the event's recurrence and alarm repetition data. */ void KAEvent::setNoRecur() { d->clearRecur(); } void KAEventPrivate::clearRecur() { if (mRecurrence || mRepetition) { delete mRecurrence; mRecurrence = nullptr; mRepetition.set(0, 0); mTriggerChanged = true; } mNextRepeat = 0; } /****************************************************************************** * Initialise the event's recurrence from a KCal::Recurrence. * The event's start date/time is not changed. */ void KAEvent::setRecurrence(const KARecurrence &recurrence) { d->setRecurrence(recurrence); } void KAEventPrivate::setRecurrence(const KARecurrence &recurrence) { startChanges(); // prevent multiple trigger time evaluation here if (recurrence.recurs()) { delete mRecurrence; mRecurrence = new KARecurrence(recurrence); mRecurrence->setStartDateTime(mStartDateTime.effectiveKDateTime(), mStartDateTime.isDateOnly()); mTriggerChanged = true; // Adjust sub-repetition values to fit the recurrence. setRepetition(mRepetition); } else { clearRecur(); } endChanges(); } /****************************************************************************** * Set the recurrence to recur at a minutes interval. * Parameters: * freq = how many minutes between recurrences. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date/time (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurMinutely(int freq, int count, const KADateTime &end) { const bool success = d->setRecur(RecurrenceRule::rMinutely, freq, count, end); d->mTriggerChanged = true; return success; } /****************************************************************************** * Set the recurrence to recur daily. * Parameters: * freq = how many days between recurrences. * days = which days of the week alarms are allowed to occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurDaily(int freq, const QBitArray &days, int count, const QDate &end) { const bool success = d->setRecur(RecurrenceRule::rDaily, freq, count, end); if (success) { int n = 0; for (int i = 0; i < 7; ++i) { if (days.testBit(i)) { ++n; } } if (n < 7) { d->mRecurrence->addWeeklyDays(days); } } d->mTriggerChanged = true; return success; } /****************************************************************************** * Set the recurrence to recur weekly, on the specified weekdays. * Parameters: * freq = how many weeks between recurrences. * days = which days of the week alarms should occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurWeekly(int freq, const QBitArray &days, int count, const QDate &end) { const bool success = d->setRecur(RecurrenceRule::rWeekly, freq, count, end); if (success) { d->mRecurrence->addWeeklyDays(days); } d->mTriggerChanged = true; return success; } /****************************************************************************** * Set the recurrence to recur monthly, on the specified days within the month. * Parameters: * freq = how many months between recurrences. * days = which days of the month alarms should occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurMonthlyByDate(int freq, const QVector &days, int count, const QDate &end) { const bool success = d->setRecur(RecurrenceRule::rMonthly, freq, count, end); if (success) { for (int i = 0, end = days.count(); i < end; ++i) { d->mRecurrence->addMonthlyDate(days[i]); } } d->mTriggerChanged = true; return success; } /****************************************************************************** * Set the recurrence to recur monthly, on the specified weekdays in the * specified weeks of the month. * Parameters: * freq = how many months between recurrences. * posns = which days of the week/weeks of the month alarms should occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurMonthlyByPos(int freq, const QVector &posns, int count, const QDate &end) { const bool success = d->setRecur(RecurrenceRule::rMonthly, freq, count, end); if (success) { for (int i = 0, end = posns.count(); i < end; ++i) { d->mRecurrence->addMonthlyPos(posns[i].weeknum, posns[i].days); } } d->mTriggerChanged = true; return success; } /****************************************************************************** * Set the recurrence to recur annually, on the specified start date in each * of the specified months. * Parameters: * freq = how many years between recurrences. * months = which months of the year alarms should occur on. * day = day of month, or 0 to use start date * feb29 = when February 29th should recur in non-leap years. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurAnnualByDate(int freq, const QVector &months, int day, KARecurrence::Feb29Type feb29, int count, const QDate &end) { const bool success = d->setRecur(RecurrenceRule::rYearly, freq, count, end, feb29); if (success) { for (int i = 0, end = months.count(); i < end; ++i) { d->mRecurrence->addYearlyMonth(months[i]); } if (day) { d->mRecurrence->addMonthlyDate(day); } } d->mTriggerChanged = true; return success; } /****************************************************************************** * Set the recurrence to recur annually, on the specified weekdays in the * specified weeks of the specified months. * Parameters: * freq = how many years between recurrences. * posns = which days of the week/weeks of the month alarms should occur on. * months = which months of the year alarms should occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurAnnualByPos(int freq, const QVector &posns, const QVector &months, int count, const QDate &end) { const bool success = d->setRecur(RecurrenceRule::rYearly, freq, count, end); if (success) { int i = 0; int iend; for (iend = months.count(); i < iend; ++i) { d->mRecurrence->addYearlyMonth(months[i]); } for (i = 0, iend = posns.count(); i < iend; ++i) { d->mRecurrence->addYearlyPos(posns[i].weeknum, posns[i].days); } } d->mTriggerChanged = true; return success; } /****************************************************************************** * Initialise the event's recurrence data. * Parameters: * freq = how many intervals between recurrences. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date/time (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEventPrivate::setRecur(KCalCore::RecurrenceRule::PeriodType recurType, int freq, int count, const QDate &end, KARecurrence::Feb29Type feb29) { KADateTime edt = mNextMainDateTime.kDateTime(); edt.setDate(end); return setRecur(recurType, freq, count, edt, feb29); } bool KAEventPrivate::setRecur(KCalCore::RecurrenceRule::PeriodType recurType, int freq, int count, const KADateTime &end, KARecurrence::Feb29Type feb29) { if (count >= -1 && (count || end.date().isValid())) { if (!mRecurrence) { mRecurrence = new KARecurrence; } if (mRecurrence->init(recurType, freq, count, mNextMainDateTime.kDateTime(), end, feb29)) { return true; } } clearRecur(); return false; } bool KAEvent::recurs() const { return d->checkRecur() != KARecurrence::NO_RECUR; } KARecurrence::Type KAEvent::recurType() const { return d->checkRecur(); } KARecurrence *KAEvent::recurrence() const { return d->mRecurrence; } /****************************************************************************** * Return the recurrence interval in units of the recurrence period type. */ int KAEvent::recurInterval() const { if (d->mRecurrence) { switch (d->mRecurrence->type()) { case KARecurrence::MINUTELY: case KARecurrence::DAILY: case KARecurrence::WEEKLY: case KARecurrence::MONTHLY_DAY: case KARecurrence::MONTHLY_POS: case KARecurrence::ANNUAL_DATE: case KARecurrence::ANNUAL_POS: return d->mRecurrence->frequency(); default: break; } } return 0; } Duration KAEvent::longestRecurrenceInterval() const { return d->mRecurrence ? d->mRecurrence->longestInterval() : Duration(0); } /****************************************************************************** * Adjust the event date/time to the first recurrence of the event, on or after * start date/time. The event start date may not be a recurrence date, in which * case a later date will be set. */ void KAEvent::setFirstRecurrence() { d->setFirstRecurrence(); } void KAEventPrivate::setFirstRecurrence() { switch (checkRecur()) { case KARecurrence::NO_RECUR: case KARecurrence::MINUTELY: return; case KARecurrence::ANNUAL_DATE: case KARecurrence::ANNUAL_POS: if (mRecurrence->yearMonths().isEmpty()) { return; // (presumably it's a template) } break; case KARecurrence::DAILY: case KARecurrence::WEEKLY: case KARecurrence::MONTHLY_POS: case KARecurrence::MONTHLY_DAY: break; } const KADateTime recurStart = mRecurrence->startDateTime(); if (mRecurrence->recursOn(recurStart.date(), recurStart.timeSpec())) { return; // it already recurs on the start date } // Set the frequency to 1 to find the first possible occurrence const int frequency = mRecurrence->frequency(); mRecurrence->setFrequency(1); DateTime next; nextRecurrence(mNextMainDateTime.effectiveKDateTime(), next); if (!next.isValid()) { mRecurrence->setStartDateTime(recurStart, mStartDateTime.isDateOnly()); // reinstate the old value } else { mRecurrence->setStartDateTime(next.effectiveKDateTime(), next.isDateOnly()); mStartDateTime = mNextMainDateTime = next; mTriggerChanged = true; } mRecurrence->setFrequency(frequency); // restore the frequency } /****************************************************************************** * Return the recurrence interval as text suitable for display. */ QString KAEvent::recurrenceText(bool brief) const { if (d->mRepeatAtLogin) { return brief ? i18nc("@info Brief form of 'At Login'", "Login") : i18nc("@info", "At login"); } if (d->mRecurrence) { const int frequency = d->mRecurrence->frequency(); switch (d->mRecurrence->defaultRRuleConst()->recurrenceType()) { case RecurrenceRule::rMinutely: if (frequency < 60) { return i18ncp("@info", "1 Minute", "%1 Minutes", frequency); } else if (frequency % 60 == 0) { return i18ncp("@info", "1 Hour", "%1 Hours", frequency / 60); } else { QString mins; return i18nc("@info Hours and minutes", "%1h %2m", frequency / 60, mins.sprintf("%02d", frequency % 60)); } case RecurrenceRule::rDaily: return i18ncp("@info", "1 Day", "%1 Days", frequency); case RecurrenceRule::rWeekly: return i18ncp("@info", "1 Week", "%1 Weeks", frequency); case RecurrenceRule::rMonthly: return i18ncp("@info", "1 Month", "%1 Months", frequency); case RecurrenceRule::rYearly: return i18ncp("@info", "1 Year", "%1 Years", frequency); case RecurrenceRule::rNone: default: break; } } return brief ? QString() : i18nc("@info No recurrence", "None"); } /****************************************************************************** * Initialise the event's sub-repetition. * The repetition length is adjusted if necessary to fit the recurrence interval. * If the event doesn't recur, the sub-repetition is cleared. * Reply = false if a non-daily interval was specified for a date-only recurrence. */ bool KAEvent::setRepetition(const Repetition &r) { return d->setRepetition(r); } bool KAEventPrivate::setRepetition(const Repetition &repetition) { // Don't set mRepetition to zero at the start of this function, in case the // 'repetition' parameter passed in is a reference to mRepetition. mNextRepeat = 0; if (repetition && !mRepeatAtLogin) { Q_ASSERT(checkRecur() != KARecurrence::NO_RECUR); if (!repetition.isDaily() && mStartDateTime.isDateOnly()) { mRepetition.set(0, 0); return false; // interval must be in units of days for date-only alarms } Duration longestInterval = mRecurrence->longestInterval(); if (repetition.duration() >= longestInterval) { const int count = mStartDateTime.isDateOnly() ? (longestInterval.asDays() - 1) / repetition.intervalDays() : (longestInterval.asSeconds() - 1) / repetition.intervalSeconds(); mRepetition.set(repetition.interval(), count); } else { mRepetition = repetition; } mTriggerChanged = true; } else if (mRepetition) { mRepetition.set(0, 0); mTriggerChanged = true; } return true; } Repetition KAEvent::repetition() const { return d->mRepetition; } int KAEvent::nextRepetition() const { return d->mNextRepeat; } /****************************************************************************** * Return the repetition interval as text suitable for display. */ QString KAEvent::repetitionText(bool brief) const { if (d->mRepetition) { if (!d->mRepetition.isDaily()) { const int minutes = d->mRepetition.intervalMinutes(); if (minutes < 60) { return i18ncp("@info", "1 Minute", "%1 Minutes", minutes); } if (minutes % 60 == 0) { return i18ncp("@info", "1 Hour", "%1 Hours", minutes / 60); } QString mins; return i18nc("@info Hours and minutes", "%1h %2m", minutes / 60, mins.sprintf("%02d", minutes % 60)); } const int days = d->mRepetition.intervalDays(); if (days % 7) { return i18ncp("@info", "1 Day", "%1 Days", days); } return i18ncp("@info", "1 Week", "%1 Weeks", days / 7); } return brief ? QString() : i18nc("@info No repetition", "None"); } /****************************************************************************** * Determine whether the event will occur after the specified date/time. * If 'includeRepetitions' is true and the alarm has a sub-repetition, it * returns true if any repetitions occur after the specified date/time. */ bool KAEvent::occursAfter(const KADateTime &preDateTime, bool includeRepetitions) const { return d->occursAfter(preDateTime, includeRepetitions); } bool KAEventPrivate::occursAfter(const KADateTime &preDateTime, bool includeRepetitions) const { KADateTime dt; if (checkRecur() != KARecurrence::NO_RECUR) { if (mRecurrence->duration() < 0) { return true; // infinite recurrence } dt = mRecurrence->endDateTime(); } else { dt = mNextMainDateTime.effectiveKDateTime(); } if (mStartDateTime.isDateOnly()) { QDate pre = preDateTime.date(); if (preDateTime.toTimeSpec(mStartDateTime.timeSpec()).time() < DateTime::startOfDay()) { pre = pre.addDays(-1); // today's recurrence (if today recurs) is still to come } if (pre < dt.date()) { return true; } } else if (preDateTime < dt) { return true; } if (includeRepetitions && mRepetition) { if (preDateTime < KADateTime(mRepetition.duration().end(dt.qDateTime()))) { return true; } } return false; } /****************************************************************************** * Set the date/time of the event to the next scheduled occurrence after the * specified date/time, provided that this is later than its current date/time. * Any reminder alarm is adjusted accordingly. * If the alarm has a sub-repetition, and a repetition of a previous recurrence * occurs after the specified date/time, that repetition is set as the next * occurrence. */ KAEvent::OccurType KAEvent::setNextOccurrence(const KADateTime &preDateTime) { return d->setNextOccurrence(preDateTime); } KAEvent::OccurType KAEventPrivate::setNextOccurrence(const KADateTime &preDateTime) { if (preDateTime < mNextMainDateTime.effectiveKDateTime()) { return KAEvent::FIRST_OR_ONLY_OCCURRENCE; // it might not be the first recurrence - tant pis } KADateTime pre = preDateTime; // If there are repetitions, adjust the comparison date/time so that // we find the earliest recurrence which has a repetition falling after // the specified preDateTime. if (mRepetition) { pre = KADateTime(mRepetition.duration(-mRepetition.count()).end(preDateTime.qDateTime())); } DateTime afterPre; // next recurrence after 'pre' KAEvent::OccurType type; if (pre < mNextMainDateTime.effectiveKDateTime()) { afterPre = mNextMainDateTime; type = KAEvent::FIRST_OR_ONLY_OCCURRENCE; // may not actually be the first occurrence } else if (checkRecur() != KARecurrence::NO_RECUR) { type = nextRecurrence(pre, afterPre); if (type == KAEvent::NO_OCCURRENCE) { return KAEvent::NO_OCCURRENCE; } if (type != KAEvent::FIRST_OR_ONLY_OCCURRENCE && afterPre != mNextMainDateTime) { // Need to reschedule the next trigger date/time mNextMainDateTime = afterPre; if (mReminderMinutes > 0 && (mDeferral == REMINDER_DEFERRAL || mReminderActive != ACTIVE_REMINDER)) { // Reinstate the advance reminder for the rescheduled recurrence. // Note that a reminder AFTER the main alarm will be left active. activate_reminder(!mReminderOnceOnly); } if (mDeferral == REMINDER_DEFERRAL) { set_deferral(NO_DEFERRAL); } mTriggerChanged = true; } } else { return KAEvent::NO_OCCURRENCE; } if (mRepetition) { if (afterPre <= preDateTime) { // The next occurrence is a sub-repetition. type = static_cast(type | KAEvent::OCCURRENCE_REPEAT); mNextRepeat = mRepetition.nextRepeatCount(afterPre.effectiveKDateTime(), preDateTime); // Repetitions can't have a reminder, so remove any. activate_reminder(false); if (mDeferral == REMINDER_DEFERRAL) { set_deferral(NO_DEFERRAL); } mTriggerChanged = true; } else if (mNextRepeat) { // The next occurrence is the main occurrence, not a repetition mNextRepeat = 0; mTriggerChanged = true; } } return type; } /****************************************************************************** * Get the date/time of the next occurrence of the event, after the specified * date/time. * 'result' = date/time of next occurrence, or invalid date/time if none. */ KAEvent::OccurType KAEvent::nextOccurrence(const KADateTime &preDateTime, DateTime &result, OccurOption o) const { return d->nextOccurrence(preDateTime, result, o); } KAEvent::OccurType KAEventPrivate::nextOccurrence(const KADateTime &preDateTime, DateTime &result, KAEvent::OccurOption includeRepetitions) const { KADateTime pre = preDateTime; if (includeRepetitions != KAEvent::IGNORE_REPETITION) { // RETURN_REPETITION or ALLOW_FOR_REPETITION if (!mRepetition) { includeRepetitions = KAEvent::IGNORE_REPETITION; } else { pre = KADateTime(mRepetition.duration(-mRepetition.count()).end(preDateTime.qDateTime())); } } KAEvent::OccurType type; const bool recurs = (checkRecur() != KARecurrence::NO_RECUR); if (recurs) { type = nextRecurrence(pre, result); } else if (pre < mNextMainDateTime.effectiveKDateTime()) { result = mNextMainDateTime; type = KAEvent::FIRST_OR_ONLY_OCCURRENCE; } else { result = DateTime(); type = KAEvent::NO_OCCURRENCE; } if (type != KAEvent::NO_OCCURRENCE && result <= preDateTime && includeRepetitions != KAEvent::IGNORE_REPETITION) { // RETURN_REPETITION or ALLOW_FOR_REPETITION // The next occurrence is a sub-repetition int repetition = mRepetition.nextRepeatCount(result.kDateTime(), preDateTime); const DateTime repeatDT(mRepetition.duration(repetition).end(result.qDateTime())); if (recurs) { // We've found a recurrence before the specified date/time, which has // a sub-repetition after the date/time. // However, if the intervals between recurrences vary, we could possibly // have missed a later recurrence which fits the criterion, so check again. DateTime dt; const KAEvent::OccurType newType = previousOccurrence(repeatDT.effectiveKDateTime(), dt, false); if (dt > result) { type = newType; result = dt; if (includeRepetitions == KAEvent::RETURN_REPETITION && result <= preDateTime) { // The next occurrence is a sub-repetition repetition = mRepetition.nextRepeatCount(result.kDateTime(), preDateTime); result = DateTime(mRepetition.duration(repetition).end(result.qDateTime())); type = static_cast(type | KAEvent::OCCURRENCE_REPEAT); } return type; } } if (includeRepetitions == KAEvent::RETURN_REPETITION) { // The next occurrence is a sub-repetition result = repeatDT; type = static_cast(type | KAEvent::OCCURRENCE_REPEAT); } } return type; } /****************************************************************************** * Get the date/time of the last previous occurrence of the event, before the * specified date/time. * If 'includeRepetitions' is true and the alarm has a sub-repetition, the * last previous repetition is returned if appropriate. * 'result' = date/time of previous occurrence, or invalid date/time if none. */ KAEvent::OccurType KAEvent::previousOccurrence(const KADateTime &afterDateTime, DateTime &result, bool includeRepetitions) const { return d->previousOccurrence(afterDateTime, result, includeRepetitions); } KAEvent::OccurType KAEventPrivate::previousOccurrence(const KADateTime &afterDateTime, DateTime &result, bool includeRepetitions) const { Q_ASSERT(!afterDateTime.isDateOnly()); if (mStartDateTime >= afterDateTime) { result = KADateTime(); return KAEvent::NO_OCCURRENCE; // the event starts after the specified date/time } // Find the latest recurrence of the event KAEvent::OccurType type; if (checkRecur() == KARecurrence::NO_RECUR) { result = mStartDateTime; type = KAEvent::FIRST_OR_ONLY_OCCURRENCE; } else { const KADateTime recurStart = mRecurrence->startDateTime(); KADateTime after = afterDateTime.toTimeSpec(mStartDateTime.timeSpec()); if (mStartDateTime.isDateOnly() && afterDateTime.time() > DateTime::startOfDay()) { after = after.addDays(1); // today's recurrence (if today recurs) has passed } const KADateTime dt = mRecurrence->getPreviousDateTime(after); result = dt; result.setDateOnly(mStartDateTime.isDateOnly()); if (!dt.isValid()) { return KAEvent::NO_OCCURRENCE; } if (dt == recurStart) { type = KAEvent::FIRST_OR_ONLY_OCCURRENCE; } else if (mRecurrence->getNextDateTime(dt).isValid()) { type = result.isDateOnly() ? KAEvent::RECURRENCE_DATE : KAEvent::RECURRENCE_DATE_TIME; } else { type = KAEvent::LAST_RECURRENCE; } } if (includeRepetitions && mRepetition) { // Find the latest repetition which is before the specified time. const int repetition = mRepetition.previousRepeatCount(result.effectiveKDateTime(), afterDateTime); if (repetition > 0) { result = DateTime(mRepetition.duration(qMin(repetition, mRepetition.count())).end(result.qDateTime())); return static_cast(type | KAEvent::OCCURRENCE_REPEAT); } } return type; } /****************************************************************************** * Set the event to be a copy of the specified event, making the specified * alarm the 'displaying' alarm. * The purpose of setting up a 'displaying' alarm is to be able to reinstate * the alarm message in case of a crash, or to reinstate it should the user * choose to defer the alarm. Note that even repeat-at-login alarms need to be * saved in case their end time expires before the next login. * Reply = true if successful, false if alarm was not copied. */ bool KAEvent::setDisplaying(const KAEvent &e, KAAlarm::Type t, Akonadi::Collection::Id id, const KADateTime &dt, bool showEdit, bool showDefer) { return d->setDisplaying(*e.d, t, id, dt, showEdit, showDefer); } bool KAEventPrivate::setDisplaying(const KAEventPrivate &event, KAAlarm::Type alarmType, Akonadi::Collection::Id collectionId, const KADateTime &repeatAtLoginTime, bool showEdit, bool showDefer) { if (!mDisplaying && (alarmType == KAAlarm::MAIN_ALARM || alarmType == KAAlarm::REMINDER_ALARM || alarmType == KAAlarm::DEFERRED_REMINDER_ALARM || alarmType == KAAlarm::DEFERRED_ALARM || alarmType == KAAlarm::AT_LOGIN_ALARM)) { //qCDebug(KALARMCAL_LOG)<= 0) { qCDebug(KALARMCAL_LOG) << "-- mSoundVolume:" << mSoundVolume; if (mFadeVolume >= 0) { qCDebug(KALARMCAL_LOG) << "-- mFadeVolume:" << mFadeVolume; qCDebug(KALARMCAL_LOG) << "-- mFadeSeconds:" << mFadeSeconds; } else { qCDebug(KALARMCAL_LOG) << "-- mFadeVolume:-:"; } } else { qCDebug(KALARMCAL_LOG) << "-- mSoundVolume:-:"; } qCDebug(KALARMCAL_LOG) << "-- mRepeatSoundPause:" << mRepeatSoundPause; } qCDebug(KALARMCAL_LOG) << "-- mKMailSerialNumber:" << mKMailSerialNumber; qCDebug(KALARMCAL_LOG) << "-- mCopyToKOrganizer:" << mCopyToKOrganizer; qCDebug(KALARMCAL_LOG) << "-- mExcludeHolidays:" << mExcludeHolidays; qCDebug(KALARMCAL_LOG) << "-- mWorkTimeOnly:" << mWorkTimeOnly; qCDebug(KALARMCAL_LOG) << "-- mStartDateTime:" << mStartDateTime.toString(); // qCDebug(KALARMCAL_LOG) << "-- mCreatedDateTime:" << mCreatedDateTime; qCDebug(KALARMCAL_LOG) << "-- mRepeatAtLogin:" << mRepeatAtLogin; // if (mRepeatAtLogin) // qCDebug(KALARMCAL_LOG) << "-- mAtLoginDateTime:" << mAtLoginDateTime; qCDebug(KALARMCAL_LOG) << "-- mArchiveRepeatAtLogin:" << mArchiveRepeatAtLogin; qCDebug(KALARMCAL_LOG) << "-- mConfirmAck:" << mConfirmAck; qCDebug(KALARMCAL_LOG) << "-- mEnabled:" << mEnabled; qCDebug(KALARMCAL_LOG) << "-- mItemId:" << mItemId; qCDebug(KALARMCAL_LOG) << "-- mCollectionId:" << mCollectionId; qCDebug(KALARMCAL_LOG) << "-- mCompatibility:" << mCompatibility; qCDebug(KALARMCAL_LOG) << "-- mReadOnly:" << mReadOnly; if (mReminderMinutes) { qCDebug(KALARMCAL_LOG) << "-- mReminderMinutes:" << mReminderMinutes; qCDebug(KALARMCAL_LOG) << "-- mReminderActive:" << (mReminderActive == ACTIVE_REMINDER ? "active" : mReminderActive == HIDDEN_REMINDER ? "hidden" : "no"); qCDebug(KALARMCAL_LOG) << "-- mReminderOnceOnly:" << mReminderOnceOnly; } else if (mDeferral > 0) { qCDebug(KALARMCAL_LOG) << "-- mDeferral:" << (mDeferral == NORMAL_DEFERRAL ? "normal" : "reminder"); qCDebug(KALARMCAL_LOG) << "-- mDeferralTime:" << mDeferralTime.toString(); } qCDebug(KALARMCAL_LOG) << "-- mDeferDefaultMinutes:" << mDeferDefaultMinutes; if (mDeferDefaultMinutes) { qCDebug(KALARMCAL_LOG) << "-- mDeferDefaultDateOnly:" << mDeferDefaultDateOnly; } if (mDisplaying) { qCDebug(KALARMCAL_LOG) << "-- mDisplayingTime:" << mDisplayingTime.toString(); qCDebug(KALARMCAL_LOG) << "-- mDisplayingFlags:" << mDisplayingFlags; qCDebug(KALARMCAL_LOG) << "-- mDisplayingDefer:" << mDisplayingDefer; qCDebug(KALARMCAL_LOG) << "-- mDisplayingEdit:" << mDisplayingEdit; } qCDebug(KALARMCAL_LOG) << "-- mRevision:" << mRevision; qCDebug(KALARMCAL_LOG) << "-- mRecurrence:" << mRecurrence; if (!mRepetition) { qCDebug(KALARMCAL_LOG) << "-- mRepetition: 0"; } else if (mRepetition.isDaily()) { qCDebug(KALARMCAL_LOG) << "-- mRepetition: count:" << mRepetition.count() << ", interval:" << mRepetition.intervalDays() << "days"; } else { qCDebug(KALARMCAL_LOG) << "-- mRepetition: count:" << mRepetition.count() << ", interval:" << mRepetition.intervalMinutes() << "minutes"; } qCDebug(KALARMCAL_LOG) << "-- mNextRepeat:" << mNextRepeat; qCDebug(KALARMCAL_LOG) << "-- mAlarmCount:" << mAlarmCount; qCDebug(KALARMCAL_LOG) << "-- mMainExpired:" << mMainExpired; qCDebug(KALARMCAL_LOG) << "-- mDisplaying:" << mDisplaying; qCDebug(KALARMCAL_LOG) << "KAEvent dump end"; } #endif /****************************************************************************** * Fetch the start and next date/time for a KCal::Event. * Reply = next main date/time. */ DateTime KAEventPrivate::readDateTime(const Event::Ptr &event, bool localZone, bool dateOnly, DateTime &start) { start = DateTime(event->dtStart()); if (dateOnly) { // A date-only event is indicated by the X-KDE-KALARM-FLAGS:DATE property, not // by a date-only start date/time (for the reasons given in updateKCalEvent()). start.setDateOnly(true); } if (localZone) { // The local system time zone is indicated by the X-KDE-KALARM-FLAGS:LOCAL // property, because QDateTime values with time spec Qt::LocalTime are not // stored correctly in the calendar file. start.setTimeSpec(KADateTime::LocalZone); } DateTime next = start; const int SZ_YEAR = 4; // number of digits in year value const int SZ_MONTH = 2; // number of digits in month value const int SZ_DAY = 2; // number of digits in day value const int SZ_DATE = SZ_YEAR + SZ_MONTH + SZ_DAY; // total size of date value const int IX_TIME = SZ_DATE + 1; // offset to time value const int SZ_HOUR = 2; // number of digits in hour value const int SZ_MIN = 2; // number of digits in minute value const int SZ_SEC = 2; // number of digits in second value const int SZ_TIME = SZ_HOUR + SZ_MIN + SZ_SEC; // total size of time value const QString prop = event->customProperty(KACalendar::APPNAME, KAEventPrivate::NEXT_RECUR_PROPERTY); if (prop.length() >= SZ_DATE) { // The next due recurrence time is specified const QDate d(prop.leftRef(SZ_YEAR).toInt(), prop.midRef(SZ_YEAR, SZ_MONTH).toInt(), prop.midRef(SZ_YEAR + SZ_MONTH, SZ_DAY).toInt()); if (d.isValid()) { if (dateOnly && prop.length() == SZ_DATE) { next.setDate(d); } else if (!dateOnly && prop.length() == IX_TIME + SZ_TIME && prop[SZ_DATE] == QLatin1Char('T')) { const QTime t(prop.midRef(IX_TIME, SZ_HOUR).toInt(), prop.midRef(IX_TIME + SZ_HOUR, SZ_MIN).toInt(), prop.midRef(IX_TIME + SZ_HOUR + SZ_MIN, SZ_SEC).toInt()); if (t.isValid()) { next.setDate(d); next.setTime(t); } } if (next < start) { next = start; // ensure next recurrence time is valid } } } return next; } /****************************************************************************** * Parse the alarms for a KCal::Event. * Reply = map of alarm data, indexed by KAAlarm::Type */ void KAEventPrivate::readAlarms(const Event::Ptr &event, AlarmMap *alarmMap, bool cmdDisplay) { const Alarm::List alarms = event->alarms(); // Check if it's an audio event with no display alarm bool audioOnly = false; for (int i = 0, end = alarms.count(); i < end; ++i) { switch (alarms[i]->type()) { case Alarm::Display: case Alarm::Procedure: audioOnly = false; i = end; // exit from the 'for' loop break; case Alarm::Audio: audioOnly = true; break; default: break; } } for (int i = 0, end = alarms.count(); i < end; ++i) { // Parse the next alarm's text AlarmData data; readAlarm(alarms[i], data, audioOnly, cmdDisplay); if (data.type != INVALID_ALARM) { alarmMap->insert(data.type, data); } } } /****************************************************************************** * Parse a KCal::Alarm. * If 'audioMain' is true, the event contains an audio alarm but no display alarm. * Reply = alarm ID (sequence number) */ void KAEventPrivate::readAlarm(const Alarm::Ptr &alarm, AlarmData &data, bool audioMain, bool cmdDisplay) { // Parse the next alarm's text data.alarm = alarm; data.displayingFlags = 0; data.isEmailText = false; data.speak = false; data.hiddenReminder = false; data.timedDeferral = false; data.nextRepeat = 0; data.repeatSoundPause = -1; if (alarm->repeatCount()) { bool ok; const QString property = alarm->customProperty(KACalendar::APPNAME, KAEventPrivate::NEXT_REPEAT_PROPERTY); int n = static_cast(property.toUInt(&ok)); if (ok) { data.nextRepeat = n; } } QString property = alarm->customProperty(KACalendar::APPNAME, KAEventPrivate::FLAGS_PROPERTY); const QStringList flags = property.split(KAEventPrivate::SC, QString::SkipEmptyParts); switch (alarm->type()) { case Alarm::Procedure: data.action = KAAlarm::COMMAND; data.cleanText = alarm->programFile(); data.commandScript = data.cleanText.isEmpty(); // blank command indicates a script if (!alarm->programArguments().isEmpty()) { if (!data.commandScript) { data.cleanText += QLatin1Char(' '); } data.cleanText += alarm->programArguments(); } data.extraActionOptions = {}; if (flags.contains(KAEventPrivate::EXEC_ON_DEFERRAL_FLAG)) { data.extraActionOptions |= KAEvent::ExecPreActOnDeferral; } if (flags.contains(KAEventPrivate::CANCEL_ON_ERROR_FLAG)) { data.extraActionOptions |= KAEvent::CancelOnPreActError; } if (flags.contains(KAEventPrivate::DONT_SHOW_ERROR_FLAG)) { data.extraActionOptions |= KAEvent::DontShowPreActError; } if (!cmdDisplay) { break; } // fall through to Display Q_FALLTHROUGH(); case Alarm::Display: { if (alarm->type() == Alarm::Display) { data.action = KAAlarm::MESSAGE; data.cleanText = AlarmText::fromCalendarText(alarm->text(), data.isEmailText); } const QString property = alarm->customProperty(KACalendar::APPNAME, KAEventPrivate::FONT_COLOUR_PROPERTY); const QStringList list = property.split(QLatin1Char(';'), QString::KeepEmptyParts); data.bgColour = QColor(255, 255, 255); // white data.fgColour = QColor(0, 0, 0); // black const int n = list.count(); if (n > 0) { if (!list[0].isEmpty()) { QColor c(list[0]); if (c.isValid()) { data.bgColour = c; } } if (n > 1 && !list[1].isEmpty()) { QColor c(list[1]); if (c.isValid()) { data.fgColour = c; } } } data.defaultFont = (n <= 2 || list[2].isEmpty()); if (!data.defaultFont) { data.font.fromString(list[2]); } break; } case Alarm::Email: { data.action = KAAlarm::EMAIL; data.cleanText = alarm->mailText(); const int i = flags.indexOf(KAEventPrivate::EMAIL_ID_FLAG); data.emailFromId = (i >= 0 && i + 1 < flags.count()) ? flags[i + 1].toUInt() : 0; break; } case Alarm::Audio: { data.action = KAAlarm::AUDIO; data.cleanText = alarm->audioFile(); data.repeatSoundPause = (alarm->repeatCount() == -2) ? alarm->snoozeTime().asSeconds() : (alarm->repeatCount() == -1) ? 0 : -1; data.soundVolume = -1; data.fadeVolume = -1; data.fadeSeconds = 0; QString property = alarm->customProperty(KACalendar::APPNAME, KAEventPrivate::VOLUME_PROPERTY); if (!property.isEmpty()) { bool ok; float fadeVolume; int fadeSecs = 0; const QStringList list = property.split(QLatin1Char(';'), QString::KeepEmptyParts); data.soundVolume = list[0].toFloat(&ok); if (!ok || data.soundVolume > 1.0f) { data.soundVolume = -1; } if (data.soundVolume >= 0 && list.count() >= 3) { fadeVolume = list[1].toFloat(&ok); if (ok) { fadeSecs = static_cast(list[2].toUInt(&ok)); } if (ok && fadeVolume >= 0 && fadeVolume <= 1.0f && fadeSecs > 0) { data.fadeVolume = fadeVolume; data.fadeSeconds = fadeSecs; } } } if (!audioMain) { data.type = AUDIO_ALARM; data.speak = flags.contains(KAEventPrivate::SPEAK_FLAG); return; } break; } case Alarm::Invalid: data.type = INVALID_ALARM; return; } bool atLogin = false; bool reminder = false; bool deferral = false; bool dateDeferral = false; bool repeatSound = false; data.type = MAIN_ALARM; property = alarm->customProperty(KACalendar::APPNAME, KAEventPrivate::TYPE_PROPERTY); const QStringList types = property.split(QLatin1Char(','), QString::SkipEmptyParts); for (int i = 0, end = types.count(); i < end; ++i) { const QString type = types[i]; if (type == KAEventPrivate::AT_LOGIN_TYPE) { atLogin = true; } else if (type == KAEventPrivate::FILE_TYPE && data.action == KAAlarm::MESSAGE) { data.action = KAAlarm::FILE; } else if (type == KAEventPrivate::REMINDER_TYPE) { reminder = true; } else if (type == KAEventPrivate::TIME_DEFERRAL_TYPE) { deferral = true; } else if (type == KAEventPrivate::DATE_DEFERRAL_TYPE) { dateDeferral = deferral = true; } else if (type == KAEventPrivate::DISPLAYING_TYPE) { data.type = DISPLAYING_ALARM; } else if (type == KAEventPrivate::PRE_ACTION_TYPE && data.action == KAAlarm::COMMAND) { data.type = PRE_ACTION_ALARM; } else if (type == KAEventPrivate::POST_ACTION_TYPE && data.action == KAAlarm::COMMAND) { data.type = POST_ACTION_ALARM; } else if (type == KAEventPrivate::SOUND_REPEAT_TYPE && data.action == KAAlarm::AUDIO) { repeatSound = true; if (i + 1 < end) { bool ok; uint n = types[i + 1].toUInt(&ok); if (ok) { data.repeatSoundPause = n; ++i; } } } } if (repeatSound && data.repeatSoundPause < 0) { data.repeatSoundPause = 0; } else if (!repeatSound) { data.repeatSoundPause = -1; } if (reminder) { if (data.type == MAIN_ALARM) { data.type = deferral ? DEFERRED_REMINDER_ALARM : REMINDER_ALARM; data.timedDeferral = (deferral && !dateDeferral); if (data.type == REMINDER_ALARM && flags.contains(KAEventPrivate::HIDDEN_REMINDER_FLAG)) { data.hiddenReminder = true; } } else if (data.type == DISPLAYING_ALARM) data.displayingFlags = dateDeferral ? REMINDER | DATE_DEFERRAL : deferral ? REMINDER | TIME_DEFERRAL : REMINDER; } else if (deferral) { if (data.type == MAIN_ALARM) { data.type = DEFERRED_ALARM; data.timedDeferral = !dateDeferral; } else if (data.type == DISPLAYING_ALARM) { data.displayingFlags = dateDeferral ? DATE_DEFERRAL : TIME_DEFERRAL; } } if (atLogin) { if (data.type == MAIN_ALARM) { data.type = AT_LOGIN_ALARM; } else if (data.type == DISPLAYING_ALARM) { data.displayingFlags = KAEvent::REPEAT_AT_LOGIN; } } //qCDebug(KALARMCAL_LOG)<<"text="<text()<<", time="<time().toString()<<", valid time="<time().isValid(); } QSharedPointer KAEventPrivate::holidays() { if (!mHolidays) mHolidays.reset(new HolidayRegion()); return mHolidays; } inline void KAEventPrivate::set_deferral(DeferType type) { if (type) { if (mDeferral == NO_DEFERRAL) { ++mAlarmCount; } } else { if (mDeferral != NO_DEFERRAL) { --mAlarmCount; } } mDeferral = type; } /****************************************************************************** * Calculate the next trigger times of the alarm. * This should only be called when changes have actually occurred which might * affect the event's trigger times. * mMainTrigger is set to the next scheduled recurrence/sub-repetition, or the * deferral time if a deferral is pending. * mAllTrigger is the same as mMainTrigger, but takes account of reminders. * mMainWorkTrigger is set to the next scheduled recurrence/sub-repetition * which occurs in working hours, if working-time-only is set. * mAllWorkTrigger is the same as mMainWorkTrigger, but takes account of reminders. */ void KAEventPrivate::calcTriggerTimes() const { if (mChangeCount) { return; } #ifdef __GNUC__ #warning May need to set date-only alarms to after start-of-day time in working-time checks #endif holidays(); // initialise mHolidays if necessary bool recurs = (checkRecur() != KARecurrence::NO_RECUR); if ((recurs && mWorkTimeOnly && mWorkTimeOnly != mWorkTimeIndex) || (recurs && mExcludeHolidays && mExcludeHolidayRegion->regionCode() != mHolidays->regionCode())) { // It's a work time alarm, and work days/times have changed, or // it excludes holidays, and the holidays definition has changed. mTriggerChanged = true; } else if (!mTriggerChanged) { return; } mTriggerChanged = false; if (recurs && mWorkTimeOnly) { mWorkTimeOnly = mWorkTimeIndex; // note which work time definition was used in calculation } if (recurs && mExcludeHolidays) { mExcludeHolidayRegion = mHolidays; // note which holiday definition was used in calculation } bool excludeHolidays = mExcludeHolidays && mExcludeHolidayRegion->isValid(); if (mCategory == CalEvent::ARCHIVED || mCategory == CalEvent::TEMPLATE) { // It's a template or archived mAllTrigger = mMainTrigger = mAllWorkTrigger = mMainWorkTrigger = KADateTime(); } else if (mDeferral == NORMAL_DEFERRAL) { // For a deferred alarm, working time setting is ignored mAllTrigger = mMainTrigger = mAllWorkTrigger = mMainWorkTrigger = mDeferralTime; } else { mMainTrigger = mainDateTime(true); // next recurrence or sub-repetition mAllTrigger = (mDeferral == REMINDER_DEFERRAL) ? mDeferralTime : (mReminderActive != ACTIVE_REMINDER) ? mMainTrigger : (mReminderMinutes < 0) ? mReminderAfterTime : mMainTrigger.addMins(-mReminderMinutes); // It's not deferred. // If only-during-working-time is set and it recurs, it won't actually trigger // unless it falls during working hours. if ((!mWorkTimeOnly && !excludeHolidays) || !recurs || isWorkingTime(mMainTrigger.kDateTime())) { // It only occurs once, or it complies with any working hours/holiday // restrictions. mMainWorkTrigger = mMainTrigger; mAllWorkTrigger = mAllTrigger; } else if (mWorkTimeOnly) { // The alarm is restricted to working hours. // Finding the next occurrence during working hours can sometimes take a long time, // so mark the next actual trigger as invalid until the calculation completes. // Note that reminders are only triggered if the main alarm is during working time. if (!excludeHolidays) { // There are no holiday restrictions. calcNextWorkingTime(mMainTrigger); } else if (mHolidays->isValid()) { // Holidays are excluded. DateTime nextTrigger = mMainTrigger; KADateTime kdt; for (int i = 0; i < 20; ++i) { calcNextWorkingTime(nextTrigger); if (!mHolidays->isHoliday(mMainWorkTrigger.date())) { return; // found a non-holiday occurrence } kdt = mMainWorkTrigger.effectiveKDateTime(); kdt.setTime(QTime(23, 59, 59)); const KAEvent::OccurType type = nextOccurrence(kdt, nextTrigger, KAEvent::RETURN_REPETITION); if (!nextTrigger.isValid()) { break; } if (isWorkingTime(nextTrigger.kDateTime())) { const int reminder = (mReminderMinutes > 0) ? mReminderMinutes : 0; // only interested in reminders BEFORE the alarm mMainWorkTrigger = nextTrigger; mAllWorkTrigger = (type & KAEvent::OCCURRENCE_REPEAT) ? mMainWorkTrigger : mMainWorkTrigger.addMins(-reminder); return; // found a non-holiday occurrence } } mMainWorkTrigger = mAllWorkTrigger = DateTime(); } } else if (excludeHolidays && mHolidays->isValid()) { // Holidays are excluded. DateTime nextTrigger = mMainTrigger; KADateTime kdt; for (int i = 0; i < 20; ++i) { kdt = nextTrigger.effectiveKDateTime(); kdt.setTime(QTime(23, 59, 59)); const KAEvent::OccurType type = nextOccurrence(kdt, nextTrigger, KAEvent::RETURN_REPETITION); if (!nextTrigger.isValid()) { break; } if (!mHolidays->isHoliday(nextTrigger.date())) { const int reminder = (mReminderMinutes > 0) ? mReminderMinutes : 0; // only interested in reminders BEFORE the alarm mMainWorkTrigger = nextTrigger; mAllWorkTrigger = (type & KAEvent::OCCURRENCE_REPEAT) ? mMainWorkTrigger : mMainWorkTrigger.addMins(-reminder); return; // found a non-holiday occurrence } } mMainWorkTrigger = mAllWorkTrigger = DateTime(); } } } /****************************************************************************** * Return the time of the next scheduled occurrence of the event during working * hours, for an alarm which is restricted to working hours. * On entry, 'nextTrigger' = the next recurrence or repetition (as returned by * mainDateTime(true) ). */ void KAEventPrivate::calcNextWorkingTime(const DateTime &nextTrigger) const { qCDebug(KALARMCAL_LOG) << "next=" << nextTrigger.kDateTime().toString(QStringLiteral("%Y-%m-%d %H:%M")); mMainWorkTrigger = mAllWorkTrigger = DateTime(); for (int i = 0; ; ++i) { if (i >= 7) { return; // no working days are defined } if (mWorkDays.testBit(i)) { break; } } const KARecurrence::Type recurType = checkRecur(); KADateTime kdt = nextTrigger.effectiveKDateTime(); const int reminder = (mReminderMinutes > 0) ? mReminderMinutes : 0; // only interested in reminders BEFORE the alarm // Check if it always falls on the same day(s) of the week. const RecurrenceRule *rrule = mRecurrence->defaultRRuleConst(); if (!rrule) { return; // no recurrence rule! } unsigned allDaysMask = 0x7F; // mask bits for all days of week bool noWorkPos = false; // true if no recurrence day position is working day const QList pos = rrule->byDays(); const int nDayPos = pos.count(); // number of day positions if (nDayPos) { noWorkPos = true; allDaysMask = 0; for (int i = 0; i < nDayPos; ++i) { const int day = pos[i].day() - 1; // Monday = 0 if (mWorkDays.testBit(day)) { noWorkPos = false; // found a working day occurrence } allDaysMask |= 1 << day; } if (noWorkPos && !mRepetition) { return; // never occurs on a working day } } DateTime newdt; if (mStartDateTime.isDateOnly()) { // It's a date-only alarm. // Sub-repetitions also have to be date-only. const int repeatFreq = mRepetition.intervalDays(); const bool weeklyRepeat = mRepetition && !(repeatFreq % 7); const Duration interval = mRecurrence->regularInterval(); if ((!interval.isNull() && !(interval.asDays() % 7)) || nDayPos == 1) { // It recurs on the same day each week if (!mRepetition || weeklyRepeat) { return; // any repetitions are also weekly } // It's a weekly recurrence with a non-weekly sub-repetition. // Check one cycle of repetitions for the next one that lands // on a working day. KADateTime dt(nextTrigger.kDateTime().addDays(1)); dt.setTime(QTime(0, 0, 0)); previousOccurrence(dt, newdt, false); if (!newdt.isValid()) { return; // this should never happen } kdt = newdt.effectiveKDateTime(); const int day = kdt.date().dayOfWeek() - 1; // Monday = 0 for (int repeatNum = mNextRepeat + 1; ; ++repeatNum) { if (repeatNum > mRepetition.count()) { repeatNum = 0; } if (repeatNum == mNextRepeat) { break; } if (!repeatNum) { nextOccurrence(newdt.kDateTime(), newdt, KAEvent::IGNORE_REPETITION); if (mWorkDays.testBit(day)) { mMainWorkTrigger = newdt; mAllWorkTrigger = mMainWorkTrigger.addMins(-reminder); return; } kdt = newdt.effectiveKDateTime(); } else { const int inc = repeatFreq * repeatNum; if (mWorkDays.testBit((day + inc) % 7)) { kdt = kdt.addDays(inc); kdt.setDateOnly(true); mMainWorkTrigger = mAllWorkTrigger = kdt; return; } } } return; } if (!mRepetition || weeklyRepeat) { // It's a date-only alarm with either no sub-repetition or a // sub-repetition which always falls on the same day of the week // as the recurrence (if any). unsigned days = 0; for (; ;) { kdt.setTime(QTime(23, 59, 59)); nextOccurrence(kdt, newdt, KAEvent::IGNORE_REPETITION); if (!newdt.isValid()) { return; } kdt = newdt.effectiveKDateTime(); const int day = kdt.date().dayOfWeek() - 1; if (mWorkDays.testBit(day)) { break; // found a working day occurrence } // Prevent indefinite looping (which should never happen anyway) if ((days & allDaysMask) == allDaysMask) { return; // found a recurrence on every possible day of the week!?! } days |= 1 << day; } kdt.setDateOnly(true); mMainWorkTrigger = kdt; mAllWorkTrigger = kdt.addSecs(-60 * reminder); return; } // It's a date-only alarm which recurs on different days of the week, // as does the sub-repetition. // Find the previous recurrence (as opposed to sub-repetition) unsigned days = 1 << (kdt.date().dayOfWeek() - 1); KADateTime dt(nextTrigger.kDateTime().addDays(1)); dt.setTime(QTime(0, 0, 0)); previousOccurrence(dt, newdt, false); if (!newdt.isValid()) { return; // this should never happen } kdt = newdt.effectiveKDateTime(); int day = kdt.date().dayOfWeek() - 1; // Monday = 0 for (int repeatNum = mNextRepeat; ; repeatNum = 0) { while (++repeatNum <= mRepetition.count()) { const int inc = repeatFreq * repeatNum; if (mWorkDays.testBit((day + inc) % 7)) { kdt = kdt.addDays(inc); kdt.setDateOnly(true); mMainWorkTrigger = mAllWorkTrigger = kdt; return; } if ((days & allDaysMask) == allDaysMask) { return; // found an occurrence on every possible day of the week!?! } days |= 1 << day; } nextOccurrence(kdt, newdt, KAEvent::IGNORE_REPETITION); if (!newdt.isValid()) { return; } kdt = newdt.effectiveKDateTime(); day = kdt.date().dayOfWeek() - 1; if (mWorkDays.testBit(day)) { kdt.setDateOnly(true); mMainWorkTrigger = kdt; mAllWorkTrigger = kdt.addSecs(-60 * reminder); return; } if ((days & allDaysMask) == allDaysMask) { return; // found an occurrence on every possible day of the week!?! } days |= 1 << day; } return; } // It's a date-time alarm. /* Check whether the recurrence or sub-repetition occurs at the same time * every day. Note that because of seasonal time changes, a recurrence * defined in terms of minutes will vary its time of day even if its value * is a multiple of a day (24*60 minutes). Sub-repetitions are considered * to repeat at the same time of day regardless of time changes if they * are multiples of a day, which doesn't strictly conform to the iCalendar * format because this only allows their interval to be recorded in seconds. */ const bool recurTimeVaries = (recurType == KARecurrence::MINUTELY); const bool repeatTimeVaries = (mRepetition && !mRepetition.isDaily()); if (!recurTimeVaries && !repeatTimeVaries) { // The alarm always occurs at the same time of day. // Check whether it can ever occur during working hours. if (!mayOccurDailyDuringWork(kdt)) { return; // never occurs during working hours } // Find the next working day it occurs on bool repetition = false; unsigned days = 0; for (; ;) { KAEvent::OccurType type = nextOccurrence(kdt, newdt, KAEvent::RETURN_REPETITION); if (!newdt.isValid()) { return; } repetition = (type & KAEvent::OCCURRENCE_REPEAT); kdt = newdt.effectiveKDateTime(); const int day = kdt.date().dayOfWeek() - 1; if (mWorkDays.testBit(day)) { break; // found a working day occurrence } // Prevent indefinite looping (which should never happen anyway) if (!repetition) { if ((days & allDaysMask) == allDaysMask) { return; // found a recurrence on every possible day of the week!?! } days |= 1 << day; } } mMainWorkTrigger = nextTrigger; mMainWorkTrigger.setDate(kdt.date()); mAllWorkTrigger = repetition ? mMainWorkTrigger : mMainWorkTrigger.addMins(-reminder); return; } // The alarm occurs at different times of day. // We may need to check for a full annual cycle of seasonal time changes, in // case it only occurs during working hours after a time change. const QTimeZone tz = kdt.timeZone(); // Get time zone transitions for the next 10 years. QDateTime endTransitionsTime = QDateTime::currentDateTimeUtc().addYears(10); QTimeZone::OffsetDataList tzTransitions = tz.transitions(mStartDateTime.qDateTime(), endTransitionsTime); if (recurTimeVaries) { /* The alarm recurs at regular clock intervals, at different times of day. * Note that for this type of recurrence, it's necessary to avoid the * performance overhead of Recurrence class calls since these can in the * worst case cause the program to hang for a significant length of time. * In this case, we can calculate the next recurrence by simply adding the * recurrence interval, since KAlarm offers no facility to regularly miss * recurrences. (But exception dates/times need to be taken into account.) */ KADateTime kdtRecur; int repeatFreq = 0; int repeatNum = 0; if (mRepetition) { // It's a repetition inside a recurrence, each of which occurs // at different times of day (bearing in mind that the repetition // may occur at daily intervals after each recurrence). // Find the previous recurrence (as opposed to sub-repetition) repeatFreq = mRepetition.intervalSeconds(); previousOccurrence(kdt.addSecs(1), newdt, false); if (!newdt.isValid()) { return; // this should never happen } kdtRecur = newdt.effectiveKDateTime(); repeatNum = kdtRecur.secsTo(kdt) / repeatFreq; kdt = kdtRecur.addSecs(repeatNum * repeatFreq); } else { // There is no sub-repetition. // (N.B. Sub-repetitions can't exist without a recurrence.) // Check until the original time wraps round, but ensure that // if there are seasonal time changes, that all other subsequent // time offsets within the next year are checked. // This does not guarantee to find the next working time, // particularly if there are exceptions, but it's a // reasonable try. kdtRecur = kdt; } QTime firstTime = kdtRecur.time(); int firstOffset = kdtRecur.utcOffset(); int currentOffset = firstOffset; int dayRecur = kdtRecur.date().dayOfWeek() - 1; // Monday = 0 int firstDay = dayRecur; QDate finalDate; const bool subdaily = (repeatFreq < 24 * 3600); // int period = mRecurrence->frequency() % (24*60); // it is by definition a MINUTELY recurrence // int limit = (24*60 + period - 1) / period; // number of times until recurrence wraps round int transitionIndex = -1; for (int n = 0; n < 7 * 24 * 60; ++n) { if (mRepetition) { // Check the sub-repetitions for this recurrence for (; ;) { // Find the repeat count to the next start of the working day const int inc = subdaily ? nextWorkRepetition(kdt) : 1; repeatNum += inc; if (repeatNum > mRepetition.count()) { break; } kdt = kdt.addSecs(inc * repeatFreq); const QTime t = kdt.time(); if (t >= mWorkDayStart && t < mWorkDayEnd) { if (mWorkDays.testBit(kdt.date().dayOfWeek() - 1)) { mMainWorkTrigger = mAllWorkTrigger = kdt; return; } } } repeatNum = 0; } nextOccurrence(kdtRecur, newdt, KAEvent::IGNORE_REPETITION); if (!newdt.isValid()) { return; } kdtRecur = newdt.effectiveKDateTime(); dayRecur = kdtRecur.date().dayOfWeek() - 1; // Monday = 0 const QTime t = kdtRecur.time(); if (t >= mWorkDayStart && t < mWorkDayEnd) { if (mWorkDays.testBit(dayRecur)) { mMainWorkTrigger = kdtRecur; mAllWorkTrigger = kdtRecur.addSecs(-60 * reminder); return; } } if (kdtRecur.utcOffset() != currentOffset) { currentOffset = kdtRecur.utcOffset(); } if (t == firstTime && dayRecur == firstDay && currentOffset == firstOffset) { // We've wrapped round to the starting day and time. // If there are seasonal time changes, check for up // to the next year in other time offsets in case the // alarm occurs inside working hours then. if (!finalDate.isValid()) { finalDate = kdtRecur.date(); } const int i = KAEventPrivate::transitionIndex(kdtRecur.toUtc().qDateTime(), tzTransitions); if (i < 0) { return; } if (i > transitionIndex) { transitionIndex = i; } if (++transitionIndex >= tzTransitions.count()) { return; } previousOccurrence(KADateTime(tzTransitions[transitionIndex].atUtc), newdt, KAEvent::IGNORE_REPETITION); kdtRecur = newdt.effectiveKDateTime(); if (finalDate.daysTo(kdtRecur.date()) > 365) { return; } firstTime = kdtRecur.time(); firstOffset = kdtRecur.utcOffset(); currentOffset = firstOffset; firstDay = kdtRecur.date().dayOfWeek() - 1; } kdt = kdtRecur; } //qCDebug(KALARMCAL_LOG)<<"-----exit loop: count="<= mWorkDayStart && kdtRecur.time() < mWorkDayEnd); // Use the previous recurrence as a base for checking whether // our tests have wrapped round to the same time/day of week. const bool subdaily = (repeatFreq < 24 * 3600); unsigned days = 0; bool checkTimeChangeOnly = false; int transitionIndex = -1; for (int limit = 10; --limit >= 0;) { // Check the next seasonal time change (for an arbitrary 10 times, // even though that might not guarantee the correct result) QDate dateRecur = kdtRecur.date(); int dayRecur = dateRecur.dayOfWeek() - 1; // Monday = 0 int repeatNum = kdtRecur.secsTo(kdt) / repeatFreq; kdt = kdtRecur.addSecs(repeatNum * repeatFreq); // Find the next recurrence, which sets the limit on possible sub-repetitions. // Note that for a monthly recurrence, for example, a sub-repetition could // be defined which is longer than the recurrence interval in short months. // In these cases, the sub-repetition is truncated by the following // recurrence. nextOccurrence(kdtRecur, newdt, KAEvent::IGNORE_REPETITION); KADateTime kdtNextRecur = newdt.effectiveKDateTime(); int repeatsToCheck = mRepetition.count(); int repeatsDuringWork = 0; // 0=unknown, 1=does, -1=never for (; ;) { // Check the sub-repetitions for this recurrence if (repeatsDuringWork >= 0) { for (; ;) { // Find the repeat count to the next start of the working day int inc = subdaily ? nextWorkRepetition(kdt) : 1; repeatNum += inc; const bool pastEnd = (repeatNum > mRepetition.count()); if (pastEnd) { inc -= repeatNum - mRepetition.count(); } repeatsToCheck -= inc; kdt = kdt.addSecs(inc * repeatFreq); if (kdtNextRecur.isValid() && kdt >= kdtNextRecur) { // This sub-repetition is past the next recurrence, // so start the check again from the next recurrence. repeatsToCheck = mRepetition.count(); break; } if (pastEnd) { break; } const QTime t = kdt.time(); if (t >= mWorkDayStart && t < mWorkDayEnd) { if (mWorkDays.testBit(kdt.date().dayOfWeek() - 1)) { mMainWorkTrigger = mAllWorkTrigger = kdt; return; } repeatsDuringWork = 1; } else if (!repeatsDuringWork && repeatsToCheck <= 0) { // Sub-repetitions never occur during working hours repeatsDuringWork = -1; break; } } } repeatNum = 0; if (repeatsDuringWork < 0 && !recurDuringWork) { break; // it never occurs during working hours } // Check the next recurrence if (!kdtNextRecur.isValid()) { return; } if (checkTimeChangeOnly || (days & allDaysMask) == allDaysMask) { break; // found a recurrence on every possible day of the week!?! } kdtRecur = kdtNextRecur; nextOccurrence(kdtRecur, newdt, KAEvent::IGNORE_REPETITION); kdtNextRecur = newdt.effectiveKDateTime(); dateRecur = kdtRecur.date(); dayRecur = dateRecur.dayOfWeek() - 1; if (recurDuringWork && mWorkDays.testBit(dayRecur)) { mMainWorkTrigger = kdtRecur; mAllWorkTrigger = kdtRecur.addSecs(-60 * reminder); return; } days |= 1 << dayRecur; kdt = kdtRecur; } // Find the next recurrence before a seasonal time change, // and ensure the time change is after the last one processed. checkTimeChangeOnly = true; const int i = KAEventPrivate::transitionIndex(kdtRecur.toUtc().qDateTime(), tzTransitions); if (i < 0) { return; } if (i > transitionIndex) { transitionIndex = i; } if (++transitionIndex >= tzTransitions.count()) { return; } kdt = KADateTime(tzTransitions[transitionIndex].atUtc); previousOccurrence(kdt, newdt, KAEvent::IGNORE_REPETITION); kdtRecur = newdt.effectiveKDateTime(); } return; // not found - give up } } /****************************************************************************** * Find the repeat count to the next start of a working day. * This allows for possible daylight saving time changes during the repetition. * Use for repetitions which occur at different times of day. */ int KAEventPrivate::nextWorkRepetition(const KADateTime &pre) const { KADateTime nextWork(pre); if (pre.time() < mWorkDayStart) { nextWork.setTime(mWorkDayStart); } else { const int preDay = pre.date().dayOfWeek() - 1; // Monday = 0 for (int n = 1; ; ++n) { if (n >= 7) { return mRepetition.count() + 1; // should never happen } if (mWorkDays.testBit((preDay + n) % 7)) { nextWork = nextWork.addDays(n); nextWork.setTime(mWorkDayStart); break; } } } return (pre.secsTo(nextWork) - 1) / mRepetition.intervalSeconds() + 1; } /****************************************************************************** * Check whether an alarm which recurs at the same time of day can possibly * occur during working hours. * This does not determine whether it actually does, but rather whether it could * potentially given enough repetitions. * Reply = false if it can never occur during working hours, true if it might. */ bool KAEventPrivate::mayOccurDailyDuringWork(const KADateTime &kdt) const { if (!kdt.isDateOnly() && (kdt.time() < mWorkDayStart || kdt.time() >= mWorkDayEnd)) { return false; // its time is outside working hours } // Check if it always occurs on the same day of the week const Duration interval = mRecurrence->regularInterval(); if (!interval.isNull() && interval.isDaily() && !(interval.asDays() % 7)) { // It recurs weekly if (!mRepetition || (mRepetition.isDaily() && !(mRepetition.intervalDays() % 7))) { return false; // any repetitions are also weekly } // Repetitions are daily. Check if any occur on working days // by checking the first recurrence and up to 6 repetitions. int day = mRecurrence->startDateTime().date().dayOfWeek() - 1; // Monday = 0 const int repeatDays = mRepetition.intervalDays(); const int maxRepeat = (mRepetition.count() < 6) ? mRepetition.count() : 6; for (int i = 0; !mWorkDays.testBit(day); ++i, day = (day + repeatDays) % 7) { if (i >= maxRepeat) { return false; // no working day occurrences } } } return true; } /****************************************************************************** * Set the specified alarm to be an audio alarm with the given file name. */ void KAEventPrivate::setAudioAlarm(const Alarm::Ptr &alarm) const { alarm->setAudioAlarm(mAudioFile); // empty for a beep or for speaking if (mSoundVolume >= 0) alarm->setCustomProperty(KACalendar::APPNAME, VOLUME_PROPERTY, QStringLiteral("%1;%2;%3").arg(QString::number(mSoundVolume, 'f', 2), QString::number(mFadeVolume, 'f', 2), QString::number(mFadeSeconds))); } /****************************************************************************** * Get the date/time of the next recurrence of the event, after the specified * date/time. * 'result' = date/time of next occurrence, or invalid date/time if none. */ KAEvent::OccurType KAEventPrivate::nextRecurrence(const KADateTime &preDateTime, DateTime &result) const { const KADateTime recurStart = mRecurrence->startDateTime(); KADateTime pre = preDateTime.toTimeSpec(mStartDateTime.timeSpec()); if (mStartDateTime.isDateOnly() && !pre.isDateOnly() && pre.time() < DateTime::startOfDay()) { pre = pre.addDays(-1); // today's recurrence (if today recurs) is still to come pre.setTime(DateTime::startOfDay()); } const KADateTime dt = mRecurrence->getNextDateTime(pre); result = dt; result.setDateOnly(mStartDateTime.isDateOnly()); if (!dt.isValid()) { return KAEvent::NO_OCCURRENCE; } if (dt == recurStart) { return KAEvent::FIRST_OR_ONLY_OCCURRENCE; } if (mRecurrence->duration() >= 0 && dt == mRecurrence->endDateTime()) { return KAEvent::LAST_RECURRENCE; } return result.isDateOnly() ? KAEvent::RECURRENCE_DATE : KAEvent::RECURRENCE_DATE_TIME; } /****************************************************************************** * Validate the event's recurrence data, correcting any inconsistencies (which * should never occur!). * Reply = recurrence period type. */ KARecurrence::Type KAEventPrivate::checkRecur() const { if (mRecurrence) { KARecurrence::Type type = mRecurrence->type(); switch (type) { case KARecurrence::MINUTELY: // hourly case KARecurrence::DAILY: // daily case KARecurrence::WEEKLY: // weekly on multiple days of week case KARecurrence::MONTHLY_DAY: // monthly on multiple dates in month case KARecurrence::MONTHLY_POS: // monthly on multiple nth day of week case KARecurrence::ANNUAL_DATE: // annually on multiple months (day of month = start date) case KARecurrence::ANNUAL_POS: // annually on multiple nth day of week in multiple months return type; default: if (mRecurrence) { const_cast(this)->clearRecur(); // this shouldn't ever be necessary!! } break; } } if (mRepetition) { // can't have a repetition without a recurrence const_cast(this)->clearRecur(); // this shouldn't ever be necessary!! } return KARecurrence::NO_RECUR; } /****************************************************************************** * If the calendar was written by a previous version of KAlarm, do any * necessary format conversions on the events to ensure that when the calendar * is saved, no information is lost or corrupted. * Reply = true if any conversions were done. */ bool KAEvent::convertKCalEvents(const Calendar::Ptr &calendar, int calendarVersion) { // KAlarm pre-0.9 codes held in the alarm's DESCRIPTION property static const QChar SEPARATOR = QLatin1Char(';'); static const QChar LATE_CANCEL_CODE = QLatin1Char('C'); static const QChar AT_LOGIN_CODE = QLatin1Char('L'); // subsidiary alarm at every login static const QChar DEFERRAL_CODE = QLatin1Char('D'); // extra deferred alarm static const QString TEXT_PREFIX = QStringLiteral("TEXT:"); static const QString FILE_PREFIX = QStringLiteral("FILE:"); static const QString COMMAND_PREFIX = QStringLiteral("CMD:"); // KAlarm pre-0.9.2 codes held in the event's CATEGORY property static const QString BEEP_CATEGORY = QStringLiteral("BEEP"); // KAlarm pre-1.1.1 LATECANCEL category with no parameter static const QString LATE_CANCEL_CAT = QStringLiteral("LATECANCEL"); // KAlarm pre-1.3.0 TMPLDEFTIME category with no parameter static const QString TEMPL_DEF_TIME_CAT = QStringLiteral("TMPLDEFTIME"); // KAlarm pre-1.3.1 XTERM category static const QString EXEC_IN_XTERM_CAT = QStringLiteral("XTERM"); // KAlarm pre-1.9.0 categories static const QString DATE_ONLY_CATEGORY = QStringLiteral("DATE"); static const QString EMAIL_BCC_CATEGORY = QStringLiteral("BCC"); static const QString CONFIRM_ACK_CATEGORY = QStringLiteral("ACKCONF"); static const QString KORGANIZER_CATEGORY = QStringLiteral("KORG"); static const QString DEFER_CATEGORY = QStringLiteral("DEFER;"); static const QString ARCHIVE_CATEGORY = QStringLiteral("SAVE"); static const QString ARCHIVE_CATEGORIES = QStringLiteral("SAVE:"); static const QString LATE_CANCEL_CATEGORY = QStringLiteral("LATECANCEL;"); static const QString AUTO_CLOSE_CATEGORY = QStringLiteral("LATECLOSE;"); static const QString TEMPL_AFTER_TIME_CATEGORY = QStringLiteral("TMPLAFTTIME;"); static const QString KMAIL_SERNUM_CATEGORY = QStringLiteral("KMAIL:"); static const QString LOG_CATEGORY = QStringLiteral("LOG:"); // KAlarm pre-1.5.0/1.9.9 properties static const QByteArray KMAIL_ID_PROPERTY("KMAILID"); // X-KDE-KALARM-KMAILID property // KAlarm pre-2.6.0 properties static const QByteArray ARCHIVE_PROPERTY("ARCHIVE"); // X-KDE-KALARM-ARCHIVE property static const QString ARCHIVE_REMINDER_ONCE_TYPE = QStringLiteral("ONCE"); static const QString REMINDER_ONCE_TYPE = QStringLiteral("REMINDER_ONCE"); static const QByteArray EMAIL_ID_PROPERTY("EMAILID"); // X-KDE-KALARM-EMAILID property static const QByteArray SPEAK_PROPERTY("SPEAK"); // X-KDE-KALARM-SPEAK property static const QByteArray CANCEL_ON_ERROR_PROPERTY("ERRCANCEL");// X-KDE-KALARM-ERRCANCEL property static const QByteArray DONT_SHOW_ERROR_PROPERTY("ERRNOSHOW");// X-KDE-KALARM-ERRNOSHOW property bool adjustSummerTime = false; if (calendarVersion == -Version(0, 5, 7)) { // The calendar file was written by the KDE 3.0.0 version of KAlarm 0.5.7. // Summer time was ignored when converting to UTC. calendarVersion = -calendarVersion; adjustSummerTime = true; } if (calendarVersion >= currentCalendarVersion()) { return false; } qCDebug(KALARMCAL_LOG) << "Adjusting version" << calendarVersion; const bool pre_0_7 = (calendarVersion < Version(0, 7, 0)); const bool pre_0_9 = (calendarVersion < Version(0, 9, 0)); const bool pre_0_9_2 = (calendarVersion < Version(0, 9, 2)); const bool pre_1_1_1 = (calendarVersion < Version(1, 1, 1)); const bool pre_1_2_1 = (calendarVersion < Version(1, 2, 1)); const bool pre_1_3_0 = (calendarVersion < Version(1, 3, 0)); const bool pre_1_3_1 = (calendarVersion < Version(1, 3, 1)); const bool pre_1_4_14 = (calendarVersion < Version(1, 4, 14)); const bool pre_1_5_0 = (calendarVersion < Version(1, 5, 0)); const bool pre_1_9_0 = (calendarVersion < Version(1, 9, 0)); const bool pre_1_9_2 = (calendarVersion < Version(1, 9, 2)); const bool pre_1_9_7 = (calendarVersion < Version(1, 9, 7)); const bool pre_1_9_9 = (calendarVersion < Version(1, 9, 9)); const bool pre_1_9_10 = (calendarVersion < Version(1, 9, 10)); const bool pre_2_2_9 = (calendarVersion < Version(2, 2, 9)); const bool pre_2_3_0 = (calendarVersion < Version(2, 3, 0)); const bool pre_2_3_2 = (calendarVersion < Version(2, 3, 2)); const bool pre_2_7_0 = (calendarVersion < Version(2, 7, 0)); Q_ASSERT(currentCalendarVersion() == Version(2, 7, 0)); const QTimeZone localZone = QTimeZone::systemTimeZone(); bool converted = false; const Event::List events = calendar->rawEvents(); for (int ei = 0, eend = events.count(); ei < eend; ++ei) { Event::Ptr event = events[ei]; const Alarm::List alarms = event->alarms(); if (alarms.isEmpty()) { continue; // KAlarm isn't interested in events without alarms } event->startUpdates(); // prevent multiple update notifications const bool readOnly = event->isReadOnly(); if (readOnly) { event->setReadOnly(false); } QStringList cats = event->categories(); bool addLateCancel = false; QStringList flags; if (pre_0_7 && event->allDay()) { // It's a KAlarm pre-0.7 calendar file. // Ensure that when the calendar is saved, the alarm time isn't lost. event->setAllDay(false); } if (pre_0_9) { /* * It's a KAlarm pre-0.9 calendar file. * All alarms were of type DISPLAY. Instead of the X-KDE-KALARM-TYPE * alarm property, characteristics were stored as a prefix to the * alarm DESCRIPTION property, as follows: * SEQNO;[FLAGS];TYPE:TEXT * where * SEQNO = sequence number of alarm within the event * FLAGS = C for late-cancel, L for repeat-at-login, D for deferral * TYPE = TEXT or FILE or CMD * TEXT = message text, file name/URL or command */ for (int ai = 0, aend = alarms.count(); ai < aend; ++ai) { Alarm::Ptr alarm = alarms[ai]; bool atLogin = false; bool deferral = false; bool lateCancel = false; KAAlarm::Action action = KAAlarm::MESSAGE; const QString txt = alarm->text(); const int length = txt.length(); int i = 0; if (txt[0].isDigit()) { while (++i < length && txt[i].isDigit()) ; if (i < length && txt[i++] == SEPARATOR) { while (i < length) { const QChar ch = txt[i++]; if (ch == SEPARATOR) { break; } if (ch == LATE_CANCEL_CODE) { lateCancel = true; } else if (ch == AT_LOGIN_CODE) { atLogin = true; } else if (ch == DEFERRAL_CODE) { deferral = true; } } } else { i = 0; // invalid prefix } } if (txt.indexOf(TEXT_PREFIX, i) == i) { i += TEXT_PREFIX.length(); } else if (txt.indexOf(FILE_PREFIX, i) == i) { action = KAAlarm::FILE; i += FILE_PREFIX.length(); } else if (txt.indexOf(COMMAND_PREFIX, i) == i) { action = KAAlarm::COMMAND; i += COMMAND_PREFIX.length(); } else { i = 0; } const QString altxt = txt.mid(i); QStringList types; switch (action) { case KAAlarm::FILE: types += KAEventPrivate::FILE_TYPE; // fall through to MESSAGE Q_FALLTHROUGH(); case KAAlarm::MESSAGE: alarm->setDisplayAlarm(altxt); break; case KAAlarm::COMMAND: setProcedureAlarm(alarm, altxt); break; case KAAlarm::EMAIL: // email alarms were introduced in KAlarm 0.9 case KAAlarm::AUDIO: // audio alarms (with no display) were introduced in KAlarm 2.3.2 break; } if (atLogin) { types += KAEventPrivate::AT_LOGIN_TYPE; lateCancel = false; } else if (deferral) { types += KAEventPrivate::TIME_DEFERRAL_TYPE; } if (lateCancel) { addLateCancel = true; } if (types.count() > 0) { alarm->setCustomProperty(KACalendar::APPNAME, KAEventPrivate::TYPE_PROPERTY, types.join(QStringLiteral(","))); } if (pre_0_7 && alarm->repeatCount() > 0 && alarm->snoozeTime().value() > 0) { // It's a KAlarm pre-0.7 calendar file. // Minutely recurrences were stored differently. Recurrence *recur = event->recurrence(); if (recur && recur->recurs()) { recur->setMinutely(alarm->snoozeTime().asSeconds() / 60); recur->setDuration(alarm->repeatCount() + 1); alarm->setRepeatCount(0); alarm->setSnoozeTime(0); } } if (adjustSummerTime) { // The calendar file was written by the KDE 3.0.0 version of KAlarm 0.5.7. // Summer time was ignored when converting to UTC. KADateTime dt(alarm->time()); const time_t t = dt.toTime_t(); const struct tm *dtm = localtime(&t); if (dtm->tm_isdst) { dt = dt.addSecs(-3600); alarm->setTime(dt.qDateTime()); } } } } if (pre_0_9_2) { /* * It's a KAlarm pre-0.9.2 calendar file. * For the archive calendar, set the CREATED time to the DTEND value. * Convert date-only DTSTART to date/time, and add category "DATE". * Set the DTEND time to the DTSTART time. * Convert all alarm times to DTSTART offsets. * For display alarms, convert the first unlabelled category to an * X-KDE-KALARM-FONTCOLOUR property. * Convert BEEP category into an audio alarm with no audio file. */ if (CalEvent::status(event) == CalEvent::ARCHIVED) { event->setCreated(event->dtEnd()); } QDateTime start = event->dtStart(); if (event->allDay()) { start.setTime(QTime(0, 0)); flags += KAEventPrivate::DATE_ONLY_FLAG; } event->setDtEnd(QDateTime()); for (int ai = 0, aend = alarms.count(); ai < aend; ++ai) { Alarm::Ptr alarm = alarms[ai]; alarm->setStartOffset(start.secsTo(alarm->time())); } if (!cats.isEmpty()) { for (int ai = 0, aend = alarms.count(); ai < aend; ++ai) { Alarm::Ptr alarm = alarms[ai]; if (alarm->type() == Alarm::Display) alarm->setCustomProperty(KACalendar::APPNAME, KAEventPrivate::FONT_COLOUR_PROPERTY, QStringLiteral("%1;;").arg(cats.at(0))); } cats.removeAt(0); } for (int i = 0, end = cats.count(); i < end; ++i) { if (cats.at(i) == BEEP_CATEGORY) { cats.removeAt(i); Alarm::Ptr alarm = event->newAlarm(); alarm->setEnabled(true); alarm->setAudioAlarm(); QDateTime dt = event->dtStart(); // default // Parse and order the alarms to know which one's date/time to use KAEventPrivate::AlarmMap alarmMap; KAEventPrivate::readAlarms(event, &alarmMap); KAEventPrivate::AlarmMap::ConstIterator it = alarmMap.constBegin(); if (it != alarmMap.constEnd()) { dt = it.value().alarm->time(); break; } alarm->setStartOffset(start.secsTo(dt)); break; } } } if (pre_1_1_1) { /* * It's a KAlarm pre-1.1.1 calendar file. * Convert simple LATECANCEL category to LATECANCEL:n where n = minutes late. */ int i; while ((i = cats.indexOf(LATE_CANCEL_CAT)) >= 0) { cats.removeAt(i); addLateCancel = true; } } if (pre_1_2_1) { /* * It's a KAlarm pre-1.2.1 calendar file. * Convert email display alarms from translated to untranslated header prefixes. */ for (int ai = 0, aend = alarms.count(); ai < aend; ++ai) { Alarm::Ptr alarm = alarms[ai]; if (alarm->type() == Alarm::Display) { const QString oldtext = alarm->text(); const QString newtext = AlarmText::toCalendarText(oldtext); if (oldtext != newtext) { alarm->setDisplayAlarm(newtext); } } } } if (pre_1_3_0) { /* * It's a KAlarm pre-1.3.0 calendar file. * Convert simple TMPLDEFTIME category to TMPLAFTTIME:n where n = minutes after. */ int i; while ((i = cats.indexOf(TEMPL_DEF_TIME_CAT)) >= 0) { cats.removeAt(i); (flags += KAEventPrivate::TEMPL_AFTER_TIME_FLAG) += QStringLiteral("0"); } } if (pre_1_3_1) { /* * It's a KAlarm pre-1.3.1 calendar file. * Convert simple XTERM category to LOG:xterm: */ int i; while ((i = cats.indexOf(EXEC_IN_XTERM_CAT)) >= 0) { cats.removeAt(i); event->setCustomProperty(KACalendar::APPNAME, KAEventPrivate::LOG_PROPERTY, KAEventPrivate::xtermURL); } } if (pre_1_9_0) { /* * It's a KAlarm pre-1.9 calendar file. * Add the X-KDE-KALARM-STATUS custom property. * Convert KAlarm categories to custom fields. */ CalEvent::setStatus(event, CalEvent::status(event)); for (int i = 0; i < cats.count();) { const QString cat = cats.at(i); if (cat == DATE_ONLY_CATEGORY) { flags += KAEventPrivate::DATE_ONLY_FLAG; } else if (cat == CONFIRM_ACK_CATEGORY) { flags += KAEventPrivate::CONFIRM_ACK_FLAG; } else if (cat == EMAIL_BCC_CATEGORY) { flags += KAEventPrivate::EMAIL_BCC_FLAG; } else if (cat == KORGANIZER_CATEGORY) { flags += KAEventPrivate::KORGANIZER_FLAG; } else if (cat.startsWith(DEFER_CATEGORY)) { (flags += KAEventPrivate::DEFER_FLAG) += cat.mid(DEFER_CATEGORY.length()); } else if (cat.startsWith(TEMPL_AFTER_TIME_CATEGORY)) { (flags += KAEventPrivate::TEMPL_AFTER_TIME_FLAG) += cat.mid(TEMPL_AFTER_TIME_CATEGORY.length()); } else if (cat.startsWith(LATE_CANCEL_CATEGORY)) { (flags += KAEventPrivate::LATE_CANCEL_FLAG) += cat.mid(LATE_CANCEL_CATEGORY.length()); } else if (cat.startsWith(AUTO_CLOSE_CATEGORY)) { (flags += KAEventPrivate::AUTO_CLOSE_FLAG) += cat.mid(AUTO_CLOSE_CATEGORY.length()); } else if (cat.startsWith(KMAIL_SERNUM_CATEGORY)) { (flags += KAEventPrivate::KMAIL_SERNUM_FLAG) += cat.mid(KMAIL_SERNUM_CATEGORY.length()); } else if (cat == ARCHIVE_CATEGORY) { event->setCustomProperty(KACalendar::APPNAME, ARCHIVE_PROPERTY, QStringLiteral("0")); } else if (cat.startsWith(ARCHIVE_CATEGORIES)) { event->setCustomProperty(KACalendar::APPNAME, ARCHIVE_PROPERTY, cat.mid(ARCHIVE_CATEGORIES.length())); } else if (cat.startsWith(LOG_CATEGORY)) { event->setCustomProperty(KACalendar::APPNAME, KAEventPrivate::LOG_PROPERTY, cat.mid(LOG_CATEGORY.length())); } else { ++i; // Not a KAlarm category, so leave it continue; } cats.removeAt(i); } } if (pre_1_9_2) { /* * It's a KAlarm pre-1.9.2 calendar file. * Convert from clock time to the local system time zone. */ event->shiftTimes(localZone, localZone); converted = true; } if (addLateCancel) { (flags += KAEventPrivate::LATE_CANCEL_FLAG) += QStringLiteral("1"); } if (!flags.isEmpty()) { event->setCustomProperty(KACalendar::APPNAME, KAEventPrivate::FLAGS_PROPERTY, flags.join(KAEventPrivate::SC)); } event->setCategories(cats); if ((pre_1_4_14 || (pre_1_9_7 && !pre_1_9_0)) && event->recurrence() && event->recurrence()->recurs()) { /* * It's a KAlarm pre-1.4.14 or KAlarm 1.9 series pre-1.9.7 calendar file. * For recurring events, convert the main alarm offset to an absolute * time in the X-KDE-KALARM-NEXTRECUR property, and set main alarm * offsets to zero, and convert deferral alarm offsets to be relative to * the next recurrence. */ const QStringList flags = event->customProperty(KACalendar::APPNAME, KAEventPrivate::FLAGS_PROPERTY).split(KAEventPrivate::SC, QString::SkipEmptyParts); const bool dateOnly = flags.contains(KAEventPrivate::DATE_ONLY_FLAG); KADateTime startDateTime(event->dtStart()); if (dateOnly) { startDateTime.setDateOnly(true); } // Convert the main alarm and get the next main trigger time from it KADateTime nextMainDateTime; bool mainExpired = true; for (int i = 0, alend = alarms.count(); i < alend; ++i) { Alarm::Ptr alarm = alarms[i]; if (!alarm->hasStartOffset()) { continue; } // Find whether the alarm triggers at the same time as the main // alarm, in which case its offset needs to be set to 0. The // following trigger with the main alarm: // - Additional audio alarm // - PRE_ACTION_TYPE // - POST_ACTION_TYPE // - DISPLAYING_TYPE bool mainAlarm = true; QString property = alarm->customProperty(KACalendar::APPNAME, KAEventPrivate::TYPE_PROPERTY); const QStringList types = property.split(QLatin1Char(','), QString::SkipEmptyParts); for (int t = 0; t < types.count(); ++t) { QString type = types[t]; if (type == KAEventPrivate::AT_LOGIN_TYPE || type == KAEventPrivate::TIME_DEFERRAL_TYPE || type == KAEventPrivate::DATE_DEFERRAL_TYPE || type == KAEventPrivate::REMINDER_TYPE || type == REMINDER_ONCE_TYPE) { mainAlarm = false; break; } } if (mainAlarm) { if (mainExpired) { // All main alarms are supposed to be at the same time, so // don't readjust the event's time for subsequent main alarms. mainExpired = false; nextMainDateTime = KADateTime(alarm->time()); nextMainDateTime.setDateOnly(dateOnly); nextMainDateTime = nextMainDateTime.toTimeSpec(startDateTime); if (nextMainDateTime != startDateTime) { QDateTime dt = nextMainDateTime.qDateTime(); event->setCustomProperty(KACalendar::APPNAME, KAEventPrivate::NEXT_RECUR_PROPERTY, dt.toString(dateOnly ? QStringLiteral("yyyyMMdd") : QStringLiteral("yyyyMMddThhmmss"))); } } alarm->setStartOffset(0); converted = true; } } int adjustment; if (mainExpired) { // It's an expired recurrence. // Set the alarm offset relative to the first actual occurrence // (taking account of possible exceptions). KADateTime dt(event->recurrence()->getNextDateTime(startDateTime.qDateTime().addDays(-1))); dt.setDateOnly(dateOnly); adjustment = startDateTime.secsTo(dt); } else { adjustment = startDateTime.secsTo(nextMainDateTime); } if (adjustment) { // Convert deferred alarms for (int i = 0, alend = alarms.count(); i < alend; ++i) { Alarm::Ptr alarm = alarms[i]; if (!alarm->hasStartOffset()) { continue; } const QString property = alarm->customProperty(KACalendar::APPNAME, KAEventPrivate::TYPE_PROPERTY); const QStringList types = property.split(QLatin1Char(','), QString::SkipEmptyParts); for (int t = 0; t < types.count(); ++t) { const QString type = types[t]; if (type == KAEventPrivate::TIME_DEFERRAL_TYPE || type == KAEventPrivate::DATE_DEFERRAL_TYPE) { alarm->setStartOffset(alarm->startOffset().asSeconds() - adjustment); converted = true; break; } } } } } if (pre_1_5_0 || (pre_1_9_9 && !pre_1_9_0)) { /* * It's a KAlarm pre-1.5.0 or KAlarm 1.9 series pre-1.9.9 calendar file. * Convert email identity names to uoids. */ for (int i = 0, alend = alarms.count(); i < alend; ++i) { Alarm::Ptr alarm = alarms[i]; const QString name = alarm->customProperty(KACalendar::APPNAME, KMAIL_ID_PROPERTY); if (name.isEmpty()) { continue; } const uint id = Identities::identityUoid(name); if (id) { alarm->setCustomProperty(KACalendar::APPNAME, EMAIL_ID_PROPERTY, QString::number(id)); } alarm->removeCustomProperty(KACalendar::APPNAME, KMAIL_ID_PROPERTY); converted = true; } } if (pre_1_9_10) { /* * It's a KAlarm pre-1.9.10 calendar file. * Convert simple repetitions without a recurrence, to a recurrence. */ if (KAEventPrivate::convertRepetition(event)) { converted = true; } } if (pre_2_2_9 || (pre_2_3_2 && !pre_2_3_0)) { /* * It's a KAlarm pre-2.2.9 or KAlarm 2.3 series pre-2.3.2 calendar file. * Set the time in the calendar for all date-only alarms to 00:00. */ if (KAEventPrivate::convertStartOfDay(event)) { converted = true; } } if (pre_2_7_0) { /* * It's a KAlarm pre-2.7.0 calendar file. * Archive and at-login flags were stored in event's ARCHIVE property when the main alarm had expired. * Reminder parameters were stored in event's ARCHIVE property when no reminder was pending. * Negative reminder periods (i.e. alarm offset > 0) were invalid, so convert to 0. * Now store reminder information in FLAGS property, whether reminder is pending or not. * Move EMAILID, SPEAK, ERRCANCEL and ERRNOSHOW alarm properties into new FLAGS property. */ bool flagsValid = false; QStringList flags; QString reminder; bool reminderOnce = false; const QString prop = event->customProperty(KACalendar::APPNAME, ARCHIVE_PROPERTY); if (!prop.isEmpty()) { // Convert the event's ARCHIVE property to parameters in the FLAGS property flags = event->customProperty(KACalendar::APPNAME, KAEventPrivate::FLAGS_PROPERTY).split(KAEventPrivate::SC, QString::SkipEmptyParts); flags << KAEventPrivate::ARCHIVE_FLAG; flagsValid = true; if (prop != QLatin1String("0")) { // "0" was a dummy parameter if no others were present // It's the archive property containing a reminder time and/or repeat-at-login flag. // This was present when no reminder/at-login alarm was pending. const QStringList list = prop.split(KAEventPrivate::SC, QString::SkipEmptyParts); for (int i = 0; i < list.count(); ++i) { if (list[i] == KAEventPrivate::AT_LOGIN_TYPE) { flags << KAEventPrivate::AT_LOGIN_TYPE; } else if (list[i] == ARCHIVE_REMINDER_ONCE_TYPE) { reminderOnce = true; } else if (!list[i].isEmpty() && !list[i].startsWith(QChar::fromLatin1('-'))) { reminder = list[i]; } } } event->setCustomProperty(KACalendar::APPNAME, KAEventPrivate::FLAGS_PROPERTY, flags.join(KAEventPrivate::SC)); event->removeCustomProperty(KACalendar::APPNAME, ARCHIVE_PROPERTY); } for (int i = 0, alend = alarms.count(); i < alend; ++i) { Alarm::Ptr alarm = alarms[i]; // Convert EMAILID, SPEAK, ERRCANCEL, ERRNOSHOW properties QStringList flags; QString property = alarm->customProperty(KACalendar::APPNAME, EMAIL_ID_PROPERTY); if (!property.isEmpty()) { flags << KAEventPrivate::EMAIL_ID_FLAG << property; alarm->removeCustomProperty(KACalendar::APPNAME, EMAIL_ID_PROPERTY); } if (!alarm->customProperty(KACalendar::APPNAME, SPEAK_PROPERTY).isEmpty()) { flags << KAEventPrivate::SPEAK_FLAG; alarm->removeCustomProperty(KACalendar::APPNAME, SPEAK_PROPERTY); } if (!alarm->customProperty(KACalendar::APPNAME, CANCEL_ON_ERROR_PROPERTY).isEmpty()) { flags << KAEventPrivate::CANCEL_ON_ERROR_FLAG; alarm->removeCustomProperty(KACalendar::APPNAME, CANCEL_ON_ERROR_PROPERTY); } if (!alarm->customProperty(KACalendar::APPNAME, DONT_SHOW_ERROR_PROPERTY).isEmpty()) { flags << KAEventPrivate::DONT_SHOW_ERROR_FLAG; alarm->removeCustomProperty(KACalendar::APPNAME, DONT_SHOW_ERROR_PROPERTY); } if (!flags.isEmpty()) { alarm->setCustomProperty(KACalendar::APPNAME, KAEventPrivate::FLAGS_PROPERTY, flags.join(KAEventPrivate::SC)); } // Invalidate negative reminder periods in alarms if (!alarm->hasStartOffset()) { continue; } property = alarm->customProperty(KACalendar::APPNAME, KAEventPrivate::TYPE_PROPERTY); QStringList types = property.split(QChar::fromLatin1(','), QString::SkipEmptyParts); const int r = types.indexOf(REMINDER_ONCE_TYPE); if (r >= 0) { // Move reminder-once indicator from the alarm to the event's FLAGS property types[r] = KAEventPrivate::REMINDER_TYPE; alarm->setCustomProperty(KACalendar::APPNAME, KAEventPrivate::TYPE_PROPERTY, types.join(QChar::fromLatin1(','))); reminderOnce = true; } if (r >= 0 || types.contains(KAEventPrivate::REMINDER_TYPE)) { // The alarm is a reminder alarm const int offset = alarm->startOffset().asSeconds(); if (offset > 0) { alarm->setStartOffset(0); converted = true; } else if (offset < 0) { reminder = reminderToString(offset / 60); } } } if (!reminder.isEmpty()) { // Write reminder parameters into the event's FLAGS property if (!flagsValid) { flags = event->customProperty(KACalendar::APPNAME, KAEventPrivate::FLAGS_PROPERTY).split(KAEventPrivate::SC, QString::SkipEmptyParts); } if (!flags.contains(KAEventPrivate::REMINDER_TYPE)) { flags += KAEventPrivate::REMINDER_TYPE; if (reminderOnce) { flags += KAEventPrivate::REMINDER_ONCE_FLAG; } flags += reminder; } } } if (readOnly) { event->setReadOnly(true); } event->endUpdates(); // finally issue an update notification } return converted; } /****************************************************************************** * Set the time for a date-only event to 00:00. * Reply = true if the event was updated. */ bool KAEventPrivate::convertStartOfDay(const Event::Ptr &event) { bool changed = false; const QTime midnight(0, 0); const QStringList flags = event->customProperty(KACalendar::APPNAME, KAEventPrivate::FLAGS_PROPERTY).split(KAEventPrivate::SC, QString::SkipEmptyParts); if (flags.contains(KAEventPrivate::DATE_ONLY_FLAG)) { // It's an untimed event, so fix it const QDateTime oldDt = event->dtStart(); const int adjustment = oldDt.time().secsTo(midnight); if (adjustment) { event->setDtStart(QDateTime(oldDt.date(), midnight, oldDt.timeSpec())); int deferralOffset = 0; AlarmMap alarmMap; readAlarms(event, &alarmMap); for (AlarmMap::ConstIterator it = alarmMap.constBegin(); it != alarmMap.constEnd(); ++it) { const AlarmData &data = it.value(); if (!data.alarm->hasStartOffset()) { continue; } if (data.timedDeferral) { // Found a timed deferral alarm, so adjust the offset deferralOffset = data.alarm->startOffset().asSeconds(); const_cast(data.alarm.data())->setStartOffset(deferralOffset - adjustment); } else if (data.type == AUDIO_ALARM && data.alarm->startOffset().asSeconds() == deferralOffset) { // Audio alarm is set for the same time as the above deferral alarm const_cast(data.alarm.data())->setStartOffset(deferralOffset - adjustment); } } changed = true; } } else { // It's a timed event. Fix any untimed alarms. bool foundDeferral = false; int deferralOffset = 0; int newDeferralOffset = 0; DateTime start; const KADateTime nextMainDateTime = readDateTime(event, false, false, start).kDateTime(); AlarmMap alarmMap; readAlarms(event, &alarmMap); for (AlarmMap::ConstIterator it = alarmMap.constBegin(); it != alarmMap.constEnd(); ++it) { const AlarmData &data = it.value(); if (!data.alarm->hasStartOffset()) { continue; } if ((data.type & DEFERRED_ALARM) && !data.timedDeferral) { // Found a date-only deferral alarm, so adjust its time QDateTime altime = data.alarm->startOffset().end(nextMainDateTime.qDateTime()); altime.setTime(midnight); deferralOffset = data.alarm->startOffset().asSeconds(); newDeferralOffset = event->dtStart().secsTo(altime); const_cast(data.alarm.data())->setStartOffset(newDeferralOffset); foundDeferral = true; changed = true; } else if (foundDeferral && data.type == AUDIO_ALARM && data.alarm->startOffset().asSeconds() == deferralOffset) { // Audio alarm is set for the same time as the above deferral alarm const_cast(data.alarm.data())->setStartOffset(newDeferralOffset); changed = true; } } } return changed; } /****************************************************************************** * Convert simple repetitions in an event without a recurrence, to a * recurrence. Repetitions which are an exact multiple of 24 hours are converted * to daily recurrences; else they are converted to minutely recurrences. Note * that daily and minutely recurrences produce different results when they span * a daylight saving time change. * Reply = true if any conversions were done. */ bool KAEventPrivate::convertRepetition(const Event::Ptr &event) { const Alarm::List alarms = event->alarms(); if (alarms.isEmpty()) { return false; } Recurrence *recur = event->recurrence(); // guaranteed to return non-null if (recur->recurs()) { return false; } bool converted = false; const bool readOnly = event->isReadOnly(); for (int ai = 0, aend = alarms.count(); ai < aend; ++ai) { Alarm::Ptr alarm = alarms[ai]; if (alarm->repeatCount() > 0 && alarm->snoozeTime().value() > 0) { if (!converted) { event->startUpdates(); // prevent multiple update notifications if (readOnly) { event->setReadOnly(false); } if ((alarm->snoozeTime().asSeconds() % (24 * 3600)) != 0) { recur->setMinutely(alarm->snoozeTime().asSeconds() / 60); } else { recur->setDaily(alarm->snoozeTime().asDays()); } recur->setDuration(alarm->repeatCount() + 1); converted = true; } alarm->setRepeatCount(0); alarm->setSnoozeTime(0); } } if (converted) { if (readOnly) { event->setReadOnly(true); } event->endUpdates(); // finally issue an update notification } return converted; } /*============================================================================= = Class KAAlarm = Corresponds to a single KCal::Alarm instance. =============================================================================*/ KAAlarm::KAAlarm() : d(new Private) { } KAAlarm::Private::Private() : mType(INVALID_ALARM), mNextRepeat(0), mRepeatAtLogin(false), mDeferred(false) { } KAAlarm::KAAlarm(const KAAlarm &other) : d(new Private(*other.d)) { } KAAlarm::~KAAlarm() { delete d; } KAAlarm &KAAlarm::operator=(const KAAlarm &other) { if (&other != this) { *d = *other.d; } return *this; } KAAlarm::Action KAAlarm::action() const { return d->mActionType; } bool KAAlarm::isValid() const { return d->mType != INVALID_ALARM; } KAAlarm::Type KAAlarm::type() const { return d->mType; } DateTime KAAlarm::dateTime(bool withRepeats) const { return (withRepeats && d->mNextRepeat && d->mRepetition) ? DateTime(d->mRepetition.duration(d->mNextRepeat).end(d->mNextMainDateTime.qDateTime())) : d->mNextMainDateTime; } QDate KAAlarm::date() const { return d->mNextMainDateTime.date(); } QTime KAAlarm::time() const { return d->mNextMainDateTime.effectiveTime(); } bool KAAlarm::repeatAtLogin() const { return d->mRepeatAtLogin; } bool KAAlarm::isReminder() const { return d->mType == REMINDER_ALARM; } bool KAAlarm::deferred() const { return d->mDeferred; } bool KAAlarm::timedDeferral() const { return d->mDeferred && d->mTimedDeferral; } void KAAlarm::setTime(const DateTime &dt) { d->mNextMainDateTime = dt; } void KAAlarm::setTime(const KADateTime &dt) { d->mNextMainDateTime = dt; } #ifdef KDE_NO_DEBUG_OUTPUT const char *KAAlarm::debugType(Type) { return ""; } #else const char *KAAlarm::debugType(Type type) { switch (type) { case MAIN_ALARM: return "MAIN"; case REMINDER_ALARM: return "REMINDER"; case DEFERRED_ALARM: return "DEFERRED"; case DEFERRED_REMINDER_ALARM: return "DEFERRED_REMINDER"; case AT_LOGIN_ALARM: return "LOGIN"; case DISPLAYING_ALARM: return "DISPLAYING"; default: return "INVALID"; } } #endif /*============================================================================= = Class EmailAddressList =============================================================================*/ /****************************************************************************** * Sets the list of email addresses, removing any empty addresses. * Reply = false if empty addresses were found. */ EmailAddressList &EmailAddressList::operator=(const Person::List &addresses) { clear(); for (int p = 0, end = addresses.count(); p < end; ++p) { if (!addresses[p]->email().isEmpty()) { append(addresses[p]); } } return *this; } /****************************************************************************** * Return the email address list as a string list of email addresses. */ EmailAddressList::operator QStringList() const { QStringList list; for (int p = 0, end = count(); p < end; ++p) { list += address(p); } return list; } /****************************************************************************** * Return the email address list as a string, each address being delimited by * the specified separator string. */ QString EmailAddressList::join(const QString &separator) const { QString result; bool first = true; for (int p = 0, end = count(); p < end; ++p) { if (first) { first = false; } else { result += separator; } result += address(p); } return result; } /****************************************************************************** * Convert one item into an email address, including name. */ QString EmailAddressList::address(int index) const { if (index < 0 || index > count()) { return QString(); } QString result; bool quote = false; const Person::Ptr person = (*this)[index]; const QString name = person->name(); if (!name.isEmpty()) { // Need to enclose the name in quotes if it has any special characters for (int i = 0, len = name.length(); i < len; ++i) { const QChar ch = name[i]; if (!ch.isLetterOrNumber()) { quote = true; result += QLatin1Char('\"'); break; } } result += (*this)[index]->name(); result += (quote ? QLatin1String("\" <") : QLatin1String(" <")); quote = true; // need angle brackets round email address } result += person->email(); if (quote) { result += QLatin1Char('>'); } return result; } /****************************************************************************** * Return a list of the pure email addresses, excluding names. */ QStringList EmailAddressList::pureAddresses() const { QStringList list; for (int p = 0, end = count(); p < end; ++p) { list += at(p)->email(); } return list; } /****************************************************************************** * Return a list of the pure email addresses, excluding names, as a string. */ QString EmailAddressList::pureAddresses(const QString &separator) const { QString result; bool first = true; for (int p = 0, end = count(); p < end; ++p) { if (first) { first = false; } else { result += separator; } result += at(p)->email(); } return result; } /*============================================================================= = Static functions =============================================================================*/ /****************************************************************************** * Set the specified alarm to be a procedure alarm with the given command line. * The command line is first split into its program file and arguments before * initialising the alarm. */ static void setProcedureAlarm(const Alarm::Ptr &alarm, const QString &commandLine) { //TODO: cater for environment variables prefixed to command QString command; QString arguments; QChar quoteChar; bool quoted = false; const uint posMax = commandLine.length(); uint pos; for (pos = 0; pos < posMax; ++pos) { const QChar ch = commandLine[pos]; if (quoted) { if (ch == quoteChar) { ++pos; // omit the quote character break; } command += ch; } else { bool done = false; switch (ch.toLatin1()) { case ' ': case ';': case '|': case '<': case '>': done = !command.isEmpty(); break; case '\'': case '"': if (command.isEmpty()) { // Start of a quoted string. Omit the quote character. quoted = true; quoteChar = ch; break; } // fall through to default Q_FALLTHROUGH(); default: command += ch; break; } if (done) { break; } } } // Skip any spaces after the command for (; pos < posMax && commandLine[pos] == QLatin1Char(' '); ++pos) ; arguments = commandLine.mid(pos); alarm->setProcedureAlarm(command, arguments); } /****************************************************************************** * Converts a reminder interval into a parameter string for the * X-KDE-KALARM-FLAGS property. */ QString reminderToString(int minutes) { char unit = 'M'; int count = abs(minutes); if (count % 1440 == 0) { unit = 'D'; count /= 1440; } else if (count % 60 == 0) { unit = 'H'; count /= 60; } if (minutes < 0) { count = -count; } return QStringLiteral("%1%2").arg(count).arg(unit); } } // namespace KAlarmCal // vim: et sw=4: