File indexing completed on 2024-04-21 03:52:54
0001 /* 0002 This file is part of the kcalcore library. 0003 0004 SPDX-FileCopyrightText: 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. 0005 SPDX-FileContributor: Alvaro Manera <alvaro.manera@nokia.com> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 #include "sorting.h" 0010 0011 // PENDING(kdab) Review 0012 // The QString::compare() need to be replace by a DUI string comparisons. 0013 // See http://qt.gitorious.org/maemo-6-ui-framework/libdui 0014 // If not compiled in "meego-mode" should we be using locale compares? 0015 0016 using namespace KCalendarCore; 0017 0018 /** 0019 * How one QDateTime compares with another. 0020 * 0021 * If any all-day events are involved, comparison of QDateTime values 0022 * requires them to be considered as representing time periods. An all-day 0023 * instance represents a time period from 00:00:00 to 23:59:59.999 on a given 0024 * date, while a date/time instance can be considered to represent a time 0025 * period whose start and end times are the same. They may therefore be 0026 * earlier or later, or may overlap or be contained one within the other. 0027 * 0028 * Values may be OR'ed with each other in any combination of 'consecutive' 0029 * intervals to represent different types of relationship. 0030 * 0031 * In the descriptions of the values below, 0032 * - s1 = start time of first instance 0033 * - e1 = end time of first instance 0034 * - s2 = start time of second instance 0035 * - e2 = end time of second instance. 0036 */ 0037 enum DateTimeComparison { 0038 Before = 0x01, /**< The first QDateTime is strictly earlier than the second, 0039 * i.e. e1 < s2. 0040 */ 0041 AtStart = 0x02, /**< The first QDateTime starts at the same time as the second, 0042 * and ends before the end of the second, 0043 * i.e. s1 = s2, e1 < e2. 0044 */ 0045 Inside = 0x04, /**< The first QDateTime starts after the start of the second, 0046 * and ends before the end of the second, 0047 * i.e. s1 > s2, e1 < e2. 0048 */ 0049 AtEnd = 0x08, /**< The first QDateTime starts after the start of the second, 0050 * and ends at the same time as the second, 0051 * i.e. s1 > s2, e1 = e2. 0052 */ 0053 After = 0x10, /**< The first QDateTime is strictly later than the second, 0054 * i.e. s1 > e2. 0055 */ 0056 Equal = AtStart | Inside | AtEnd, 0057 /**< Simultaneous, i.e. s1 = s2 && e1 = e2. 0058 */ 0059 Outside = Before | AtStart | Inside | AtEnd | After, 0060 /**< The first QDateTime starts before the start of the other, 0061 * and ends after the end of the other, 0062 * i.e. s1 < s2, e1 > e2. 0063 */ 0064 StartsAt = AtStart | Inside | AtEnd | After, 0065 /**< The first QDateTime starts at the same time as the other, 0066 * and ends after the end of the other, 0067 * i.e. s1 = s2, e1 > e2. 0068 */ 0069 EndsAt = Before | AtStart | Inside | AtEnd, 0070 /**< The first QDateTime starts before the start of the other, 0071 * and ends at the same time as the other, 0072 * i.e. s1 < s2, e1 = e2. 0073 */ 0074 }; 0075 0076 /** 0077 * Compare two QDateTime instances to determine whether they are 0078 * simultaneous, earlier or later. 0079 0080 * The comparison takes time zones into account: if the two instances have 0081 * different time zones, they are first converted to UTC before comparing. 0082 * 0083 * If both instances are not all-day values, the first instance is considered to 0084 * be either simultaneous, earlier or later, and does not overlap. 0085 * 0086 * If one instance is all-day and the other is a not all-day, the first instance 0087 * is either strictly earlier, strictly later, or overlaps. 0088 * 0089 * If both instance are all-day, they are considered simultaneous if both 0090 * their start of day and end of day times are simultaneous with each 0091 * other. (Both start and end of day times need to be considered in case a 0092 * daylight savings change occurs during that day.) Otherwise, the first instance 0093 * can be strictly earlier, earlier but overlapping, later but overlapping, 0094 * or strictly later. 0095 * 0096 * Note that if either instance is a local time (Qt::TimeSpec of QTimeZone::LocalTime), 0097 * the result cannot be guaranteed to be correct, since by definition they 0098 * contain no information about time zones or daylight savings changes. 0099 * 0100 * @return DateTimeComparison indicating the relationship of dt1 to dt2 0101 * @see operator==(), operator!=(), operator<(), operator<=(), operator>=(), operator>() 0102 */ 0103 0104 DateTimeComparison compare(const QDateTime &dt1, bool isAllDay1, const QDateTime &dt2, bool isAllDay2) 0105 { 0106 QDateTime start1; 0107 QDateTime start2; 0108 // FIXME When secondOccurrence is available in QDateTime 0109 // const bool conv = (!d->equalSpec(*other.d) || d->secondOccurrence() != other.d->secondOccurrence()); 0110 const bool conv = dt1.timeSpec() != dt2.timeSpec() || (dt1.timeSpec() == Qt::OffsetFromUTC && dt1.offsetFromUtc() != dt2.offsetFromUtc()) 0111 || (dt1.timeSpec() == Qt::TimeZone && dt1.timeZone() != dt2.timeZone()); 0112 if (conv) { 0113 // Different time specs or one is a time which occurs twice, 0114 // so convert to UTC before comparing 0115 start1 = dt1.toUTC(); 0116 start2 = dt2.toUTC(); 0117 } else { 0118 // Same time specs, so no need to convert to UTC 0119 start1 = dt1; 0120 start2 = dt2; 0121 } 0122 if (isAllDay1 || isAllDay2) { 0123 // At least one of the instances is date-only, so we need to compare 0124 // time periods rather than just times. 0125 QDateTime end1; 0126 QDateTime end2; 0127 if (conv) { 0128 if (isAllDay1) { 0129 QDateTime dt(dt1); 0130 dt.setTime(QTime(23, 59, 59, 999)); 0131 end1 = dt.toUTC(); 0132 } else { 0133 end1 = start1; 0134 } 0135 if (isAllDay2) { 0136 QDateTime dt(dt2); 0137 dt.setTime(QTime(23, 59, 59, 999)); 0138 end2 = dt.toUTC(); 0139 } else { 0140 end2 = start2; 0141 } 0142 } else { 0143 if (isAllDay1) { 0144 end1 = QDateTime(dt1.date(), QTime(23, 59, 59, 999), QTimeZone::LocalTime); 0145 } else { 0146 end1 = dt1; 0147 } 0148 if (isAllDay2) { 0149 end2 = QDateTime(dt2.date(), QTime(23, 59, 59, 999), QTimeZone::LocalTime); 0150 } else { 0151 end2 = dt2; 0152 } 0153 } 0154 0155 if (start1 == start2) { 0156 return !isAllDay1 ? AtStart 0157 : (end1 == end2) ? Equal 0158 : (end1 < end2) ? static_cast<DateTimeComparison>(AtStart | Inside) 0159 : static_cast<DateTimeComparison>(AtStart | Inside | AtEnd | After); 0160 } 0161 0162 if (start1 < start2) { 0163 return (end1 < start2) ? Before 0164 : (end1 == end2) ? static_cast<DateTimeComparison>(Before | AtStart | Inside | AtEnd) 0165 : (end1 == start2) ? static_cast<DateTimeComparison>(Before | AtStart) 0166 : (end1 < end2) ? static_cast<DateTimeComparison>(Before | AtStart | Inside) 0167 : Outside; 0168 } else { 0169 return (start1 > end2) ? After 0170 : (start1 == end2) ? (end1 == end2 ? AtEnd : static_cast<DateTimeComparison>(AtEnd | After)) 0171 : (end1 == end2) ? static_cast<DateTimeComparison>(Inside | AtEnd) 0172 : (end1 < end2) ? Inside 0173 : static_cast<DateTimeComparison>(Inside | AtEnd | After); 0174 } 0175 } 0176 return (start1 == start2) ? Equal : (start1 < start2) ? Before : After; 0177 } 0178 0179 bool KCalendarCore::Events::startDateLessThan(const Event::Ptr &e1, const Event::Ptr &e2) 0180 { 0181 DateTimeComparison res = compare(e1->dtStart(), e1->allDay(), e2->dtStart(), e2->allDay()); 0182 if (res == Equal) { 0183 return Events::summaryLessThan(e1, e2); 0184 } else { 0185 return (res & Before || res & AtStart); 0186 } 0187 } 0188 0189 bool KCalendarCore::Events::startDateMoreThan(const Event::Ptr &e1, const Event::Ptr &e2) 0190 { 0191 DateTimeComparison res = compare(e1->dtStart(), e1->allDay(), e2->dtStart(), e2->allDay()); 0192 if (res == Equal) { 0193 return Events::summaryMoreThan(e1, e2); 0194 } else { 0195 return (res & After || res & AtEnd); 0196 } 0197 } 0198 0199 bool KCalendarCore::Events::summaryLessThan(const Event::Ptr &e1, const Event::Ptr &e2) 0200 { 0201 return QString::compare(e1->summary(), e2->summary(), Qt::CaseInsensitive) < 0; 0202 } 0203 0204 bool KCalendarCore::Events::summaryMoreThan(const Event::Ptr &e1, const Event::Ptr &e2) 0205 { 0206 return QString::compare(e1->summary(), e2->summary(), Qt::CaseInsensitive) > 0; 0207 } 0208 0209 bool KCalendarCore::Events::endDateLessThan(const Event::Ptr &e1, const Event::Ptr &e2) 0210 { 0211 DateTimeComparison res = compare(e1->dtEnd(), e1->allDay(), e2->dtEnd(), e2->allDay()); 0212 if (res == Equal) { 0213 return Events::summaryLessThan(e1, e2); 0214 } else { 0215 return (res & Before || res & AtStart); 0216 } 0217 } 0218 0219 bool KCalendarCore::Events::endDateMoreThan(const Event::Ptr &e1, const Event::Ptr &e2) 0220 { 0221 DateTimeComparison res = compare(e1->dtEnd(), e1->allDay(), e2->dtEnd(), e2->allDay()); 0222 if (res == Equal) { 0223 return Events::summaryMoreThan(e1, e2); 0224 } else { 0225 return (res & After || res & AtEnd); 0226 } 0227 } 0228 0229 bool KCalendarCore::Journals::dateLessThan(const Journal::Ptr &j1, const Journal::Ptr &j2) 0230 { 0231 DateTimeComparison res = compare(j1->dtStart(), j1->allDay(), j2->dtStart(), j2->allDay()); 0232 return (res & Before || res & AtStart); 0233 } 0234 0235 bool KCalendarCore::Journals::dateMoreThan(const Journal::Ptr &j1, const Journal::Ptr &j2) 0236 { 0237 DateTimeComparison res = compare(j1->dtStart(), j1->allDay(), j2->dtStart(), j2->allDay()); 0238 return (res & After || res & AtEnd); 0239 } 0240 0241 bool KCalendarCore::Journals::summaryLessThan(const Journal::Ptr &j1, const Journal::Ptr &j2) 0242 { 0243 return QString::compare(j1->summary(), j2->summary(), Qt::CaseInsensitive) < 0; 0244 } 0245 0246 bool KCalendarCore::Journals::summaryMoreThan(const Journal::Ptr &j1, const Journal::Ptr &j2) 0247 { 0248 return QString::compare(j1->summary(), j2->summary(), Qt::CaseInsensitive) > 0; 0249 } 0250 0251 bool KCalendarCore::Todos::startDateLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0252 { 0253 DateTimeComparison res = compare(t1->dtStart(), t1->allDay(), t2->dtStart(), t2->allDay()); 0254 if (res == Equal) { 0255 return Todos::summaryLessThan(t1, t2); 0256 } else { 0257 return (res & Before || res & AtStart); 0258 } 0259 } 0260 0261 bool KCalendarCore::Todos::startDateMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0262 { 0263 DateTimeComparison res = compare(t1->dtStart(), t1->allDay(), t2->dtStart(), t2->allDay()); 0264 if (res == Equal) { 0265 return Todos::summaryMoreThan(t1, t2); 0266 } else { 0267 return (res & After || res & AtEnd); 0268 } 0269 } 0270 0271 bool KCalendarCore::Todos::dueDateLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0272 { 0273 if (!t1->hasDueDate() ) { 0274 return false; 0275 } 0276 if (!t2->hasDueDate()) { 0277 return true; 0278 } 0279 DateTimeComparison res = compare(t1->dtDue(), t1->allDay(), t2->dtDue(), t2->allDay()); 0280 if (res == Equal) { 0281 return Todos::summaryLessThan(t1, t2); 0282 } else { 0283 return (res & Before || res & AtStart); 0284 } 0285 } 0286 0287 bool KCalendarCore::Todos::dueDateMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0288 { 0289 if (!t2->hasDueDate()) { 0290 return false; 0291 } 0292 if (!t1->hasDueDate()) { 0293 return true; 0294 } 0295 DateTimeComparison res = compare(t1->dtDue(), t1->allDay(), t2->dtDue(), t2->allDay()); 0296 if (res == Equal) { 0297 return Todos::summaryMoreThan(t1, t2); 0298 } else { 0299 return (res & After || res & AtEnd); 0300 } 0301 } 0302 0303 bool KCalendarCore::Todos::priorityLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0304 { 0305 if (t1->priority() < t2->priority()) { 0306 return true; 0307 } else if (t1->priority() == t2->priority()) { 0308 return Todos::summaryLessThan(t1, t2); 0309 } else { 0310 return false; 0311 } 0312 } 0313 0314 bool KCalendarCore::Todos::priorityMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0315 { 0316 if (t1->priority() > t2->priority()) { 0317 return true; 0318 } else if (t1->priority() == t2->priority()) { 0319 return Todos::summaryMoreThan(t1, t2); 0320 } else { 0321 return false; 0322 } 0323 } 0324 0325 bool KCalendarCore::Todos::percentLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0326 { 0327 if (t1->percentComplete() < t2->percentComplete()) { 0328 return true; 0329 } else if (t1->percentComplete() == t2->percentComplete()) { 0330 return Todos::summaryLessThan(t1, t2); 0331 } else { 0332 return false; 0333 } 0334 } 0335 0336 bool KCalendarCore::Todos::percentMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0337 { 0338 if (t1->percentComplete() > t2->percentComplete()) { 0339 return true; 0340 } else if (t1->percentComplete() == t2->percentComplete()) { 0341 return Todos::summaryMoreThan(t1, t2); 0342 } else { 0343 return false; 0344 } 0345 } 0346 0347 bool KCalendarCore::Todos::summaryLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0348 { 0349 return QString::compare(t1->summary(), t2->summary(), Qt::CaseInsensitive) < 0; 0350 } 0351 0352 bool KCalendarCore::Todos::summaryMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0353 { 0354 return QString::compare(t1->summary(), t2->summary(), Qt::CaseInsensitive) > 0; 0355 } 0356 0357 bool KCalendarCore::Todos::createdLessThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0358 { 0359 DateTimeComparison res = compare(t1->created(), t1->allDay(), t2->created(), t2->allDay()); 0360 if (res == Equal) { 0361 return Todos::summaryLessThan(t1, t2); 0362 } else { 0363 return (res & Before || res & AtStart); 0364 } 0365 } 0366 0367 bool KCalendarCore::Todos::createdMoreThan(const Todo::Ptr &t1, const Todo::Ptr &t2) 0368 { 0369 DateTimeComparison res = compare(t1->created(), t1->allDay(), t2->created(), t2->allDay()); 0370 if (res == Equal) { 0371 return Todos::summaryMoreThan(t1, t2); 0372 } else { 0373 return (res & After || res & AtEnd); 0374 } 0375 } 0376 0377 bool KCalendarCore::Incidences::dateLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) 0378 { 0379 DateTimeComparison res = compare(i1->dateTime(Incidence::RoleSort), i1->allDay(), i2->dateTime(Incidence::RoleSort), i2->allDay()); 0380 if (res == Equal) { 0381 return Incidences::summaryLessThan(i1, i2); 0382 } else { 0383 return (res & Before || res & AtStart); 0384 } 0385 } 0386 0387 bool KCalendarCore::Incidences::dateMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) 0388 { 0389 DateTimeComparison res = compare(i1->dateTime(Incidence::RoleSort), i1->allDay(), i2->dateTime(Incidence::RoleSort), i2->allDay()); 0390 if (res == Equal) { 0391 return Incidences::summaryMoreThan(i1, i2); 0392 } else { 0393 return (res & After || res & AtEnd); 0394 } 0395 } 0396 0397 bool KCalendarCore::Incidences::createdLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) 0398 { 0399 DateTimeComparison res = compare(i1->created(), i1->allDay(), i2->created(), i2->allDay()); 0400 if (res == Equal) { 0401 return Incidences::summaryLessThan(i1, i2); 0402 } else { 0403 return (res & Before || res & AtStart); 0404 } 0405 } 0406 0407 bool KCalendarCore::Incidences::createdMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) 0408 { 0409 DateTimeComparison res = compare(i1->created(), i1->allDay(), i2->created(), i2->allDay()); 0410 if (res == Equal) { 0411 return Incidences::summaryMoreThan(i1, i2); 0412 } else { 0413 return (res & After || res & AtEnd); 0414 } 0415 } 0416 0417 bool KCalendarCore::Incidences::summaryLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) 0418 { 0419 return QString::compare(i1->summary(), i2->summary(), Qt::CaseInsensitive) < 0; 0420 } 0421 0422 bool KCalendarCore::Incidences::summaryMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) 0423 { 0424 return QString::compare(i1->summary(), i2->summary(), Qt::CaseInsensitive) > 0; 0425 } 0426 0427 bool KCalendarCore::Incidences::categoriesLessThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) 0428 { 0429 const auto res = QString::compare(i1->categoriesStr(), i2->categoriesStr(), Qt::CaseSensitive); 0430 if (res == 0) { 0431 return Incidences::summaryLessThan(i1, i2); 0432 } else { 0433 return res < 0; 0434 } 0435 } 0436 0437 bool KCalendarCore::Incidences::categoriesMoreThan(const Incidence::Ptr &i1, const Incidence::Ptr &i2) 0438 { 0439 const auto res = QString::compare(i1->categoriesStr(), i2->categoriesStr(), Qt::CaseSensitive); 0440 if (res == 0) { 0441 return Incidences::summaryMoreThan(i1, i2); 0442 } else { 0443 return res > 0; 0444 } 0445 }