diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch) | |
tree | 67208f7c145782a7e90b123b982ca78d88cc2c87 /kalarm/karecurrence.cpp | |
download | tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kalarm/karecurrence.cpp')
-rw-r--r-- | kalarm/karecurrence.cpp | 876 |
1 files changed, 876 insertions, 0 deletions
diff --git a/kalarm/karecurrence.cpp b/kalarm/karecurrence.cpp new file mode 100644 index 000000000..9461d71ec --- /dev/null +++ b/kalarm/karecurrence.cpp @@ -0,0 +1,876 @@ +/* + * karecurrence.cpp - recurrence with special yearly February 29th handling + * Program: kalarm + * Copyright © 2005,2006,2008 by David Jarvie <djarvie@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kalarm.h" + +#include <qbitarray.h> +#include <kdebug.h> + +#include <libkcal/icalformat.h> + +#include "datetime.h" +#include "functions.h" +#include "karecurrence.h" + +using namespace KCal; + +/*============================================================================= += Class KARecurrence += The purpose of this class is to represent the restricted range of recurrence += types which are handled by KAlarm, and to translate between these and the += libkcal Recurrence class. In particular, it handles yearly recurrences on += 29th February specially: += += KARecurrence allows annual 29th February recurrences to fall on 28th += February or 1st March, or not at all, in non-leap years. It allows such += 29th February recurrences to be combined with the 29th of other months in += a simple way, represented simply as the 29th of multiple months including += February. For storage in the libkcal calendar, the 29th day of the month += recurrence for other months is combined with a last-day-of-February or a += 60th-day-of-the-year recurrence rule, thereby conforming to RFC2445. +=============================================================================*/ + + +KARecurrence::Feb29Type KARecurrence::mDefaultFeb29 = KARecurrence::FEB29_FEB29; + + +/****************************************************************************** +* Set up a KARecurrence from recurrence parameters, using the start date to +* determine the recurrence day/month as appropriate. +* Only a restricted subset of recurrence types is allowed. +* Reply = true if successful. +*/ +bool KARecurrence::set(Type recurType, int freq, int count, int f29, const DateTime& start, const QDateTime& end) +{ + mCachedType = -1; + RecurrenceRule::PeriodType rrtype; + switch (recurType) + { + case MINUTELY: rrtype = RecurrenceRule::rMinutely; break; + case DAILY: rrtype = RecurrenceRule::rDaily; break; + case WEEKLY: rrtype = RecurrenceRule::rWeekly; break; + case MONTHLY_DAY: rrtype = RecurrenceRule::rMonthly; break; + case ANNUAL_DATE: rrtype = RecurrenceRule::rYearly; break; + case NO_RECUR: rrtype = RecurrenceRule::rNone; break; + default: + return false; + } + if (!init(rrtype, freq, count, f29, start, end)) + return false; + switch (recurType) + { + case WEEKLY: + { + QBitArray days(7); + days.setBit(start.date().dayOfWeek() - 1); + addWeeklyDays(days); + break; + } + case MONTHLY_DAY: + addMonthlyDate(start.date().day()); + break; + case ANNUAL_DATE: + addYearlyDate(start.date().day()); + addYearlyMonth(start.date().month()); + break; + default: + break; + } + return true; +} + +/****************************************************************************** +* Initialise a KARecurrence from recurrence parameters. +* Reply = true if successful. +*/ +bool KARecurrence::init(RecurrenceRule::PeriodType recurType, int freq, int count, int f29, const DateTime& start, + const QDateTime& end) +{ + mCachedType = -1; + Feb29Type feb29Type = (f29 == -1) ? mDefaultFeb29 : static_cast<Feb29Type>(f29); + mFeb29Type = FEB29_FEB29; + clear(); + if (count < -1) + return false; + bool dateOnly = start.isDateOnly(); + if (!count && (!dateOnly && !end.isValid() + || dateOnly && !end.date().isValid())) + return false; + switch (recurType) + { + case RecurrenceRule::rMinutely: + case RecurrenceRule::rDaily: + case RecurrenceRule::rWeekly: + case RecurrenceRule::rMonthly: + case RecurrenceRule::rYearly: + break; + case rNone: + return true; + default: + return false; + } + setNewRecurrenceType(recurType, freq); + if (count) + setDuration(count); + else if (dateOnly) + setEndDate(end.date()); + else + setEndDateTime(end); + QDateTime startdt = start.dateTime(); + if (recurType == RecurrenceRule::rYearly + && feb29Type == FEB29_FEB28 || feb29Type == FEB29_MAR1) + { + int year = startdt.date().year(); + if (!QDate::leapYear(year) + && startdt.date().dayOfYear() == (feb29Type == FEB29_MAR1 ? 60 : 59)) + { + /* The event start date is February 28th or March 1st, but it + * is a recurrence on February 29th (recurring on February 28th + * or March 1st in non-leap years). Adjust the start date to + * be on February 29th in the last previous leap year. + * This is necessary because KARecurrence represents all types + * of 29th February recurrences by a simple 29th February. + */ + while (!QDate::leapYear(--year)) ; + startdt.setDate(QDate(year, 2, 29)); + } + mFeb29Type = feb29Type; + } + if (dateOnly) + setStartDate(startdt.date()); + else + setStartDateTime(startdt); + return true; +} + +/****************************************************************************** + * Initialise the recurrence from an iCalendar RRULE string. + */ +bool KARecurrence::set(const QString& icalRRULE) +{ + static QString RRULE = QString::fromLatin1("RRULE:"); + mCachedType = -1; + clear(); + if (icalRRULE.isEmpty()) + return true; + ICalFormat format; + if (!format.fromString(defaultRRule(true), + (icalRRULE.startsWith(RRULE) ? icalRRULE.mid(RRULE.length()) : icalRRULE))) + return false; + fix(); + return true; +} + +/****************************************************************************** +* Must be called after presetting with a KCal::Recurrence, to convert the +* recurrence to KARecurrence types: +* - Convert hourly recurrences to minutely. +* - Remove all but the first day in yearly date recurrences. +* - Check for yearly recurrences falling on February 29th and adjust them as +* necessary. A 29th of the month rule can be combined with either a 60th day +* of the year rule or a last day of February rule. +*/ +void KARecurrence::fix() +{ + mCachedType = -1; + mFeb29Type = FEB29_FEB29; + int convert = 0; + int days[2] = { 0, 0 }; + RecurrenceRule* rrules[2]; + RecurrenceRule::List rrulelist = rRules(); + RecurrenceRule::List::ConstIterator rr = rrulelist.begin(); + for (int i = 0; i < 2 && rr != rrulelist.end(); ++i, ++rr) + { + RecurrenceRule* rrule = *rr; + rrules[i] = rrule; + bool stop = true; + int rtype = recurrenceType(rrule); + switch (rtype) + { + case rHourly: + // Convert an hourly recurrence to a minutely one + rrule->setRecurrenceType(RecurrenceRule::rMinutely); + rrule->setFrequency(rrule->frequency() * 60); + // fall through to rMinutely + case rMinutely: + case rDaily: + case rWeekly: + case rMonthlyDay: + case rMonthlyPos: + case rYearlyPos: + if (!convert) + ++rr; // remove all rules except the first + break; + case rOther: + if (dailyType(rrule)) + { // it's a daily rule with BYDAYS + if (!convert) + ++rr; // remove all rules except the first + } + break; + case rYearlyDay: + { + // Ensure that the yearly day number is 60 (i.e. Feb 29th/Mar 1st) + if (convert) + { + // This is the second rule. + // Ensure that it can be combined with the first one. + if (days[0] != 29 + || rrule->frequency() != rrules[0]->frequency() + || rrule->startDt() != rrules[0]->startDt()) + break; + } + QValueList<int> ds = rrule->byYearDays(); + if (!ds.isEmpty() && ds.first() == 60) + { + ++convert; // this rule needs to be converted + days[i] = 60; + stop = false; + break; + } + break; // not day 60, so remove this rule + } + case rYearlyMonth: + { + QValueList<int> ds = rrule->byMonthDays(); + if (!ds.isEmpty()) + { + int day = ds.first(); + if (convert) + { + // This is the second rule. + // Ensure that it can be combined with the first one. + if (day == days[0] || day == -1 && days[0] == 60 + || rrule->frequency() != rrules[0]->frequency() + || rrule->startDt() != rrules[0]->startDt()) + break; + } + if (ds.count() > 1) + { + ds.clear(); // remove all but the first day + ds.append(day); + rrule->setByMonthDays(ds); + } + if (day == -1) + { + // Last day of the month - only combine if it's February + QValueList<int> months = rrule->byMonths(); + if (months.count() != 1 || months.first() != 2) + day = 0; + } + if (day == 29 || day == -1) + { + ++convert; // this rule may need to be converted + days[i] = day; + stop = false; + break; + } + } + if (!convert) + ++rr; + break; + } + default: + break; + } + if (stop) + break; + } + + // Remove surplus rules + for ( ; rr != rrulelist.end(); ++rr) + { + removeRRule(*rr); + delete *rr; + } + + QDate end; + int count; + QValueList<int> months; + if (convert == 2) + { + // There are two yearly recurrence rules to combine into a February 29th recurrence. + // Combine the two recurrence rules into a single rYearlyMonth rule falling on Feb 29th. + // Find the duration of the two RRULEs combined, using the shorter of the two if they differ. + if (days[0] != 29) + { + // Swap the two rules so that the 29th rule is the first + RecurrenceRule* rr = rrules[0]; + rrules[0] = rrules[1]; // the 29th rule + rrules[1] = rr; + int d = days[0]; + days[0] = days[1]; + days[1] = d; // the non-29th day + } + // If February is included in the 29th rule, remove it to avoid duplication + months = rrules[0]->byMonths(); + if (months.remove(2)) + rrules[0]->setByMonths(months); + + count = combineDurations(rrules[0], rrules[1], end); + mFeb29Type = (days[1] == 60) ? FEB29_MAR1 : FEB29_FEB28; + } + else if (convert == 1 && days[0] == 60) + { + // There is a single 60th day of the year rule. + // Convert it to a February 29th recurrence. + count = duration(); + if (!count) + end = endDate(); + mFeb29Type = FEB29_MAR1; + } + else + return; + + // Create the new February 29th recurrence + setNewRecurrenceType(RecurrenceRule::rYearly, frequency()); + RecurrenceRule* rrule = defaultRRule(); + months.append(2); + rrule->setByMonths(months); + QValueList<int> ds; + ds.append(29); + rrule->setByMonthDays(ds); + if (count) + setDuration(count); + else + setEndDate(end); +} + +/****************************************************************************** +* Get the next time the recurrence occurs, strictly after a specified time. +*/ +QDateTime KARecurrence::getNextDateTime(const QDateTime& preDateTime) const +{ + switch (type()) + { + case ANNUAL_DATE: + case ANNUAL_POS: + { + Recurrence recur; + writeRecurrence(recur); + return recur.getNextDateTime(preDateTime); + } + default: + return Recurrence::getNextDateTime(preDateTime); + } +} + +/****************************************************************************** +* Get the previous time the recurrence occurred, strictly before a specified time. +*/ +QDateTime KARecurrence::getPreviousDateTime(const QDateTime& afterDateTime) const +{ + switch (type()) + { + case ANNUAL_DATE: + case ANNUAL_POS: + { + Recurrence recur; + writeRecurrence(recur); + return recur.getPreviousDateTime(afterDateTime); + } + default: + return Recurrence::getPreviousDateTime(afterDateTime); + } +} + +/****************************************************************************** +* Initialise a KCal::Recurrence to be the same as this instance. +* Additional recurrence rules are created as necessary if it recurs on Feb 29th. +*/ +void KARecurrence::writeRecurrence(KCal::Recurrence& recur) const +{ + recur.clear(); + recur.setStartDateTime(startDateTime()); + recur.setExDates(exDates()); + recur.setExDateTimes(exDateTimes()); + const RecurrenceRule* rrule = defaultRRuleConst(); + if (!rrule) + return; + int freq = frequency(); + int count = duration(); + static_cast<KARecurrence*>(&recur)->setNewRecurrenceType(rrule->recurrenceType(), freq); + if (count) + recur.setDuration(count); + else + recur.setEndDateTime(endDateTime()); + switch (type()) + { + case DAILY: + if (rrule->byDays().isEmpty()) + break; + // fall through to rWeekly + case WEEKLY: + case MONTHLY_POS: + recur.defaultRRule(true)->setByDays(rrule->byDays()); + break; + case MONTHLY_DAY: + recur.defaultRRule(true)->setByMonthDays(rrule->byMonthDays()); + break; + case ANNUAL_POS: + recur.defaultRRule(true)->setByMonths(rrule->byMonths()); + recur.defaultRRule()->setByDays(rrule->byDays()); + break; + case ANNUAL_DATE: + { + QValueList<int> months = rrule->byMonths(); + QValueList<int> days = monthDays(); + bool special = (mFeb29Type != FEB29_FEB29 && !days.isEmpty() + && days.first() == 29 && months.remove(2)); + RecurrenceRule* rrule1 = recur.defaultRRule(); + rrule1->setByMonths(months); + rrule1->setByMonthDays(days); + if (!special) + break; + + // It recurs on the 29th February. + // Create an additional 60th day of the year, or last day of February, rule. + RecurrenceRule* rrule2 = new RecurrenceRule(); + rrule2->setRecurrenceType(RecurrenceRule::rYearly); + rrule2->setFrequency(freq); + rrule2->setStartDt(startDateTime()); + rrule2->setFloats(doesFloat()); + if (!count) + rrule2->setEndDt(endDateTime()); + if (mFeb29Type == FEB29_MAR1) + { + QValueList<int> ds; + ds.append(60); + rrule2->setByYearDays(ds); + } + else + { + QValueList<int> ds; + ds.append(-1); + rrule2->setByMonthDays(ds); + QValueList<int> ms; + ms.append(2); + rrule2->setByMonths(ms); + } + + if (months.isEmpty()) + { + // Only February recurs. + // Replace the RRULE and keep the recurrence count the same. + if (count) + rrule2->setDuration(count); + recur.unsetRecurs(); + } + else + { + // Months other than February also recur on the 29th. + // Remove February from the list and add a separate RRULE for February. + if (count) + { + rrule1->setDuration(-1); + rrule2->setDuration(-1); + if (count > 0) + { + /* Adjust counts in the two rules to keep the correct occurrence total. + * Note that durationTo() always includes the start date. Since for an + * individual RRULE the start date may not actually be included, we need + * to decrement the count if the start date doesn't actually recur in + * this RRULE. + * Note that if the count is small, one of the rules may not recur at + * all. In that case, retain it so that the February 29th characteristic + * is not lost should the user later change the recurrence count. + */ + QDateTime end = endDateTime(); +kdDebug()<<"29th recurrence: count="<<count<<", end date="<<end.toString()<<endl; + int count1 = rrule1->durationTo(end) + - (rrule1->recursOn(startDate()) ? 0 : 1); + if (count1 > 0) + rrule1->setDuration(count1); + else + rrule1->setEndDt(startDateTime()); + int count2 = rrule2->durationTo(end) + - (rrule2->recursOn(startDate()) ? 0 : 1); + if (count2 > 0) + rrule2->setDuration(count2); + else + rrule2->setEndDt(startDateTime()); + } + } + } + recur.addRRule(rrule2); + break; + } + default: + break; + } +} + +/****************************************************************************** +* Return the date/time of the last recurrence. +*/ +QDateTime KARecurrence::endDateTime() const +{ + if (mFeb29Type == FEB29_FEB29 || duration() <= 1) + { + /* Either it doesn't have any special February 29th treatment, + * it's infinite (count = -1), the end date is specified + * (count = 0), or it ends on the start date (count = 1). + * So just use the normal KCal end date calculation. + */ + return Recurrence::endDateTime(); + } + + /* Create a temporary recurrence rule to find the end date. + * In a standard KCal recurrence, the 29th February only occurs once every + * 4 years. So shift the temporary recurrence date to the 28th to ensure + * that it occurs every year, thus giving the correct occurrence count. + */ + RecurrenceRule* rrule = new RecurrenceRule(); + rrule->setRecurrenceType(RecurrenceRule::rYearly); + QDateTime dt = startDateTime(); + QDate d = dt.date(); + switch (d.day()) + { + case 29: + // The start date is definitely a recurrence date, so shift + // start date to the temporary recurrence date of the 28th + d.setYMD(d.year(), d.month(), 28); + break; + case 28: + if (d.month() != 2 || mFeb29Type != FEB29_FEB28 || QDate::leapYear(d.year())) + { + // Start date is not a recurrence date, so shift it to 27th + d.setYMD(d.year(), d.month(), 27); + } + break; + case 1: + if (d.month() == 3 && mFeb29Type == FEB29_MAR1 && !QDate::leapYear(d.year())) + { + // Start date is a March 1st recurrence date, so shift + // start date to the temporary recurrence date of the 28th + d.setYMD(d.year(), 2, 28); + } + break; + default: + break; + } + dt.setDate(d); + rrule->setStartDt(dt); + rrule->setFloats(doesFloat()); + rrule->setFrequency(frequency()); + rrule->setDuration(duration()); + QValueList<int> ds; + ds.append(28); + rrule->setByMonthDays(ds); + rrule->setByMonths(defaultRRuleConst()->byMonths()); + dt = rrule->endDt(); + delete rrule; + + // We've found the end date for a recurrence on the 28th. Unless that date + // is a real February 28th recurrence, adjust to the actual recurrence date. + if (mFeb29Type == FEB29_FEB28 && dt.date().month() == 2 && !QDate::leapYear(dt.date().year())) + return dt; + return dt.addDays(1); +} + +/****************************************************************************** +* Return the date/time of the last recurrence. +*/ +QDate KARecurrence::endDate() const +{ + QDateTime end = endDateTime(); + return end.isValid() ? end.date() : QDate(); +} + +/****************************************************************************** +* Return whether the event will recur on the specified date. +* The start date only returns true if it matches the recurrence rules. +*/ +bool KARecurrence::recursOn(const QDate& dt) const +{ + if (!Recurrence::recursOn(dt)) + return false; + if (dt != startDate()) + return true; + // We know now that it isn't in EXDATES or EXRULES, + // so we just need to check if it's in RDATES or RRULES + if (rDates().contains(dt)) + return true; + RecurrenceRule::List rulelist = rRules(); + for (RecurrenceRule::List::ConstIterator rr = rulelist.begin(); rr != rulelist.end(); ++rr) + if ((*rr)->recursOn(dt)) + return true; + DateTimeList dtlist = rDateTimes(); + for (DateTimeList::ConstIterator rdt = dtlist.begin(); rdt != dtlist.end(); ++rdt) + if ((*rdt).date() == dt) + return true; + return false; +} + +/****************************************************************************** +* Find the duration of two RRULEs combined. +* Use the shorter of the two if they differ. +*/ +int KARecurrence::combineDurations(const RecurrenceRule* rrule1, const RecurrenceRule* rrule2, QDate& end) const +{ + int count1 = rrule1->duration(); + int count2 = rrule2->duration(); + if (count1 == -1 && count2 == -1) + return -1; + + // One of the RRULEs may not recur at all if the recurrence count is small. + // In this case, its end date will have been set to the start date. + if (count1 && !count2 && rrule2->endDt().date() == startDateTime().date()) + return count1; + if (count2 && !count1 && rrule1->endDt().date() == startDateTime().date()) + return count2; + + /* The duration counts will be different even for RRULEs of the same length, + * because the first RRULE only actually occurs every 4 years. So we need to + * compare the end dates. + */ + if (!count1 || !count2) + count1 = count2 = 0; + // Get the two rules sorted by end date. + QDateTime end1 = rrule1->endDt(); + QDateTime end2 = rrule2->endDt(); + if (end1.date() == end2.date()) + { + end = end1.date(); + return count1 + count2; + } + const RecurrenceRule* rr1; // earlier end date + const RecurrenceRule* rr2; // later end date + if (end2.isValid() + && (!end1.isValid() || end1.date() > end2.date())) + { + // Swap the two rules to make rr1 have the earlier end date + rr1 = rrule2; + rr2 = rrule1; + QDateTime e = end1; + end1 = end2; + end2 = e; + } + else + { + rr1 = rrule1; + rr2 = rrule2; + } + + // Get the date of the next occurrence after the end of the earlier ending rule + RecurrenceRule rr(*rr1); + rr.setDuration(-1); + QDateTime next1(rr.getNextDate(end1).date()); + if (!next1.isValid()) + end = end1.date(); + else + { + if (end2.isValid() && next1 > end2) + { + // The next occurrence after the end of the earlier ending rule + // is later than the end of the later ending rule. So simply use + // the end date of the later rule. + end = end2.date(); + return count1 + count2; + } + QDate prev2 = rr2->getPreviousDate(next1).date(); + end = (prev2 > end1.date()) ? prev2 : end1.date(); + } + if (count2) + count2 = rr2->durationTo(end); + return count1 + count2; +} + +/****************************************************************************** + * Return the longest interval (in minutes) between recurrences. + * Reply = 0 if it never recurs. + */ +int KARecurrence::longestInterval() const +{ + int freq = frequency(); + switch (type()) + { + case MINUTELY: + return freq; + + case DAILY: + { + QValueList<RecurrenceRule::WDayPos> days = defaultRRuleConst()->byDays(); + if (days.isEmpty()) + return freq * 1440; + + // It recurs only on certain days of the week, so the maximum interval + // may be greater than the frequency. + bool ds[7] = { false, false, false, false, false, false, false }; + for (QValueList<RecurrenceRule::WDayPos>::ConstIterator it = days.begin(); it != days.end(); ++it) + if ((*it).pos() == 0) + ds[(*it).day() - 1] = true; + if (freq % 7) + { + // It will recur on every day of the week in some week or other + // (except for those days which are excluded). + int first = -1; + int last = -1; + int maxgap = 1; + for (int i = 0; i < freq*7; i += freq) + { + if (ds[i % 7]) + { + if (first < 0) + first = i; + else if (i - last > maxgap) + maxgap = i - last; + last = i; + } + } + int wrap = freq*7 - last + first; + if (wrap > maxgap) + maxgap = wrap; + return maxgap * 1440; + } + else + { + // It will recur on the same day of the week every time. + // Ensure that the day is a day which is not excluded. + return ds[startDate().dayOfWeek() - 1] ? freq * 1440 : 0; + } + } + case WEEKLY: + { + // Find which days of the week it recurs on, and if on more than + // one, reduce the maximum interval accordingly. + QBitArray ds = days(); + int first = -1; + int last = -1; + int maxgap = 1; + for (int i = 0; i < 7; ++i) + { + if (ds.testBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1)) + { + if (first < 0) + first = i; + else if (i - last > maxgap) + maxgap = i - last; + last = i; + } + } + if (first < 0) + break; // no days recur + int span = last - first; + if (freq > 1) + return (freq*7 - span) * 1440; + if (7 - span > maxgap) + return (7 - span) * 1440; + return maxgap * 1440; + } + case MONTHLY_DAY: + case MONTHLY_POS: + return freq * 1440 * 31; + + case ANNUAL_DATE: + case ANNUAL_POS: + { + // Find which months of the year it recurs on, and if on more than + // one, reduce the maximum interval accordingly. + const QValueList<int> months = yearMonths(); // month list is sorted + if (months.isEmpty()) + break; // no months recur + if (months.count() == 1) + return freq * 1440 * 365; + int first = -1; + int last = -1; + int maxgap = 0; + for (QValueListConstIterator<int> it = months.begin(); it != months.end(); ++it) + { + if (first < 0) + first = *it; + else + { + int span = QDate(2001, last, 1).daysTo(QDate(2001, *it, 1)); + if (span > maxgap) + maxgap = span; + } + last = *it; + } + int span = QDate(2001, first, 1).daysTo(QDate(2001, last, 1)); + if (freq > 1) + return (freq*365 - span) * 1440; + if (365 - span > maxgap) + return (365 - span) * 1440; + return maxgap * 1440; + } + default: + break; + } + return 0; +} + +/****************************************************************************** + * Return the recurrence's period type. + */ +KARecurrence::Type KARecurrence::type() const +{ + if (mCachedType == -1) + mCachedType = type(defaultRRuleConst()); + return static_cast<Type>(mCachedType); +} + +KARecurrence::Type KARecurrence::type(const RecurrenceRule* rrule) +{ + switch (recurrenceType(rrule)) + { + case rMinutely: return MINUTELY; + case rDaily: return DAILY; + case rWeekly: return WEEKLY; + case rMonthlyDay: return MONTHLY_DAY; + case rMonthlyPos: return MONTHLY_POS; + case rYearlyMonth: return ANNUAL_DATE; + case rYearlyPos: return ANNUAL_POS; + default: + if (dailyType(rrule)) + return DAILY; + return NO_RECUR; + } +} + +/****************************************************************************** + * Check if the rule is a daily rule with or without BYDAYS specified. + */ +bool KARecurrence::dailyType(const RecurrenceRule* rrule) +{ + if (rrule->recurrenceType() != RecurrenceRule::rDaily + || !rrule->bySeconds().isEmpty() + || !rrule->byMinutes().isEmpty() + || !rrule->byHours().isEmpty() + || !rrule->byWeekNumbers().isEmpty() + || !rrule->byMonthDays().isEmpty() + || !rrule->byMonths().isEmpty() + || !rrule->bySetPos().isEmpty() + || !rrule->byYearDays().isEmpty()) + return false; + QValueList<RecurrenceRule::WDayPos> days = rrule->byDays(); + if (days.isEmpty()) + return true; + // Check that all the positions are zero (i.e. every time) + bool found = false; + for (QValueList<RecurrenceRule::WDayPos>::ConstIterator it = days.begin(); it != days.end(); ++it) + { + if ((*it).pos() != 0) + return false; + found = true; + } + return found; + +} |