00001 #include <time.h>
00002
00003 #include <qshared.h>
00004
00005 #include <qtopia/timeconversion.h>
00006
00007 #include "otimezone.h"
00008 #include "orecur.h"
00009
00010 struct ORecur::Data : public QShared {
00011 Data() : QShared() {
00012 type = ORecur::NoRepeat;
00013 freq = -1;
00014 days = 0;
00015 pos = 0;
00016 create = QDateTime::currentDateTime();
00017 hasEnd = FALSE;
00018 end = QDate::currentDate();
00019 }
00020 char days;
00021 ORecur::RepeatType type;
00022 int freq;
00023 int pos;
00024 bool hasEnd : 1;
00025 QDate end;
00026 QDateTime create;
00027 int rep;
00028 QString app;
00029 ExceptionList list;
00030 QDate start;
00031 };
00032
00033
00034 ORecur::ORecur() {
00035 data = new Data;
00036 }
00037
00038 ORecur::ORecur( const QMap<int, QString>& map )
00039 {
00040 ORecur();
00041 fromMap( map );
00042 }
00043
00044
00045 ORecur::ORecur( const ORecur& rec)
00046 : data( rec.data )
00047 {
00048 data->ref();
00049 }
00050 ORecur::~ORecur() {
00051 if ( data->deref() ) {
00052 delete data;
00053 data = 0l;
00054 }
00055 }
00056 void ORecur::deref() {
00057 if ( data->deref() ) {
00058 delete data;
00059 data = 0l;
00060 }
00061 }
00062 bool ORecur::operator==( const ORecur& )const {
00063 return false;
00064 }
00065 ORecur &ORecur::operator=( const ORecur& re) {
00066 if ( *this == re ) return *this;
00067
00068 re.data->ref();
00069 deref();
00070 data = re.data;
00071
00072 return *this;
00073 }
00074 bool ORecur::doesRecur()const {
00075 return !( type() == NoRepeat );
00076 }
00077
00078
00079
00080
00081 bool ORecur::doesRecur( const QDate& date ) {
00082
00083 QDate da = date.addDays(-1);
00084
00085 QDate recur;
00086 if (!nextOcurrence( da, recur ) )
00087 return false;
00088
00089 return (recur == date);
00090 }
00091
00092
00093
00094 bool ORecur::nextOcurrence( const QDate& from, QDate& next ) {
00095 bool stillLooking;
00096 stillLooking = p_nextOccurrence( from, next );
00097 while ( stillLooking && data->list.contains(next) )
00098 stillLooking = p_nextOccurrence( next.addDays(1), next );
00099
00100 return stillLooking;
00101 }
00102 bool ORecur::p_nextOccurrence( const QDate& from, QDate& next ) {
00103
00104
00105 QDate tmpDate;
00106 int freq = frequency();
00107 int diff, diff2, a;
00108 int iday, imonth, iyear;
00109 int dayOfWeek = 0;
00110 int firstOfWeek = 0;
00111 int weekOfMonth;
00112
00113
00114 if (hasEndDate() && endDate() < from)
00115 return FALSE;
00116
00117 if (start() >= from ) {
00118 next = start();
00119 return TRUE;
00120 }
00121
00122 switch ( type() ) {
00123 case Weekly:
00124
00125
00126
00127 dayOfWeek = from.dayOfWeek();
00128 dayOfWeek--;
00129
00130
00131
00132
00133 while(!((1 << firstOfWeek) & days() ))
00134 firstOfWeek++;
00135
00136
00137 while(!((1 << (dayOfWeek % 7)) & days() ))
00138 dayOfWeek++;
00139
00140 dayOfWeek = dayOfWeek % 7;
00141 dayOfWeek -= start().dayOfWeek() -1;
00142
00143 firstOfWeek = firstOfWeek % 7;
00144 firstOfWeek -= start().dayOfWeek() -1;
00145
00146
00147
00148
00149 freq *= 7;
00150
00151 case Daily:
00152
00153 if(start().addDays(dayOfWeek) > from) {
00154
00155 next = QDate(start().addDays(dayOfWeek) );
00156 if ((next > endDate())
00157 && hasEndDate() )
00158 return FALSE;
00159 return TRUE;
00160 }
00161
00162
00163 diff = start().addDays(dayOfWeek).daysTo(from) % freq;
00164 diff2 = start().addDays(firstOfWeek).daysTo(from) % freq;
00165
00166 if(diff != 0)
00167 diff = freq - diff;
00168 if(diff2 != 0)
00169 diff2 = freq - diff2;
00170 diff = QMIN(diff, diff2);
00171
00172 next = QDate(from.addDays(diff));
00173 if ( (next > endDate())
00174 && hasEndDate() )
00175 return FALSE;
00176 return TRUE;
00177 case MonthlyDay:
00178 iday = from.day();
00179 iyear = from.year();
00180 imonth = from.month();
00181
00182 dayOfWeek = start().dayOfWeek();
00183 weekOfMonth = (start().day() - 1) / 7;
00184
00185
00186 a = from.year() - start().year();
00187 a *= 12;
00188 a = a + (imonth - start().month());
00189
00190 if(a % freq) {
00191 a = freq - (a % freq);
00192 imonth = from.month() + a;
00193 if (imonth > 12) {
00194 imonth--;
00195 iyear += imonth / 12;
00196 imonth = imonth % 12;
00197 imonth++;
00198 }
00199 }
00200
00201
00202
00203
00204 tmpDate = QDate( iyear, imonth, 1 );
00205
00206 iday = 1;
00207 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00208 iday += 7 * weekOfMonth;
00209 while (iday > tmpDate.daysInMonth()) {
00210 imonth += freq;
00211 if (imonth > 12) {
00212 imonth--;
00213 iyear += imonth / 12;
00214 imonth = imonth % 12;
00215 imonth++;
00216 }
00217 tmpDate = QDate( iyear, imonth, 1 );
00218
00219 if ((tmpDate > endDate()) && hasEndDate() )
00220 return FALSE;
00221 iday = 1;
00222 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00223 iday += 7 * weekOfMonth;
00224 }
00225 tmpDate = QDate(iyear, imonth, iday);
00226
00227 if (tmpDate >= from) {
00228 next = tmpDate;
00229 if ((next > endDate() ) && hasEndDate() )
00230 return FALSE;
00231 return TRUE;
00232 }
00233
00234
00235 do {
00236 imonth += freq;
00237 if (imonth > 12) {
00238 imonth--;
00239 iyear += imonth / 12;
00240 imonth = imonth % 12;
00241 imonth++;
00242 }
00243 tmpDate = QDate( iyear, imonth, 1 );
00244
00245 if ((tmpDate > endDate()) && hasEndDate() )
00246 return FALSE;
00247 iday = 1;
00248 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00249 iday += 7 * weekOfMonth;
00250 } while (iday > tmpDate.daysInMonth());
00251 tmpDate = QDate(iyear, imonth, iday);
00252
00253 next = tmpDate;
00254 if ((next > endDate()) && hasEndDate() )
00255 return FALSE;
00256 return TRUE;
00257 case MonthlyDate:
00258 iday = start().day();
00259 iyear = from.year();
00260 imonth = from.month();
00261
00262 a = from.year() - start().year();
00263 a *= 12;
00264 a = a + (imonth - start().month());
00265
00266 if(a % freq) {
00267 a = freq - (a % freq);
00268 imonth = from.month() + a;
00269 if (imonth > 12) {
00270 imonth--;
00271 iyear += imonth / 12;
00272 imonth = imonth % 12;
00273 imonth++;
00274 }
00275 }
00276
00277
00278
00279
00280 while(!QDate::isValid(iyear, imonth, iday) ) {
00281 imonth += freq;
00282 if (imonth > 12) {
00283 imonth--;
00284 iyear += imonth / 12;
00285 imonth = imonth % 12;
00286 imonth++;
00287 }
00288
00289 if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() )
00290 return FALSE;
00291 }
00292
00293 if(QDate(iyear, imonth, iday) >= from) {
00294
00295 next = QDate(iyear, imonth, iday);
00296 if ((next > endDate()) && hasEndDate() )
00297 return FALSE;
00298 return TRUE;
00299 }
00300
00301
00302 imonth += freq;
00303 imonth--;
00304 iyear += imonth / 12;
00305 imonth = imonth % 12;
00306 imonth++;
00307
00308 while(!QDate::isValid(iyear, imonth, iday) ) {
00309 imonth += freq;
00310 imonth--;
00311 iyear += imonth / 12;
00312 imonth = imonth % 12;
00313 imonth++;
00314 if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() )
00315 return FALSE;
00316 }
00317
00318 next = QDate(iyear, imonth, iday);
00319 if ((next > endDate()) && hasEndDate() )
00320 return FALSE;
00321 return TRUE;
00322 case Yearly:
00323 iday = start().day();
00324 imonth = start().month();
00325 iyear = from.year();
00326
00327 diff = 1;
00328 if(imonth == 2 && iday > 28) {
00329
00330 if(freq % 4)
00331 if (freq % 2)
00332 freq = freq * 4;
00333 else
00334 freq = freq * 2;
00335
00336 diff = 4;
00337 }
00338
00339 a = from.year() - start().year();
00340 if(a % freq) {
00341 a = freq - (a % freq);
00342 iyear = iyear + a;
00343 }
00344
00345
00346 if(!QDate::isValid(iyear, imonth, iday)) {
00347
00348 iyear += freq;
00349 }
00350
00351 if(QDate(iyear, imonth, iday) >= from) {
00352 next = QDate(iyear, imonth, iday);
00353
00354 if ((next > endDate()) && hasEndDate() )
00355 return FALSE;
00356 return TRUE;
00357 }
00358
00359 iyear += freq;
00360
00361 if(!QDate::isValid(iyear, imonth, iday)) {
00362
00363 iyear += freq;
00364 }
00365
00366 next = QDate(iyear, imonth, iday);
00367 if ((next > endDate()) && hasEndDate() )
00368 return FALSE;
00369 return TRUE;
00370 default:
00371 return FALSE;
00372 }
00373 }
00374 ORecur::RepeatType ORecur::type()const{
00375 return data->type;
00376 }
00377 int ORecur::frequency()const {
00378 return data->freq;
00379 }
00380 int ORecur::position()const {
00381 return data->pos;
00382 }
00383 char ORecur::days() const{
00384 return data->days;
00385 }
00386 bool ORecur::hasEndDate()const {
00387 return data->hasEnd;
00388 }
00389 QDate ORecur::endDate()const {
00390 return data->end;
00391 }
00392 QDate ORecur::start()const{
00393 return data->start;
00394 }
00395 QDateTime ORecur::createdDateTime()const {
00396 return data->create;
00397 }
00398 int ORecur::repetition()const {
00399 return data->rep;
00400 }
00401 QString ORecur::service()const {
00402 return data->app;
00403 }
00404 ORecur::ExceptionList& ORecur::exceptions() {
00405 return data->list;
00406 }
00407 void ORecur::setType( const RepeatType& z) {
00408 checkOrModify();
00409 data->type = z;
00410 }
00411 void ORecur::setFrequency( int freq ) {
00412 checkOrModify();
00413 data->freq = freq;
00414 }
00415 void ORecur::setPosition( int pos ) {
00416 checkOrModify();
00417 data->pos = pos;
00418 }
00419 void ORecur::setDays( char c ) {
00420 checkOrModify();
00421 data->days = c;
00422 }
00423 void ORecur::setEndDate( const QDate& dt) {
00424 checkOrModify();
00425 data->end = dt;
00426 }
00427 void ORecur::setCreatedDateTime( const QDateTime& t) {
00428 checkOrModify();
00429 data->create = t;
00430 }
00431 void ORecur::setHasEndDate( bool b) {
00432 checkOrModify();
00433 data->hasEnd = b;
00434 }
00435 void ORecur::setRepitition( int rep ) {
00436 checkOrModify();
00437 data->rep = rep;
00438 }
00439 void ORecur::setService( const QString& app ) {
00440 checkOrModify();
00441 data->app = app;
00442 }
00443 void ORecur::setStart( const QDate& dt ) {
00444 checkOrModify();
00445 data->start = dt;
00446 }
00447 void ORecur::checkOrModify() {
00448 if ( data->count != 1 ) {
00449 data->deref();
00450 Data* d2 = new Data;
00451 d2->days = data->days;
00452 d2->type = data->type;
00453 d2->freq = data->freq;
00454 d2->pos = data->pos;
00455 d2->hasEnd = data->hasEnd;
00456 d2->end = data->end;
00457 d2->create = data->create;
00458 d2->rep = data->rep;
00459 d2->app = data->app;
00460 d2->list = data->list;
00461 d2->start = data->start;
00462 data = d2;
00463 }
00464 }
00465 QString ORecur::toString()const {
00466 QString buf;
00467 QMap<int, QString> recMap = toMap();
00468
00469 buf += " rtype=\"";
00470 buf += recMap[ORecur::RType];
00471 buf += "\"";
00472 if (data->days > 0 )
00473 buf += " rweekdays=\"" + recMap[ORecur::RWeekdays] + "\"";
00474 if ( data->pos != 0 )
00475 buf += " rposition=\"" + recMap[ORecur::RPosition] + "\"";
00476
00477 buf += " rfreq=\"" + recMap[ORecur::RFreq] + "\"";
00478 buf += " rhasenddate=\"" + recMap[ORecur::RHasEndDate]+ "\"";
00479 if ( data->hasEnd )
00480 buf += " enddt=\""
00481 + recMap[ORecur::EndDate]
00482 + "\"";
00483 buf += " created=\"" + recMap[ORecur::Created] + "\"";
00484
00485 if ( data->list.isEmpty() ) return buf;
00486 buf += " exceptions=\"";
00487 buf += recMap[ORecur::Exceptions];
00488 buf += "\" ";
00489
00490 return buf;
00491 }
00492
00493 QString ORecur::rTypeString() const
00494 {
00495 QString retString;
00496 switch ( data->type ) {
00497 case ORecur::Daily:
00498 retString = "Daily";
00499 break;
00500 case ORecur::Weekly:
00501 retString = "Weekly";
00502 break;
00503 case ORecur::MonthlyDay:
00504 retString = "MonthlyDay";
00505 break;
00506 case ORecur::MonthlyDate:
00507 retString = "MonthlyDate";
00508 break;
00509 case ORecur::Yearly:
00510 retString = "Yearly";
00511 break;
00512 default:
00513 retString = "NoRepeat";
00514 break;
00515
00516 }
00517
00518 return retString;
00519 }
00520
00521 QMap<QString, ORecur::RepeatType> ORecur::rTypeValueConvertMap() const
00522 {
00523 QMap<QString, RepeatType> convertMap;
00524
00525 convertMap.insert( QString( "Daily" ), ORecur::Daily );
00526 convertMap.insert( QString( "Weekly" ), ORecur::Weekly );
00527 convertMap.insert( QString( "MonthlyDay" ), ORecur::MonthlyDay );
00528 convertMap.insert( QString( "MonthlyDate" ), ORecur::MonthlyDate );
00529 convertMap.insert( QString( "Yearly" ), ORecur::Yearly );
00530 convertMap.insert( QString( "NoRepeat" ), ORecur::NoRepeat );
00531
00532 return convertMap;
00533 }
00534
00535
00536 QMap<int, QString> ORecur::toMap() const
00537 {
00538 QMap<int, QString> retMap;
00539
00540 retMap.insert( ORecur::RType, rTypeString() );
00541 retMap.insert( ORecur::RWeekdays, QString::number( static_cast<int>( data->days ) ) );
00542 retMap.insert( ORecur::RPosition, QString::number(data->pos ) );
00543 retMap.insert( ORecur::RFreq, QString::number( data->freq ) );
00544 retMap.insert( ORecur::RHasEndDate, QString::number( static_cast<int>( data->hasEnd ) ) );
00545 if( data -> hasEnd )
00546 retMap.insert( ORecur::EndDate, QString::number( OTimeZone::utc().fromUTCDateTime( QDateTime( data->end, QTime(12,0,0) ) ) ) );
00547 retMap.insert( ORecur::Created, QString::number( OTimeZone::utc().fromUTCDateTime( data->create ) ) );
00548
00549 if ( data->list.isEmpty() ) return retMap;
00550
00551
00552 ExceptionList::ConstIterator it;
00553 ExceptionList list = data->list;
00554 QString exceptBuf;
00555 QDate date;
00556 for ( it = list.begin(); it != list.end(); ++it ) {
00557 date = (*it);
00558 if ( it != list.begin() ) exceptBuf += " ";
00559
00560 exceptBuf += QCString().sprintf("%04d%02d%02d", date.year(), date.month(), date.day() );
00561 }
00562
00563 retMap.insert( ORecur::Exceptions, exceptBuf );
00564
00565 return retMap;
00566 }
00567
00568 void ORecur::fromMap( const QMap<int, QString>& map )
00569 {
00570 QMap<QString, RepeatType> repTypeMap = rTypeValueConvertMap();
00571
00572 data -> type = repTypeMap[ map [ORecur::RType] ];
00573 data -> days = (char) map[ ORecur::RWeekdays ].toInt();
00574 data -> pos = map[ ORecur::RPosition ].toInt();
00575 data -> freq = map[ ORecur::RFreq ].toInt();
00576 data -> hasEnd= map[ ORecur::RHasEndDate ].toInt() ? true : false;
00577 OTimeZone utc = OTimeZone::utc();
00578 if ( data -> hasEnd ){
00579 data -> end = utc.fromUTCDateTime( (time_t) map[ ORecur::EndDate ].toLong() ).date();
00580 }
00581 data -> create = utc.fromUTCDateTime( (time_t) map[ ORecur::Created ].toLong() ).date();
00582
00583 #if 0
00584
00585
00586 data -> list.clear();
00587 QString exceptStr = map[ ORecur::Exceptions ];
00588 QStringList exceptList = QStringList::split( " ", exceptStr );
00589 ...
00590 #endif
00591
00592
00593 }