void CompatPre31::fixRecurrence( const Incidence::Ptr &incidence ) { CompatPre32::fixRecurrence( incidence ); Recurrence *recur = incidence->recurrence(); RecurrenceRule *r = 0; if ( recur ) { r = recur->defaultRRule(); } if ( recur && r ) { int duration = r->duration(); if ( duration > 0 ) { // Backwards compatibility for KDE < 3.1. // rDuration was set to the number of time periods to recur, // with week start always on a Monday. // Convert this to the number of occurrences. r->setDuration( -1 ); QDate end( r->startDt().date() ); bool doNothing = false; // # of periods: int tmp = ( duration - 1 ) * r->frequency(); switch ( r->recurrenceType() ) { case RecurrenceRule::rWeekly: { end = end.addDays( tmp * 7 + 7 - end.dayOfWeek() ); break; } case RecurrenceRule::rMonthly: { int month = end.month() - 1 + tmp; end.setDate( end.year() + month / 12, month % 12 + 1, 31 ); break; } case RecurrenceRule::rYearly: { end.setDate( end.year() + tmp, 12, 31 ); break; } default: doNothing = true; break; } if ( !doNothing ) { duration = r->durationTo( KDateTime( end, QTime( 0, 0, 0 ), incidence->dtStart().timeSpec() ) ); r->setDuration( duration ); } } /* addYearlyNum */ // Dates were stored as day numbers, with a fiddle to take account of // leap years. Convert the day number to a month. QList<int> days = r->byYearDays(); if ( !days.isEmpty() ) { QList<int> months = r->byMonths(); for ( int i = 0; i < months.size(); ++i ) { int newmonth = QDate( r->startDt().date().year(), 1, 1 ).addDays( months.at( i ) - 1 ).month(); if ( !months.contains( newmonth ) ) { months.append( newmonth ); } } r->setByMonths( months ); days.clear(); r->setByYearDays( days ); } } }
/****************************************************************************** * 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); }