File indexing completed on 2024-04-28 15:39:56
0001 // SPDX-FileCopyrightText: 2003-2010 Jesper K. Pedersen <blackie@kde.org> 0002 // SPDX-FileCopyrightText: 2022-2023 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0003 // 0004 // SPDX-License-Identifier: GPL-2.0-or-later 0005 0006 #include "ImageDate.h" 0007 0008 #include <KLocalizedString> 0009 #include <QDebug> 0010 #include <QLocale> 0011 #include <qregexp.h> 0012 0013 using namespace DB; 0014 0015 static const QTime _startOfDay_(0, 0, 0); 0016 static const QTime _endOfDay_(23, 59, 59); 0017 0018 namespace 0019 { 0020 0021 QStringList monthNames() 0022 { 0023 static QStringList res; 0024 if (res.isEmpty()) { 0025 for (int i = 1; i <= 12; ++i) { 0026 res << QLocale().standaloneMonthName(i, QLocale::ShortFormat); 0027 } 0028 for (int i = 1; i <= 12; ++i) { 0029 res << QLocale().standaloneMonthName(i, QLocale::LongFormat); 0030 } 0031 0032 res << i18nc("Abbreviated month name", "jan") << i18nc("Abbreviated month name", "feb") 0033 << i18nc("Abbreviated month name", "mar") << i18nc("Abbreviated month name", "apr") 0034 << i18nc("Abbreviated month name", "may") << i18nc("Abbreviated month name", "jun") 0035 << i18nc("Abbreviated month name", "jul") << i18nc("Abbreviated month name", "aug") 0036 << i18nc("Abbreviated month name", "sep") << i18nc("Abbreviated month name", "oct") 0037 << i18nc("Abbreviated month name", "nov") << i18nc("Abbreviated month name", "dec"); 0038 res << QString::fromLatin1("jan") << QString::fromLatin1("feb") << QString::fromLatin1("mar") << QString::fromLatin1("apr") 0039 << QString::fromLatin1("may") << QString::fromLatin1("jun") << QString::fromLatin1("jul") << QString::fromLatin1("aug") 0040 << QString::fromLatin1("sep") << QString::fromLatin1("oct") << QString::fromLatin1("nov") << QString::fromLatin1("dec"); 0041 0042 for (int i = 1; i <= 12; ++i) { 0043 res << QLocale().monthName(i, QLocale::ShortFormat); 0044 } 0045 for (int i = 1; i <= 12; ++i) { 0046 res << QLocale().monthName(i, QLocale::LongFormat); 0047 } 0048 0049 for (QStringList::iterator it = res.begin(); it != res.end(); ++it) 0050 *it = it->toLower(); 0051 } 0052 return res; 0053 } 0054 0055 QString formatRegexp() 0056 { 0057 static QString str; 0058 if (str.isEmpty()) { 0059 str = QString::fromLatin1("^((\\d\\d?)([-. /]+|$))?(("); 0060 QStringList months = monthNames(); 0061 for (QStringList::ConstIterator monthIt = months.constBegin(); monthIt != months.constEnd(); ++monthIt) 0062 str += QString::fromLatin1("%1|").arg(*monthIt); 0063 0064 str += QString::fromLatin1("\\d?\\d)([-. /]+|$))?(\\d\\d(\\d\\d)?)?$"); 0065 } 0066 return str; 0067 } 0068 0069 } // namespace 0070 0071 ImageDate::ImageDate(const QDate &date) 0072 : m_start(date, _startOfDay_) 0073 , m_end(m_start) 0074 { 0075 } 0076 0077 ImageDate::ImageDate(const Utilities::FastDateTime &date) 0078 : m_start(date) 0079 , m_end(date) 0080 { 0081 } 0082 0083 bool ImageDate::operator<=(const ImageDate &other) const 0084 { 0085 // This operator is used by QMap when checking for equal elements, thus we need the second part too. 0086 return m_start < other.m_start || (m_start == other.m_start && m_end <= other.m_end); 0087 } 0088 0089 ImageDate::ImageDate() 0090 : m_start() 0091 , m_end() 0092 { 0093 } 0094 0095 bool ImageDate::isNull() const 0096 { 0097 return m_start.isNull(); 0098 } 0099 0100 bool ImageDate::isFuzzy() const 0101 { 0102 return m_start != m_end; 0103 } 0104 0105 static bool isFirstSecOfMonth(const Utilities::FastDateTime &date) 0106 { 0107 return date.date().day() == 1 && date.time().hour() == 0 && date.time().minute() == 0; 0108 } 0109 0110 static bool isLastSecOfMonth(Utilities::FastDateTime date) 0111 { 0112 return isFirstSecOfMonth(date.addSecs(1)); 0113 } 0114 0115 static bool isFirstSecOfDay(const Utilities::FastDateTime &time) 0116 { 0117 return time.time().hour() == 0 && time.time().minute() == 0 && time.time().second() == 0; 0118 } 0119 0120 static bool isLastSecOfDay(const Utilities::FastDateTime &time) 0121 { 0122 return time.time().hour() == 23 && time.time().minute() == 59 && time.time().second() == 59; 0123 } 0124 0125 QString ImageDate::toString(bool withTime) const 0126 { 0127 if (m_start.isNull()) 0128 return QString(); 0129 0130 if (m_start == m_end) { 0131 if (withTime && !isFirstSecOfDay(m_start)) 0132 return m_start.toString(QString::fromLatin1("d. MMM yyyy hh:mm:ss")); 0133 else 0134 return m_start.toString(QString::fromLatin1("d. MMM yyyy")); 0135 } 0136 0137 // start is different from end. 0138 if (isFirstSecOfMonth(m_start) && isLastSecOfMonth(m_end)) { 0139 if (m_start.date().month() == 1 && m_end.date().month() == 12) { 0140 if (m_start.date().year() == m_end.date().year()) { 0141 // 2005 0142 return QString::number(m_start.date().year()); 0143 } else { 0144 // 2005-2006 0145 return QString::fromLatin1("%1 - %2").arg(m_start.date().year()).arg(m_end.date().year()); 0146 } 0147 } else { 0148 // a whole month, but not a whole year. 0149 if (m_start.date().year() == m_end.date().year() && m_start.date().month() == m_end.date().month()) { 0150 // jan 2005 0151 return QString::fromLatin1("%1 %2") 0152 .arg(QLocale().standaloneMonthName(m_start.date().month(), QLocale::ShortFormat)) 0153 .arg(m_start.date().year()); 0154 } else { 0155 // jan 2005 - feb 2006 0156 return QString::fromLatin1("%1 %2 - %3 %4") 0157 .arg(QLocale().standaloneMonthName(m_start.date().month(), QLocale::ShortFormat)) 0158 .arg(m_start.date().year()) 0159 .arg(QLocale().standaloneMonthName(m_end.date().month(), QLocale::ShortFormat)) 0160 .arg(m_end.date().year()); 0161 } 0162 } 0163 } 0164 0165 if (!withTime || (isFirstSecOfDay(m_start) && isLastSecOfDay(m_end))) { 0166 if (m_start.date() == m_end.date()) { 0167 // A whole day 0168 return m_start.toString(QString::fromLatin1("d. MMM yyyy")); 0169 } else { 0170 // A day range 0171 return QString::fromLatin1("%1 - %2") 0172 .arg(m_start.toString(QString::fromLatin1("d. MMM yyyy")), m_end.toString(QString::fromLatin1("d. MMM yyyy"))); 0173 } 0174 } 0175 0176 // Range smaller than one day. 0177 if (withTime && (!isFirstSecOfDay(m_start) || !isLastSecOfDay(m_end))) 0178 return QString::fromLatin1("%1 - %2") 0179 .arg(m_start.toString(QString::fromLatin1("d. MMM yyyy hh:mm")), m_end.toString(QString::fromLatin1("d. MMM yyyy hh:mm"))); 0180 else 0181 return QString::fromLatin1("%1 - %2") 0182 .arg(m_start.toString(QString::fromLatin1("d. MMM yyyy")), m_end.toString(QString::fromLatin1("d. MMM yyyy"))); 0183 } 0184 0185 bool ImageDate::operator==(const ImageDate &other) const 0186 { 0187 return m_start == other.m_start && m_end == other.m_end; 0188 } 0189 0190 bool ImageDate::operator!=(const ImageDate &other) const 0191 { 0192 return !(*this == other); 0193 } 0194 0195 bool ImageDate::operator<(const ImageDate &other) const 0196 { 0197 return start() < other.start() || (start() == other.start() && end() < other.end()); 0198 } 0199 0200 ImageDate::ImageDate(const Utilities::FastDateTime &start, const Utilities::FastDateTime &end) 0201 { 0202 if (!start.isValid() || !end.isValid() || start <= end) { 0203 m_start = start; 0204 m_end = end; 0205 } else { 0206 m_start = end; 0207 m_end = start; 0208 } 0209 } 0210 0211 ImageDate::ImageDate(const QDate &start, const QDate &end) 0212 { 0213 if (!start.isValid() || !end.isValid() || start <= end) { 0214 m_start = Utilities::FastDateTime(start, _startOfDay_); 0215 m_end = Utilities::FastDateTime(end, _endOfDay_); 0216 } else { 0217 m_start = Utilities::FastDateTime(end, _startOfDay_); 0218 m_end = Utilities::FastDateTime(start, _endOfDay_); 0219 } 0220 } 0221 0222 static QDate addMonth(int year, int month) 0223 { 0224 if (month == 12) { 0225 year++; 0226 month = 1; 0227 } else 0228 month++; 0229 return QDate(year, month, 1); 0230 } 0231 0232 ImageDate::ImageDate(int yearFrom, int monthFrom, int dayFrom, int yearTo, int monthTo, int dayTo, int hourFrom, int minuteFrom, int secondFrom) 0233 { 0234 if (yearFrom <= 0) { 0235 m_start = Utilities::FastDateTime(); 0236 m_end = Utilities::FastDateTime(); 0237 return; 0238 } 0239 0240 if (monthFrom <= 0) { 0241 m_start = QDate(yearFrom, 1, 1).startOfDay(); 0242 m_end = QDate(yearFrom + 1, 1, 1).startOfDay().addSecs(-1); 0243 } else if (dayFrom <= 0) { 0244 m_start = QDate(yearFrom, monthFrom, 1).startOfDay(); 0245 m_end = addMonth(yearFrom, monthFrom).startOfDay().addSecs(-1); 0246 } else if (hourFrom < 0) { 0247 m_start = QDate(yearFrom, monthFrom, dayFrom).startOfDay(); 0248 m_end = QDate(yearFrom, monthFrom, dayFrom).addDays(1).startOfDay().addSecs(-1); 0249 } else if (minuteFrom < 0) { 0250 m_start = Utilities::FastDateTime(QDate(yearFrom, monthFrom, dayFrom), QTime(hourFrom, 0, 0)); 0251 m_end = Utilities::FastDateTime(QDate(yearFrom, monthFrom, dayFrom), QTime(hourFrom, 23, 59)); 0252 } else if (secondFrom < 0) { 0253 m_start = Utilities::FastDateTime(QDate(yearFrom, monthFrom, dayFrom), QTime(hourFrom, minuteFrom, 0)); 0254 m_end = Utilities::FastDateTime(QDate(yearFrom, monthFrom, dayFrom), QTime(hourFrom, minuteFrom, 59)); 0255 } else { 0256 m_start = Utilities::FastDateTime(QDate(yearFrom, monthFrom, dayFrom), QTime(hourFrom, minuteFrom, secondFrom)); 0257 m_end = m_start; 0258 } 0259 0260 if (yearTo > 0) { 0261 m_end = QDate(yearTo + 1, 1, 1).startOfDay().addSecs(-1); 0262 0263 if (monthTo > 0) { 0264 m_end = addMonth(yearTo, monthTo).startOfDay().addSecs(-1); 0265 0266 if (dayTo > 0) { 0267 if (dayFrom == dayTo && monthFrom == monthTo && yearFrom == yearTo) 0268 m_end = m_start; 0269 else 0270 m_end = QDate(yearTo, monthTo, dayTo).addDays(1).startOfDay().addSecs(-1); 0271 } 0272 } 0273 // It should not be possible here for m_end < m_start. 0274 Q_ASSERT(m_start <= m_end); 0275 } 0276 } 0277 0278 bool ImageDate::hasValidTime() const 0279 { 0280 return m_start == m_end; 0281 } 0282 0283 ImageDate::ImageDate(const QDate &start, const QDate &end, const QTime &time) 0284 { 0285 const QDate validatedEnd = (end.isValid()) ? end : start; 0286 0287 if (start == validatedEnd && time.isValid()) { 0288 m_start = Utilities::FastDateTime(start, time); 0289 m_end = m_start; 0290 } else { 0291 if (start > validatedEnd) { 0292 m_end = Utilities::FastDateTime(start, _startOfDay_); 0293 m_start = Utilities::FastDateTime(validatedEnd, _endOfDay_); 0294 } else { 0295 m_start = Utilities::FastDateTime(start, _startOfDay_); 0296 m_end = Utilities::FastDateTime(validatedEnd, _endOfDay_); 0297 } 0298 } 0299 } 0300 0301 ImageDate::MatchType ImageDate::isIncludedIn(const ImageDate &searchRange) const 0302 { 0303 if (searchRange.start() <= start() && searchRange.end() >= end()) 0304 return MatchType::IsContained; 0305 0306 if (searchRange.start() <= end() && searchRange.end() >= start()) { 0307 return MatchType::Overlap; 0308 } 0309 return MatchType::NoMatch; 0310 } 0311 0312 bool ImageDate::includes(const Utilities::FastDateTime &date) const 0313 { 0314 return ImageDate(date).isIncludedIn(*this) == MatchType::IsContained; 0315 } 0316 0317 void ImageDate::extendTo(const ImageDate &other) 0318 { 0319 if (other.isNull()) 0320 return; 0321 0322 if (isNull()) { 0323 m_start = other.m_start; 0324 m_end = other.m_end; 0325 } else { 0326 if (other.m_start < m_start) 0327 m_start = other.m_start; 0328 if (other.m_end > m_end) 0329 m_end = other.m_end; 0330 } 0331 } 0332 0333 QDate DB::parseDateString(const QString &dateString, bool assumeStartDate) 0334 { 0335 QRegExp regexp(formatRegexp(), Qt::CaseInsensitive); 0336 0337 if (regexp.exactMatch(dateString)) { 0338 int year = 0; 0339 int month = 0; 0340 int day = 0; 0341 0342 QString dayStr = regexp.cap(2); 0343 QString monthStr = regexp.cap(5).toLower(); 0344 QString yearStr = regexp.cap(7); 0345 0346 if (dayStr.length() != 0) 0347 day = dayStr.toInt(); 0348 0349 if (yearStr.length() != 0) { 0350 year = yearStr.toInt(); 0351 if (year < 50) 0352 year += 2000; 0353 if (year < 100) 0354 year += 1900; 0355 } 0356 if (monthStr.length() != 0) { 0357 int index = monthNames().indexOf(monthStr); 0358 if (index != -1) 0359 month = (index % 12) + 1; 0360 else 0361 month = monthStr.toInt(); 0362 } 0363 if (year == 0) 0364 year = QDate::currentDate().year(); 0365 if (month == 0) { 0366 if (assumeStartDate) { 0367 month = 1; 0368 day = 1; 0369 } else { 0370 month = 12; 0371 day = 31; 0372 } 0373 } else if (day == 0) { 0374 if (assumeStartDate) 0375 day = 1; 0376 else 0377 day = QDate(year, month, 1).daysInMonth(); 0378 } 0379 return QDate(year, month, day); 0380 } else 0381 return QDate(); 0382 } 0383 0384 QDebug operator<<(QDebug debug, const DB::ImageDate &d) 0385 { 0386 QDebugStateSaver saveState(debug); 0387 0388 if (d.isNull()) { 0389 debug << "DB::ImageDate()"; 0390 } else if (d.isFuzzy()) { 0391 debug.nospace() << "DB::ImageDate(" << d.start().date().toString(Qt::ISODate) << ", " << d.end().date().toString(Qt::ISODate) << ")"; 0392 } else { 0393 debug.nospace() << "DB::ImageDate(" << d.start().date().toString(Qt::ISODate) << ")"; 0394 } 0395 return debug; 0396 } 0397 0398 // vi:expandtab:tabstop=4 shiftwidth=4: