KDECore
kdatetime.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 2005-2011 David Jarvie <djarvie@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "kdatetime.h" 00022 00023 #include <config.h> 00024 00025 #ifdef HAVE_SYS_TIME_H 00026 #include <sys/time.h> 00027 #endif 00028 #ifdef HAVE_TIME_H 00029 #include <time.h> 00030 #endif 00031 #include <stdlib.h> 00032 #include <stdio.h> 00033 #include <ctype.h> 00034 00035 #include <QtCore/QDateTime> 00036 #include <QtCore/QRegExp> 00037 #include <QtCore/QStringList> 00038 #include <QtCore/QSharedData> 00039 00040 #include <kglobal.h> 00041 #include <klocale.h> 00042 #include "kcalendarsystemgregorian_p.h" 00043 #include <ksystemtimezone.h> 00044 #include <kdebug.h> 00045 00046 #ifdef Q_OS_WIN 00047 #include <windows.h> // SYSTEMTIME 00048 #endif 00049 00050 00051 static const char shortDay[][4] = { 00052 "Mon", "Tue", "Wed", 00053 "Thu", "Fri", "Sat", 00054 "Sun" 00055 }; 00056 static const char longDay[][10] = { 00057 "Monday", "Tuesday", "Wednesday", 00058 "Thursday", "Friday", "Saturday", 00059 "Sunday" 00060 }; 00061 static const char shortMonth[][4] = { 00062 "Jan", "Feb", "Mar", "Apr", 00063 "May", "Jun", "Jul", "Aug", 00064 "Sep", "Oct", "Nov", "Dec" 00065 }; 00066 static const char longMonth[][10] = { 00067 "January", "February", "March", 00068 "April", "May", "June", 00069 "July", "August", "September", 00070 "October", "November", "December" 00071 }; 00072 00073 00074 // The reason for the KDateTime being invalid, returned from KDateTime::fromString() 00075 enum Status { 00076 stValid = 0, // either valid, or really invalid 00077 stTooEarly // invalid (valid date before QDate range) 00078 }; 00079 00080 00081 static QDateTime fromStr(const QString& string, const QString& format, int& utcOffset, 00082 QString& zoneName, QByteArray& zoneAbbrev, bool& dateOnly, Status&); 00083 static int matchDay(const QString &string, int &offset, KCalendarSystem*); 00084 static int matchMonth(const QString &string, int &offset, KCalendarSystem*); 00085 static bool getUTCOffset(const QString &string, int &offset, bool colon, int &result); 00086 static int getAmPm(const QString &string, int &offset, KLocale*); 00087 static bool getNumber(const QString &string, int &offset, int mindigits, int maxdigits, int minval, int maxval, int &result); 00088 static int findString_internal(const QString &string, const char *ptr, int count, int &offset, int disp); 00089 template<int disp> static inline 00090 int findString(const QString &string, const char array[][disp], int count, int &offset) 00091 { return findString_internal(string, array[0], count, offset, disp); } 00092 static QDate checkDate(int year, int month, int day, Status&); 00093 00094 static const int MIN_YEAR = -4712; // minimum year which QDate allows 00095 static const int NO_NUMBER = 0x8000000; // indicates that no number is present in string conversion functions 00096 00097 #ifdef COMPILING_TESTS 00098 KDECORE_EXPORT int KDateTime_utcCacheHit = 0; 00099 KDECORE_EXPORT int KDateTime_zoneCacheHit = 0; 00100 #endif 00101 00102 /*----------------------------------------------------------------------------*/ 00103 00104 class KDateTimeSpecPrivate 00105 { 00106 public: 00107 KDateTimeSpecPrivate() : utcOffset(0) {} 00108 // *** NOTE: This structure is replicated in KDateTimePrivate. Any changes must be copied there. 00109 KTimeZone tz; // if type == TimeZone, the instance's time zone. 00110 int utcOffset; // if type == OffsetFromUTC, the offset from UTC 00111 KDateTime::SpecType type; // time spec type 00112 }; 00113 00114 00115 KDateTime::Spec::Spec() 00116 : d(new KDateTimeSpecPrivate) 00117 { 00118 d->type = KDateTime::Invalid; 00119 } 00120 00121 KDateTime::Spec::Spec(const KTimeZone &tz) 00122 : d(new KDateTimeSpecPrivate()) 00123 { 00124 setType(tz); 00125 } 00126 00127 KDateTime::Spec::Spec(SpecType type, int utcOffset) 00128 : d(new KDateTimeSpecPrivate()) 00129 { 00130 setType(type, utcOffset); 00131 } 00132 00133 KDateTime::Spec::Spec(const Spec& spec) 00134 : d(new KDateTimeSpecPrivate()) 00135 { 00136 operator=(spec); 00137 } 00138 00139 KDateTime::Spec::~Spec() 00140 { 00141 delete d; 00142 } 00143 00144 KDateTime::Spec &KDateTime::Spec::operator=(const Spec& spec) 00145 { 00146 if (&spec != this) 00147 { 00148 d->type = spec.d->type; 00149 if (d->type == KDateTime::TimeZone) 00150 d->tz = spec.d->tz; 00151 else if (d->type == KDateTime::OffsetFromUTC) 00152 d->utcOffset = spec.d->utcOffset; 00153 } 00154 return *this; 00155 } 00156 00157 void KDateTime::Spec::setType(SpecType type, int utcOffset) 00158 { 00159 switch (type) 00160 { 00161 case KDateTime::OffsetFromUTC: 00162 d->utcOffset = utcOffset; 00163 // fall through to UTC 00164 case KDateTime::UTC: 00165 case KDateTime::ClockTime: 00166 d->type = type; 00167 break; 00168 case KDateTime::LocalZone: 00169 d->tz = KSystemTimeZones::local(); 00170 d->type = KDateTime::TimeZone; 00171 break; 00172 case KDateTime::TimeZone: 00173 default: 00174 d->type = KDateTime::Invalid; 00175 break; 00176 } 00177 } 00178 00179 void KDateTime::Spec::setType(const KTimeZone &tz) 00180 { 00181 if (tz == KTimeZone::utc()) 00182 d->type = KDateTime::UTC; 00183 else if (tz.isValid()) 00184 { 00185 d->type = KDateTime::TimeZone; 00186 d->tz = tz; 00187 } 00188 else 00189 d->type = KDateTime::Invalid; 00190 } 00191 00192 KTimeZone KDateTime::Spec::timeZone() const 00193 { 00194 if (d->type == KDateTime::TimeZone) 00195 return d->tz; 00196 if (d->type == KDateTime::UTC) 00197 return KTimeZone::utc(); 00198 return KTimeZone(); 00199 } 00200 00201 bool KDateTime::Spec::isUtc() const 00202 { 00203 if (d->type == KDateTime::UTC 00204 || (d->type == KDateTime::OffsetFromUTC && d->utcOffset == 0)) 00205 return true; 00206 return false; 00207 } 00208 00209 KDateTime::Spec KDateTime::Spec::UTC() { return Spec(KDateTime::UTC); } 00210 KDateTime::Spec KDateTime::Spec::ClockTime() { return Spec(KDateTime::ClockTime); } 00211 KDateTime::Spec KDateTime::Spec::LocalZone() { return Spec(KDateTime::LocalZone); } 00212 KDateTime::Spec KDateTime::Spec::OffsetFromUTC(int utcOffset) { return Spec(KDateTime::OffsetFromUTC, utcOffset); } 00213 KDateTime::SpecType KDateTime::Spec::type() const { return d->type; } 00214 bool KDateTime::Spec::isValid() const { return d->type != KDateTime::Invalid; } 00215 bool KDateTime::Spec::isLocalZone() const { return d->type == KDateTime::TimeZone && d->tz == KSystemTimeZones::local(); } 00216 bool KDateTime::Spec::isClockTime() const { return d->type == KDateTime::ClockTime; } 00217 bool KDateTime::Spec::isOffsetFromUtc() const { return d->type == KDateTime::OffsetFromUTC; } 00218 int KDateTime::Spec::utcOffset() const { return d->type == KDateTime::OffsetFromUTC ? d->utcOffset : 0; } 00219 00220 bool KDateTime::Spec::operator==(const Spec &other) const 00221 { 00222 if (d->type != other.d->type 00223 || (d->type == KDateTime::TimeZone && d->tz != other.d->tz) 00224 || (d->type == KDateTime::OffsetFromUTC && d->utcOffset != other.d->utcOffset)) 00225 return false; 00226 return true; 00227 } 00228 00229 bool KDateTime::Spec::equivalentTo(const Spec &other) const 00230 { 00231 if (d->type == other.d->type) 00232 { 00233 if ((d->type == KDateTime::TimeZone && d->tz != other.d->tz) 00234 || (d->type == KDateTime::OffsetFromUTC && d->utcOffset != other.d->utcOffset)) 00235 return false; 00236 return true; 00237 } 00238 else 00239 { 00240 if ((d->type == KDateTime::UTC && other.d->type == KDateTime::OffsetFromUTC && other.d->utcOffset == 0) 00241 || (other.d->type == KDateTime::UTC && d->type == KDateTime::OffsetFromUTC && d->utcOffset == 0)) 00242 return true; 00243 return false; 00244 } 00245 } 00246 00247 QDataStream & operator<<(QDataStream &s, const KDateTime::Spec &spec) 00248 { 00249 // The specification type is encoded in order to insulate from changes 00250 // to the SpecType enum. 00251 switch (spec.type()) 00252 { 00253 case KDateTime::UTC: 00254 s << static_cast<quint8>('u'); 00255 break; 00256 case KDateTime::OffsetFromUTC: 00257 s << static_cast<quint8>('o') << spec.utcOffset(); 00258 break; 00259 case KDateTime::TimeZone: 00260 s << static_cast<quint8>('z') << (spec.timeZone().isValid() ? spec.timeZone().name() : QString()); 00261 break; 00262 case KDateTime::ClockTime: 00263 s << static_cast<quint8>('c'); 00264 break; 00265 case KDateTime::Invalid: 00266 default: 00267 s << static_cast<quint8>(' '); 00268 break; 00269 } 00270 return s; 00271 } 00272 00273 QDataStream & operator>>(QDataStream &s, KDateTime::Spec &spec) 00274 { 00275 // The specification type is encoded in order to insulate from changes 00276 // to the SpecType enum. 00277 quint8 t; 00278 s >> t; 00279 switch (static_cast<char>(t)) 00280 { 00281 case 'u': 00282 spec.setType(KDateTime::UTC); 00283 break; 00284 case 'o': 00285 { 00286 int utcOffset; 00287 s >> utcOffset; 00288 spec.setType(KDateTime::OffsetFromUTC, utcOffset); 00289 break; 00290 } 00291 case 'z': 00292 { 00293 QString zone; 00294 s >> zone; 00295 KTimeZone tz = KSystemTimeZones::zone(zone); 00296 spec.setType(tz); 00297 break; 00298 } 00299 case 'c': 00300 spec.setType(KDateTime::ClockTime); 00301 break; 00302 default: 00303 spec.setType(KDateTime::Invalid); 00304 break; 00305 } 00306 return s; 00307 } 00308 00309 00310 /*----------------------------------------------------------------------------*/ 00311 00312 K_GLOBAL_STATIC_WITH_ARGS(KDateTime::Spec, s_fromStringDefault, (KDateTime::ClockTime)) 00313 00314 class KDateTimePrivate : public QSharedData 00315 { 00316 public: 00317 KDateTimePrivate() 00318 : QSharedData(), 00319 specType(KDateTime::Invalid), 00320 status(stValid), 00321 utcCached(true), 00322 convertedCached(false), 00323 m2ndOccurrence(false), 00324 mDateOnly(false) 00325 { 00326 } 00327 00328 KDateTimePrivate(const QDateTime &d, const KDateTime::Spec &s, bool donly = false) 00329 : QSharedData(), 00330 mDt(d), 00331 specType(s.type()), 00332 status(stValid), 00333 utcCached(false), 00334 convertedCached(false), 00335 m2ndOccurrence(false), 00336 mDateOnly(donly) 00337 { 00338 switch (specType) 00339 { 00340 case KDateTime::TimeZone: 00341 specZone = s.timeZone(); 00342 break; 00343 case KDateTime::OffsetFromUTC: 00344 specUtcOffset= s.utcOffset(); 00345 break; 00346 case KDateTime::Invalid: 00347 utcCached = true; 00348 // fall through to UTC 00349 case KDateTime::UTC: 00350 default: 00351 break; 00352 } 00353 } 00354 00355 KDateTimePrivate(const KDateTimePrivate &rhs) 00356 : QSharedData(rhs), 00357 mDt(rhs.mDt), 00358 specZone(rhs.specZone), 00359 specUtcOffset(rhs.specUtcOffset), 00360 ut(rhs.ut), 00361 converted(rhs.converted), 00362 specType(rhs.specType), 00363 status(rhs.status), 00364 utcCached(rhs.utcCached), 00365 convertedCached(rhs.convertedCached), 00366 m2ndOccurrence(rhs.m2ndOccurrence), 00367 mDateOnly(rhs.mDateOnly), 00368 converted2ndOccur(rhs.converted2ndOccur) 00369 {} 00370 00371 ~KDateTimePrivate() {} 00372 const QDateTime& dt() const { return mDt; } 00373 const QDate date() const { return mDt.date(); } 00374 KDateTime::Spec spec() const; 00375 QDateTime utc() const { return QDateTime(ut.date, ut.time, Qt::UTC); } 00376 bool dateOnly() const { return mDateOnly; } 00377 bool secondOccurrence() const { return m2ndOccurrence; } 00378 void setDt(const QDateTime &dt) { mDt = dt; utcCached = convertedCached = m2ndOccurrence = false; } 00379 void setDtFromUtc(const QDateTime &utcdt); 00380 void setDate(const QDate &d) { mDt.setDate(d); utcCached = convertedCached = m2ndOccurrence = false; } 00381 void setTime(const QTime &t) { mDt.setTime(t); utcCached = convertedCached = mDateOnly = m2ndOccurrence = false; } 00382 void setDtTimeSpec(Qt::TimeSpec s) { mDt.setTimeSpec(s); utcCached = convertedCached = m2ndOccurrence = false; } 00383 void setSpec(const KDateTime::Spec&); 00384 void setDateOnly(bool d); 00385 int timeZoneOffset() const; 00386 QDateTime toUtc(const KTimeZone &local = KTimeZone()) const; 00387 QDateTime toZone(const KTimeZone &zone, const KTimeZone &local = KTimeZone()) const; 00388 void newToZone(KDateTimePrivate *newd, const KTimeZone &zone, const KTimeZone &local = KTimeZone()) const; 00389 bool equalSpec(const KDateTimePrivate&) const; 00390 void clearCache() { utcCached = convertedCached = false; } 00391 void setDt(const QDateTime &dt, const QDateTime &utcDt) 00392 { 00393 mDt = dt; 00394 ut.date = utcDt.date(); 00395 ut.time = utcDt.time(); 00396 utcCached = true; 00397 convertedCached = false; 00398 m2ndOccurrence = false; 00399 } 00400 void setUtc(const QDateTime &dt) const 00401 { 00402 ut.date = dt.date(); 00403 ut.time = dt.time(); 00404 utcCached = true; 00405 convertedCached = false; 00406 } 00407 00408 /* Initialise the date/time for specType = UTC, from a time zone time, 00409 * and cache the time zone time. 00410 */ 00411 void setUtcFromTz(const QDateTime &dt, const KTimeZone &tz) 00412 { 00413 if (specType == KDateTime::UTC) 00414 { 00415 mDt = tz.toUtc(dt); 00416 utcCached = false; 00417 converted.date = dt.date(); 00418 converted.time = dt.time(); 00419 converted.tz = tz; 00420 convertedCached = true; 00421 converted2ndOccur = false; // KTimeZone::toUtc() returns the first occurrence 00422 } 00423 } 00424 00425 // Default time spec used by fromString() 00426 static KDateTime::Spec& fromStringDefault() 00427 { 00428 return *s_fromStringDefault; 00429 } 00430 00431 00432 static QTime sod; // start of day (00:00:00) 00433 #ifndef NDEBUG 00434 static qint64 currentDateTimeOffset; // offset to apply to current system time 00435 #endif 00436 00437 /* Because some applications create thousands of instances of KDateTime, this 00438 * data structure is designed to minimize memory usage. Ensure that all small 00439 * members are kept together at the end! 00440 */ 00441 private: 00442 QDateTime mDt; 00443 public: 00444 KTimeZone specZone; // if specType == TimeZone, the instance's time zone 00445 // if specType == ClockTime, the local time zone used to calculate the cached UTC time (mutable) 00446 int specUtcOffset; // if specType == OffsetFromUTC, the offset from UTC 00447 mutable struct ut { // cached UTC equivalent of 'mDt'. Saves space compared to storing QDateTime. 00448 QDate date; 00449 QTime time; 00450 } ut; 00451 private: 00452 mutable struct converted { // cached conversion to another time zone (if 'tz' is valid) 00453 QDate date; 00454 QTime time; 00455 KTimeZone tz; 00456 } converted; 00457 public: 00458 KDateTime::SpecType specType : 4; // time spec type (N.B. need 3 bits + sign bit, since enums are signed on some platforms) 00459 Status status : 2; // reason for invalid status 00460 mutable bool utcCached : 1; // true if 'ut' is valid 00461 mutable bool convertedCached : 1; // true if 'converted' is valid 00462 mutable bool m2ndOccurrence : 1; // this is the second occurrence of a time zone time 00463 private: 00464 bool mDateOnly : 1; // true to ignore the time part 00465 mutable bool converted2ndOccur : 1; // this is the second occurrence of 'converted' time 00466 }; 00467 00468 00469 QTime KDateTimePrivate::sod(0,0,0); 00470 #ifndef NDEBUG 00471 qint64 KDateTimePrivate::currentDateTimeOffset = 0; 00472 #endif 00473 00474 KDateTime::Spec KDateTimePrivate::spec() const 00475 { 00476 if (specType == KDateTime::TimeZone) 00477 return KDateTime::Spec(specZone); 00478 else 00479 return KDateTime::Spec(specType, specUtcOffset); 00480 } 00481 00482 void KDateTimePrivate::setSpec(const KDateTime::Spec &other) 00483 { 00484 if (specType == other.type()) 00485 { 00486 switch (specType) 00487 { 00488 case KDateTime::TimeZone: 00489 { 00490 KTimeZone tz = other.timeZone(); 00491 if (specZone == tz) 00492 return; 00493 specZone = tz; 00494 break; 00495 } 00496 case KDateTime::OffsetFromUTC: 00497 { 00498 int offset = other.utcOffset(); 00499 if (specUtcOffset == offset) 00500 return; 00501 specUtcOffset = offset; 00502 break; 00503 } 00504 default: 00505 return; 00506 } 00507 utcCached = false; 00508 } 00509 else 00510 { 00511 specType = other.type(); 00512 switch (specType) 00513 { 00514 case KDateTime::TimeZone: 00515 specZone = other.timeZone(); 00516 break; 00517 case KDateTime::OffsetFromUTC: 00518 specUtcOffset = other.utcOffset(); 00519 break; 00520 case KDateTime::Invalid: 00521 ut.date = QDate(); // cache an invalid UTC value 00522 utcCached = true; 00523 // fall through to UTC 00524 case KDateTime::UTC: 00525 default: 00526 break; 00527 } 00528 } 00529 convertedCached = false; 00530 setDtTimeSpec((specType == KDateTime::UTC) ? Qt::UTC : Qt::LocalTime); // this clears cached UTC value 00531 } 00532 00533 bool KDateTimePrivate::equalSpec(const KDateTimePrivate &other) const 00534 { 00535 if (specType != other.specType 00536 || (specType == KDateTime::TimeZone && specZone != other.specZone) 00537 || (specType == KDateTime::OffsetFromUTC && specUtcOffset != other.specUtcOffset)) 00538 return false; 00539 return true; 00540 } 00541 00542 void KDateTimePrivate::setDateOnly(bool dateOnly) 00543 { 00544 if (dateOnly != mDateOnly) 00545 { 00546 mDateOnly = dateOnly; 00547 if (dateOnly && mDt.time() != sod) 00548 { 00549 mDt.setTime(sod); 00550 utcCached = false; 00551 convertedCached = false; 00552 } 00553 m2ndOccurrence = false; 00554 } 00555 } 00556 00557 /* Sets the date/time to a given UTC date/time. The time spec is not changed. */ 00558 void KDateTimePrivate::setDtFromUtc(const QDateTime &utcdt) 00559 { 00560 switch (specType) 00561 { 00562 case KDateTime::UTC: 00563 setDt(utcdt); 00564 break; 00565 case KDateTime::OffsetFromUTC: 00566 { 00567 QDateTime local = utcdt.addSecs(specUtcOffset); 00568 local.setTimeSpec(Qt::LocalTime); 00569 setDt(local, utcdt); 00570 break; 00571 } 00572 case KDateTime::TimeZone: 00573 { 00574 bool second; 00575 setDt(specZone.toZoneTime(utcdt, &second), utcdt); 00576 m2ndOccurrence = second; 00577 break; 00578 } 00579 case KDateTime::ClockTime: 00580 specZone = KSystemTimeZones::local(); 00581 setDt(specZone.toZoneTime(utcdt), utcdt); 00582 break; 00583 default: // invalid 00584 break; 00585 } 00586 } 00587 00588 /* 00589 * Returns the UTC offset for the date/time, provided that it is a time zone type. 00590 */ 00591 int KDateTimePrivate::timeZoneOffset() const 00592 { 00593 if (specType != KDateTime::TimeZone) 00594 return KTimeZone::InvalidOffset; 00595 if (utcCached) 00596 { 00597 QDateTime dt = mDt; 00598 dt.setTimeSpec(Qt::UTC); 00599 return utc().secsTo(dt); 00600 } 00601 int secondOffset; 00602 if (!specZone.isValid()) { 00603 return KTimeZone::InvalidOffset; 00604 } 00605 int offset = specZone.offsetAtZoneTime(mDt, &secondOffset); 00606 if (m2ndOccurrence) 00607 { 00608 m2ndOccurrence = (secondOffset != offset); // cancel "second occurrence" flag if not applicable 00609 offset = secondOffset; 00610 } 00611 if (offset == KTimeZone::InvalidOffset) 00612 { 00613 ut.date = QDate(); 00614 utcCached = true; 00615 convertedCached = false; 00616 } 00617 else 00618 { 00619 // Calculate the UTC time from the offset and cache it 00620 QDateTime utcdt = mDt; 00621 utcdt.setTimeSpec(Qt::UTC); 00622 setUtc(utcdt.addSecs(-offset)); 00623 } 00624 return offset; 00625 } 00626 00627 /* 00628 * Returns the date/time converted to UTC. 00629 * Depending on which KTimeZone class is involved, conversion to UTC may require 00630 * significant calculation, so the calculated UTC value is cached. 00631 */ 00632 QDateTime KDateTimePrivate::toUtc(const KTimeZone &local) const 00633 { 00634 KTimeZone loc(local); 00635 if (utcCached) 00636 { 00637 // Return cached UTC value 00638 if (specType == KDateTime::ClockTime) 00639 { 00640 // ClockTime uses the dynamic current local system time zone. 00641 // Check for a time zone change before using the cached UTC value. 00642 if (!local.isValid()) 00643 loc = KSystemTimeZones::local(); 00644 if (specZone == loc) 00645 { 00646 // kDebug() << "toUtc(): cached -> " << utc() << endl, 00647 #ifdef COMPILING_TESTS 00648 ++KDateTime_utcCacheHit; 00649 #endif 00650 return utc(); 00651 } 00652 } 00653 else 00654 { 00655 // kDebug() << "toUtc(): cached -> " << utc() << endl, 00656 #ifdef COMPILING_TESTS 00657 ++KDateTime_utcCacheHit; 00658 #endif 00659 return utc(); 00660 } 00661 } 00662 00663 // No cached UTC value, so calculate it 00664 switch (specType) 00665 { 00666 case KDateTime::UTC: 00667 return mDt; 00668 case KDateTime::OffsetFromUTC: 00669 { 00670 if (!mDt.isValid()) 00671 break; 00672 QDateTime dt = QDateTime(mDt.date(), mDt.time(), Qt::UTC).addSecs(-specUtcOffset); 00673 setUtc(dt); 00674 // kDebug() << "toUtc(): calculated -> " << dt << endl, 00675 return dt; 00676 } 00677 case KDateTime::ClockTime: 00678 { 00679 if (!mDt.isValid()) 00680 break; 00681 if (!loc.isValid()) 00682 loc = KSystemTimeZones::local(); 00683 const_cast<KDateTimePrivate*>(this)->specZone = loc; 00684 QDateTime dt(specZone.toUtc(mDt)); 00685 setUtc(dt); 00686 // kDebug() << "toUtc(): calculated -> " << dt << endl, 00687 return dt; 00688 } 00689 case KDateTime::TimeZone: 00690 if (!mDt.isValid()) 00691 break; 00692 timeZoneOffset(); // calculate offset and cache UTC value 00693 // kDebug() << "toUtc(): calculated -> " << utc() << endl, 00694 return utc(); 00695 default: 00696 break; 00697 } 00698 00699 // Invalid - mark it cached to avoid having to process it again 00700 ut.date = QDate(); // (invalid) 00701 utcCached = true; 00702 convertedCached = false; 00703 // kDebug() << "toUtc(): invalid"; 00704 return mDt; 00705 } 00706 00707 /* Convert this value to another time zone. 00708 * The value is cached to save having to repeatedly calculate it. 00709 * The caller should check for an invalid date/time. 00710 */ 00711 QDateTime KDateTimePrivate::toZone(const KTimeZone &zone, const KTimeZone &local) const 00712 { 00713 if (convertedCached && converted.tz == zone) 00714 { 00715 // Converted value is already cached 00716 #ifdef COMPILING_TESTS 00717 // kDebug() << "KDateTimePrivate::toZone(" << zone->name() << "): " << mDt << " cached"; 00718 ++KDateTime_zoneCacheHit; 00719 #endif 00720 return QDateTime(converted.date, converted.time, Qt::LocalTime); 00721 } 00722 else 00723 { 00724 // Need to convert the value 00725 bool second; 00726 QDateTime result = zone.toZoneTime(toUtc(local), &second); 00727 converted.date = result.date(); 00728 converted.time = result.time(); 00729 converted.tz = zone; 00730 convertedCached = true; 00731 converted2ndOccur = second; 00732 return result; 00733 } 00734 } 00735 00736 /* Convert this value to another time zone, and write it into the specified instance. 00737 * The value is cached to save having to repeatedly calculate it. 00738 * The caller should check for an invalid date/time. 00739 */ 00740 void KDateTimePrivate::newToZone(KDateTimePrivate *newd, const KTimeZone &zone, const KTimeZone &local) const 00741 { 00742 newd->mDt = toZone(zone, local); 00743 newd->specZone = zone; 00744 newd->specType = KDateTime::TimeZone; 00745 newd->utcCached = utcCached; 00746 newd->mDateOnly = mDateOnly; 00747 newd->m2ndOccurrence = converted2ndOccur; 00748 switch (specType) 00749 { 00750 case KDateTime::UTC: 00751 newd->ut.date = mDt.date(); // cache the UTC value 00752 newd->ut.time = mDt.time(); 00753 break; 00754 case KDateTime::TimeZone: 00755 // This instance is also type time zone, so cache its value in the new instance 00756 newd->converted.date = mDt.date(); 00757 newd->converted.time = mDt.time(); 00758 newd->converted.tz = specZone; 00759 newd->convertedCached = true; 00760 newd->converted2ndOccur = m2ndOccurrence; 00761 newd->ut = ut; 00762 return; 00763 default: 00764 newd->ut = ut; 00765 break; 00766 } 00767 newd->convertedCached = false; 00768 } 00769 00770 00771 /*----------------------------------------------------------------------------*/ 00772 K_GLOBAL_STATIC_WITH_ARGS(QSharedDataPointer<KDateTimePrivate>, emptyDateTimePrivate, (new KDateTimePrivate)) 00773 00774 KDateTime::KDateTime() 00775 : d(*emptyDateTimePrivate) 00776 { 00777 } 00778 00779 KDateTime::KDateTime(const QDate &date, const Spec &spec) 00780 : d(new KDateTimePrivate(QDateTime(date, KDateTimePrivate::sod, Qt::LocalTime), spec, true)) 00781 { 00782 if (spec.type() == UTC) 00783 d->setDtTimeSpec(Qt::UTC); 00784 } 00785 00786 KDateTime::KDateTime(const QDate &date, const QTime &time, const Spec &spec) 00787 : d(new KDateTimePrivate(QDateTime(date, time, Qt::LocalTime), spec)) 00788 { 00789 if (spec.type() == UTC) 00790 d->setDtTimeSpec(Qt::UTC); 00791 } 00792 00793 KDateTime::KDateTime(const QDateTime &dt, const Spec &spec) 00794 : d(new KDateTimePrivate(dt, spec)) 00795 { 00796 // If the supplied date/time is UTC and we need local time, or vice versa, convert it. 00797 if (spec.type() == UTC) 00798 { 00799 if (dt.timeSpec() == Qt::LocalTime) 00800 d->setUtcFromTz(dt, KSystemTimeZones::local()); // set time & cache local time 00801 } 00802 else if (dt.timeSpec() == Qt::UTC) 00803 d->setDtFromUtc(dt); 00804 } 00805 00806 KDateTime::KDateTime(const QDateTime &dt) 00807 : d(new KDateTimePrivate(dt, (dt.timeSpec() == Qt::LocalTime ? Spec(LocalZone) : Spec(UTC)))) 00808 { 00809 } 00810 00811 KDateTime::KDateTime(const KDateTime &other) 00812 : d(other.d) 00813 { 00814 } 00815 00816 KDateTime::~KDateTime() 00817 { 00818 } 00819 00820 KDateTime &KDateTime::operator=(const KDateTime &other) 00821 { 00822 if (&other != this) 00823 d = other.d; 00824 return *this; 00825 } 00826 00827 void KDateTime::detach() { d.detach(); } 00828 bool KDateTime::isNull() const { return d->dt().isNull(); } 00829 bool KDateTime::isValid() const { return d->specType != Invalid && d->dt().isValid(); } 00830 bool KDateTime::outOfRange() const { return d->status == stTooEarly; } 00831 bool KDateTime::isDateOnly() const { return d->dateOnly(); } 00832 bool KDateTime::isLocalZone() const { return d->specType == TimeZone && d->specZone == KSystemTimeZones::local(); } 00833 bool KDateTime::isClockTime() const { return d->specType == ClockTime; } 00834 bool KDateTime::isUtc() const { return d->specType == UTC || (d->specType == OffsetFromUTC && d->specUtcOffset == 0); } 00835 bool KDateTime::isOffsetFromUtc() const { return d->specType == OffsetFromUTC; } 00836 bool KDateTime::isSecondOccurrence() const { return d->specType == TimeZone && d->secondOccurrence(); } 00837 QDate KDateTime::date() const { return d->date(); } 00838 QTime KDateTime::time() const { return d->dt().time(); } 00839 QDateTime KDateTime::dateTime() const { return d->dt(); } 00840 00841 KDateTime::Spec KDateTime::timeSpec() const { return d->spec(); } 00842 KDateTime::SpecType KDateTime::timeType() const { return d->specType; } 00843 00844 KTimeZone KDateTime::timeZone() const 00845 { 00846 switch (d->specType) 00847 { 00848 case TimeZone: 00849 return d->specZone; 00850 case UTC: 00851 return KTimeZone::utc(); 00852 default: 00853 return KTimeZone(); 00854 } 00855 } 00856 00857 int KDateTime::utcOffset() const 00858 { 00859 switch (d->specType) 00860 { 00861 case TimeZone: 00862 return d->timeZoneOffset(); // calculate offset and cache UTC value 00863 case OffsetFromUTC: 00864 return d->specUtcOffset; 00865 default: 00866 return 0; 00867 } 00868 } 00869 00870 KDateTime KDateTime::toUtc() const 00871 { 00872 if (!isValid()) 00873 return KDateTime(); 00874 if (d->specType == UTC) 00875 return *this; 00876 if (d->dateOnly()) 00877 return KDateTime(d->date(), UTC); 00878 QDateTime udt = d->toUtc(); 00879 if (!udt.isValid()) 00880 return KDateTime(); 00881 return KDateTime(udt, UTC); 00882 } 00883 00884 KDateTime KDateTime::toOffsetFromUtc() const 00885 { 00886 if (!isValid()) 00887 return KDateTime(); 00888 int offset = 0; 00889 switch (d->specType) 00890 { 00891 case OffsetFromUTC: 00892 return *this; 00893 case UTC: 00894 { 00895 if (d->dateOnly()) 00896 return KDateTime(d->date(), Spec(OffsetFromUTC, 0)); 00897 QDateTime qdt = d->dt(); 00898 qdt.setTimeSpec(Qt::LocalTime); 00899 return KDateTime(qdt, Spec(OffsetFromUTC, 0)); 00900 } 00901 case TimeZone: 00902 offset = d->timeZoneOffset(); // calculate offset and cache UTC value 00903 break; 00904 case ClockTime: 00905 offset = KSystemTimeZones::local().offsetAtZoneTime(d->dt()); 00906 break; 00907 default: 00908 return KDateTime(); 00909 } 00910 if (d->dateOnly()) 00911 return KDateTime(d->date(), Spec(OffsetFromUTC, offset)); 00912 return KDateTime(d->dt(), Spec(OffsetFromUTC, offset)); 00913 } 00914 00915 KDateTime KDateTime::toOffsetFromUtc(int utcOffset) const 00916 { 00917 if (!isValid()) 00918 return KDateTime(); 00919 if (d->specType == OffsetFromUTC && d->specUtcOffset == utcOffset) 00920 return *this; 00921 if (d->dateOnly()) 00922 return KDateTime(d->date(), Spec(OffsetFromUTC, utcOffset)); 00923 return KDateTime(d->toUtc(), Spec(OffsetFromUTC, utcOffset)); 00924 } 00925 00926 KDateTime KDateTime::toLocalZone() const 00927 { 00928 if (!isValid()) 00929 return KDateTime(); 00930 KTimeZone local = KSystemTimeZones::local(); 00931 if (d->specType == TimeZone && d->specZone == local) 00932 return *this; // it's already local zone. Preserve UTC cache, if any 00933 if (d->dateOnly()) 00934 return KDateTime(d->date(), Spec(local)); 00935 switch (d->specType) 00936 { 00937 case TimeZone: 00938 case OffsetFromUTC: 00939 case UTC: 00940 { 00941 KDateTime result; 00942 d->newToZone(result.d, local, local); // cache the time zone conversion 00943 return result; 00944 } 00945 case ClockTime: 00946 return KDateTime(d->dt(), Spec(local)); 00947 default: 00948 return KDateTime(); 00949 } 00950 } 00951 00952 KDateTime KDateTime::toClockTime() const 00953 { 00954 if (!isValid()) 00955 return KDateTime(); 00956 if (d->specType == ClockTime) 00957 return *this; 00958 if (d->dateOnly()) 00959 return KDateTime(d->date(), Spec(ClockTime)); 00960 KDateTime result = toLocalZone(); 00961 result.d->specType = ClockTime; // cached value (if any) is unaffected 00962 return result; 00963 } 00964 00965 KDateTime KDateTime::toZone(const KTimeZone &zone) const 00966 { 00967 if (!zone.isValid() || !isValid()) 00968 return KDateTime(); 00969 if (d->specType == TimeZone && d->specZone == zone) 00970 return *this; // preserve UTC cache, if any 00971 if (d->dateOnly()) 00972 return KDateTime(d->date(), Spec(zone)); 00973 KDateTime result; 00974 d->newToZone(result.d, zone); // cache the time zone conversion 00975 return result; 00976 } 00977 00978 KDateTime KDateTime::toTimeSpec(const KDateTime &dt) const 00979 { 00980 return toTimeSpec(dt.timeSpec()); 00981 } 00982 00983 KDateTime KDateTime::toTimeSpec(const Spec &spec) const 00984 { 00985 if (spec == d->spec()) 00986 return *this; 00987 if (!isValid()) 00988 return KDateTime(); 00989 if (d->dateOnly()) 00990 return KDateTime(d->date(), spec); 00991 if (spec.type() == TimeZone) 00992 { 00993 KDateTime result; 00994 d->newToZone(result.d, spec.timeZone()); // cache the time zone conversion 00995 return result; 00996 } 00997 return KDateTime(d->toUtc(), spec); 00998 } 00999 01000 uint KDateTime::toTime_t() const 01001 { 01002 QDateTime qdt = d->toUtc(); 01003 if (!qdt.isValid()) 01004 return uint(-1); 01005 return qdt.toTime_t(); 01006 } 01007 01008 void KDateTime::setTime_t(qint64 seconds) 01009 { 01010 d->setSpec(UTC); 01011 int days = static_cast<int>(seconds / 86400); 01012 int secs = static_cast<int>(seconds % 86400); 01013 QDateTime dt; 01014 dt.setTimeSpec(Qt::UTC); // prevent QDateTime::setTime_t() converting to local time 01015 dt.setTime_t(0); 01016 d->setDt(dt.addDays(days).addSecs(secs)); 01017 } 01018 01019 void KDateTime::setDateOnly(bool dateOnly) 01020 { 01021 d->setDateOnly(dateOnly); 01022 } 01023 01024 void KDateTime::setDate(const QDate &date) 01025 { 01026 d->setDate(date); 01027 } 01028 01029 void KDateTime::setTime(const QTime &time) 01030 { 01031 d->setTime(time); 01032 } 01033 01034 void KDateTime::setDateTime(const QDateTime &dt) 01035 { 01036 d->clearCache(); 01037 d->setDateOnly(false); 01038 if (dt.timeSpec() == Qt::LocalTime) 01039 { 01040 if (d->specType == UTC) 01041 d->setUtcFromTz(dt, KSystemTimeZones::local()); // set time & cache local time 01042 else 01043 d->setDt(dt); 01044 } 01045 else 01046 d->setDtFromUtc(dt); // a UTC time has been supplied 01047 } 01048 01049 void KDateTime::setTimeSpec(const Spec &other) 01050 { 01051 d->setSpec(other); 01052 } 01053 01054 void KDateTime::setSecondOccurrence(bool second) 01055 { 01056 if (d->specType == KDateTime::TimeZone && second != d->m2ndOccurrence) 01057 { 01058 d->m2ndOccurrence = second; 01059 d->clearCache(); 01060 if (second) 01061 { 01062 // Check whether a second occurrence is actually possible, and 01063 // if not, reset m2ndOccurrence. 01064 d->timeZoneOffset(); // check, and cache UTC value 01065 } 01066 } 01067 } 01068 01069 KDateTime KDateTime::addMSecs(qint64 msecs) const 01070 { 01071 if (!msecs) 01072 return *this; // retain cache - don't create another instance 01073 if (!isValid()) 01074 return KDateTime(); 01075 if (d->dateOnly()) 01076 { 01077 KDateTime result(*this); 01078 result.d->setDate(d->date().addDays(static_cast<int>(msecs / 86400000))); 01079 return result; 01080 } 01081 qint64 secs = msecs / 1000; 01082 int oldms = d->dt().time().msec(); 01083 int ms = oldms + static_cast<int>(msecs % 1000); 01084 if (msecs >= 0) 01085 { 01086 if (ms >= 1000) 01087 { 01088 ++secs; 01089 ms -= 1000; 01090 } 01091 } 01092 else 01093 { 01094 if (ms < 0) 01095 { 01096 --secs; 01097 ms += 1000; 01098 } 01099 } 01100 KDateTime result = addSecs(secs); 01101 QTime t = result.time(); 01102 result.d->setTime(QTime(t.hour(), t.minute(), t.second(), ms)); 01103 return result; 01104 } 01105 01106 KDateTime KDateTime::addSecs(qint64 secs) const 01107 { 01108 if (!secs) 01109 return *this; // retain cache - don't create another instance 01110 if (!isValid()) 01111 return KDateTime(); 01112 int days = static_cast<int>(secs / 86400); 01113 int seconds = static_cast<int>(secs % 86400); 01114 if (d->dateOnly()) 01115 { 01116 KDateTime result(*this); 01117 result.d->setDate(d->date().addDays(days)); 01118 return result; 01119 } 01120 if (d->specType == ClockTime) 01121 { 01122 QDateTime qdt = d->dt(); 01123 qdt.setTimeSpec(Qt::UTC); // set time as UTC to avoid daylight savings adjustments in addSecs() 01124 qdt = qdt.addDays(days).addSecs(seconds); 01125 qdt.setTimeSpec(Qt::LocalTime); 01126 return KDateTime(qdt, Spec(ClockTime)); 01127 } 01128 return KDateTime(d->toUtc().addDays(days).addSecs(seconds), d->spec()); 01129 } 01130 01131 KDateTime KDateTime::addDays(int days) const 01132 { 01133 if (!days) 01134 return *this; // retain cache - don't create another instance 01135 KDateTime result(*this); 01136 result.d->setDate(d->date().addDays(days)); 01137 return result; 01138 } 01139 01140 KDateTime KDateTime::addMonths(int months) const 01141 { 01142 if (!months) 01143 return *this; // retain cache - don't create another instance 01144 KDateTime result(*this); 01145 result.d->setDate(d->date().addMonths(months)); 01146 return result; 01147 } 01148 01149 KDateTime KDateTime::addYears(int years) const 01150 { 01151 if (!years) 01152 return *this; // retain cache - don't create another instance 01153 KDateTime result(*this); 01154 result.d->setDate(d->date().addYears(years)); 01155 return result; 01156 } 01157 01158 int KDateTime::secsTo(const KDateTime &t2) const 01159 { 01160 return static_cast<int>(secsTo_long(t2)); 01161 } 01162 01163 qint64 KDateTime::secsTo_long(const KDateTime &t2) const 01164 { 01165 if (!isValid() || !t2.isValid()) 01166 return 0; 01167 if (d->dateOnly()) 01168 { 01169 QDate dat = t2.d->dateOnly() ? t2.d->date() : t2.toTimeSpec(d->spec()).d->date(); 01170 return static_cast<qint64>(d->date().daysTo(dat)) * 86400; 01171 } 01172 if (t2.d->dateOnly()) 01173 return static_cast<qint64>(toTimeSpec(t2.d->spec()).d->date().daysTo(t2.d->date())) * 86400; 01174 01175 QDateTime dt1, dt2; 01176 if (d->specType == ClockTime && t2.d->specType == ClockTime) 01177 { 01178 // Set both times as UTC to avoid daylight savings adjustments in secsTo() 01179 dt1 = d->dt(); 01180 dt1.setTimeSpec(Qt::UTC); 01181 dt2 = t2.d->dt(); 01182 dt2.setTimeSpec(Qt::UTC); 01183 return dt1.secsTo(dt2); 01184 } 01185 else 01186 { 01187 dt1 = d->toUtc(); 01188 dt2 = t2.d->toUtc(); 01189 } 01190 return static_cast<qint64>(dt1.date().daysTo(dt2.date())) * 86400 01191 + dt1.time().secsTo(dt2.time()); 01192 } 01193 01194 int KDateTime::daysTo(const KDateTime &t2) const 01195 { 01196 if (!isValid() || !t2.isValid()) 01197 return 0; 01198 if (d->dateOnly()) 01199 { 01200 QDate dat = t2.d->dateOnly() ? t2.d->date() : t2.toTimeSpec(d->spec()).d->date(); 01201 return d->date().daysTo(dat); 01202 } 01203 if (t2.d->dateOnly()) 01204 return toTimeSpec(t2.d->spec()).d->date().daysTo(t2.d->date()); 01205 01206 QDate dat; 01207 switch (d->specType) 01208 { 01209 case UTC: 01210 dat = t2.d->toUtc().date(); 01211 break; 01212 case OffsetFromUTC: 01213 dat = t2.d->toUtc().addSecs(d->specUtcOffset).date(); 01214 break; 01215 case TimeZone: 01216 dat = t2.d->toZone(d->specZone).date(); // this caches the converted time in t2 01217 break; 01218 case ClockTime: 01219 { 01220 KTimeZone local = KSystemTimeZones::local(); 01221 dat = t2.d->toZone(local, local).date(); // this caches the converted time in t2 01222 break; 01223 } 01224 default: // invalid 01225 return 0; 01226 } 01227 return d->date().daysTo(dat); 01228 } 01229 01230 KDateTime KDateTime::currentLocalDateTime() 01231 { 01232 #ifndef NDEBUG 01233 if (KSystemTimeZones::isSimulated()) 01234 return currentUtcDateTime().toZone(KSystemTimeZones::local()); 01235 #endif 01236 return KDateTime(QDateTime::currentDateTime(), Spec(KSystemTimeZones::local())); 01237 } 01238 01239 KDateTime KDateTime::currentUtcDateTime() 01240 { 01241 KDateTime result; 01242 #ifdef Q_OS_WIN 01243 SYSTEMTIME st; 01244 memset(&st, 0, sizeof(SYSTEMTIME)); 01245 GetSystemTime(&st); 01246 result = KDateTime(QDate(st.wYear, st.wMonth, st.wDay), 01247 QTime(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds), 01248 UTC); 01249 #else 01250 time_t t; 01251 ::time(&t); 01252 result.setTime_t(static_cast<qint64>(t)); 01253 #endif 01254 #ifndef NDEBUG 01255 return result.addSecs(KDateTimePrivate::currentDateTimeOffset); 01256 #else 01257 return result; 01258 #endif 01259 } 01260 01261 KDateTime KDateTime::currentDateTime(const Spec &spec) 01262 { 01263 switch (spec.type()) 01264 { 01265 case UTC: 01266 return currentUtcDateTime(); 01267 case TimeZone: 01268 if (spec.timeZone() != KSystemTimeZones::local()) 01269 break; 01270 // fall through to LocalZone 01271 case LocalZone: 01272 return currentLocalDateTime(); 01273 default: 01274 break; 01275 } 01276 return currentUtcDateTime().toTimeSpec(spec); 01277 } 01278 01279 QDate KDateTime::currentLocalDate() 01280 { 01281 return currentLocalDateTime().date(); 01282 } 01283 01284 QTime KDateTime::currentLocalTime() 01285 { 01286 return currentLocalDateTime().time(); 01287 } 01288 01289 KDateTime::Comparison KDateTime::compare(const KDateTime &other) const 01290 { 01291 QDateTime start1, start2; 01292 bool conv = (!d->equalSpec(*other.d) || d->secondOccurrence() != other.d->secondOccurrence()); 01293 if (conv) 01294 { 01295 // Different time specs or one is a time which occurs twice, 01296 // so convert to UTC before comparing 01297 start1 = d->toUtc(); 01298 start2 = other.d->toUtc(); 01299 } 01300 else 01301 { 01302 // Same time specs, so no need to convert to UTC 01303 start1 = d->dt(); 01304 start2 = other.d->dt(); 01305 } 01306 if (d->dateOnly() || other.d->dateOnly()) 01307 { 01308 // At least one of the instances is date-only, so we need to compare 01309 // time periods rather than just times. 01310 QDateTime end1, end2; 01311 if (conv) 01312 { 01313 if (d->dateOnly()) 01314 { 01315 KDateTime kdt(*this); 01316 kdt.setTime(QTime(23,59,59,999)); 01317 end1 = kdt.d->toUtc(); 01318 } 01319 else 01320 end1 = start1; 01321 if (other.d->dateOnly()) 01322 { 01323 KDateTime kdt(other); 01324 kdt.setTime(QTime(23,59,59,999)); 01325 end2 = kdt.d->toUtc(); 01326 } 01327 else 01328 end2 = start2; 01329 } 01330 else 01331 { 01332 if (d->dateOnly()) 01333 end1 = QDateTime(d->date(), QTime(23,59,59,999), Qt::LocalTime); 01334 else 01335 end1 = d->dt(); 01336 if (other.d->dateOnly()) 01337 end2 = QDateTime(other.d->date(), QTime(23,59,59,999), Qt::LocalTime); 01338 else 01339 end2 = other.d->dt(); 01340 } 01341 if (start1 == start2) 01342 return !d->dateOnly() ? AtStart : (end1 == end2) ? Equal 01343 : (end1 < end2) ? static_cast<Comparison>(AtStart|Inside) 01344 : static_cast<Comparison>(AtStart|Inside|AtEnd|After); 01345 if (start1 < start2) 01346 return (end1 < start2) ? Before 01347 : (end1 == end2) ? static_cast<Comparison>(Before|AtStart|Inside|AtEnd) 01348 : (end1 == start2) ? static_cast<Comparison>(Before|AtStart) 01349 : (end1 < end2) ? static_cast<Comparison>(Before|AtStart|Inside) : Outside; 01350 else 01351 return (start1 > end2) ? After 01352 : (start1 == end2) ? (end1 == end2 ? AtEnd : static_cast<Comparison>(AtEnd|After)) 01353 : (end1 == end2) ? static_cast<Comparison>(Inside|AtEnd) 01354 : (end1 < end2) ? Inside : static_cast<Comparison>(Inside|AtEnd|After); 01355 } 01356 return (start1 == start2) ? Equal : (start1 < start2) ? Before : After; 01357 } 01358 01359 bool KDateTime::operator==(const KDateTime &other) const 01360 { 01361 if (d == other.d) 01362 return true; // the two instances share the same data 01363 if (d->dateOnly() != other.d->dateOnly()) 01364 return false; 01365 if (d->equalSpec(*other.d)) 01366 { 01367 // Both instances are in the same time zone, so compare directly 01368 if (d->dateOnly()) 01369 return d->date() == other.d->date(); 01370 else 01371 return d->secondOccurrence() == other.d->secondOccurrence() 01372 && d->dt() == other.d->dt(); 01373 } 01374 if (d->dateOnly()) 01375 { 01376 // Date-only values are equal if both the start and end of day times are equal. 01377 // Don't waste time converting to UTC if the dates aren't very close. 01378 if (qAbs(d->date().daysTo(other.d->date())) > 2) 01379 return false; 01380 if (d->toUtc() != other.d->toUtc()) 01381 return false; // start-of-day times differ 01382 KDateTime end1(*this); 01383 end1.setTime(QTime(23,59,59,999)); 01384 KDateTime end2(other); 01385 end2.setTime(QTime(23,59,59,999)); 01386 return end1.d->toUtc() == end2.d->toUtc(); 01387 } 01388 return d->toUtc() == other.d->toUtc(); 01389 } 01390 01391 bool KDateTime::operator<(const KDateTime &other) const 01392 { 01393 if (d == other.d) 01394 return false; // the two instances share the same data 01395 if (d->equalSpec(*other.d)) 01396 { 01397 // Both instances are in the same time zone, so compare directly 01398 if (d->dateOnly() || other.d->dateOnly()) 01399 return d->date() < other.d->date(); 01400 if (d->secondOccurrence() == other.d->secondOccurrence()) 01401 return d->dt() < other.d->dt(); 01402 // One is the second occurrence of a date/time, during a change from 01403 // daylight saving to standard time, so only do a direct comparison 01404 // if the dates are more than 1 day apart. 01405 int diff = d->dt().date().daysTo(other.d->dt().date()); 01406 if (diff > 1) 01407 return true; 01408 if (diff < -1) 01409 return false; 01410 } 01411 if (d->dateOnly()) 01412 { 01413 // This instance is date-only, so we need to compare the end of its 01414 // day with the other value. Note that if the other value is date-only, 01415 // we want to compare with the start of its day, which will happen 01416 // automatically. 01417 KDateTime kdt(*this); 01418 kdt.setTime(QTime(23,59,59,999)); 01419 return kdt.d->toUtc() < other.d->toUtc(); 01420 } 01421 return d->toUtc() < other.d->toUtc(); 01422 } 01423 01424 QString KDateTime::toString(const QString &format) const 01425 { 01426 if (!isValid()) 01427 return QString(); 01428 enum { TZNone, UTCOffsetShort, UTCOffset, UTCOffsetColon, TZAbbrev, TZName }; 01429 KLocale *locale = KGlobal::locale(); 01430 KCalendarSystemGregorian calendar(locale); 01431 QString result; 01432 QString s; 01433 int num, numLength, zone; 01434 bool escape = false; 01435 ushort flag = 0; 01436 for (int i = 0, end = format.length(); i < end; ++i) 01437 { 01438 zone = TZNone; 01439 num = NO_NUMBER; 01440 numLength = 0; // no leading zeroes 01441 ushort ch = format[i].unicode(); 01442 if (!escape) 01443 { 01444 if (ch == '%') 01445 escape = true; 01446 else 01447 result += format[i]; 01448 continue; 01449 } 01450 if (!flag) 01451 { 01452 switch (ch) 01453 { 01454 case '%': 01455 result += QLatin1Char('%'); 01456 break; 01457 case ':': 01458 flag = ch; 01459 break; 01460 case 'Y': // year 01461 num = d->date().year(); 01462 numLength = 4; 01463 break; 01464 case 'y': // year, 2 digits 01465 num = d->date().year() % 100; 01466 numLength = 2; 01467 break; 01468 case 'm': // month, 01 - 12 01469 numLength = 2; 01470 num = d->date().month(); 01471 break; 01472 case 'B': // month name, translated 01473 result += calendar.monthName(d->date().month(), 2000, KCalendarSystem::LongName); 01474 break; 01475 case 'b': // month name, translated, short 01476 result += calendar.monthName(d->date().month(), 2000, KCalendarSystem::ShortName); 01477 break; 01478 case 'd': // day of month, 01 - 31 01479 numLength = 2; 01480 // fall through to 'e' 01481 case 'e': // day of month, 1 - 31 01482 num = d->date().day(); 01483 break; 01484 case 'A': // week day name, translated 01485 result += calendar.weekDayName(d->date().dayOfWeek(), KCalendarSystem::LongDayName); 01486 break; 01487 case 'a': // week day name, translated, short 01488 result += calendar.weekDayName(d->date().dayOfWeek(), KCalendarSystem::ShortDayName); 01489 break; 01490 case 'H': // hour, 00 - 23 01491 numLength = 2; 01492 // fall through to 'k' 01493 case 'k': // hour, 0 - 23 01494 num = d->dt().time().hour(); 01495 break; 01496 case 'I': // hour, 01 - 12 01497 numLength = 2; 01498 // fall through to 'l' 01499 case 'l': // hour, 1 - 12 01500 num = (d->dt().time().hour() + 11) % 12 + 1; 01501 break; 01502 case 'M': // minutes, 00 - 59 01503 num = d->dt().time().minute(); 01504 numLength = 2; 01505 break; 01506 case 'S': // seconds, 00 - 59 01507 num = d->dt().time().second(); 01508 numLength = 2; 01509 break; 01510 case 'P': // am/pm 01511 { 01512 bool am = (d->dt().time().hour() < 12); 01513 QString ap = ki18n(am ? "am" : "pm").toString(locale); 01514 if (ap.isEmpty()) 01515 result += am ? QLatin1String("am") : QLatin1String("pm"); 01516 else 01517 result += ap; 01518 break; 01519 } 01520 case 'p': // AM/PM 01521 { 01522 bool am = (d->dt().time().hour() < 12); 01523 QString ap = ki18n(am ? "am" : "pm").toString(locale).toUpper(); 01524 if (ap.isEmpty()) 01525 result += am ? QLatin1String("AM") : QLatin1String("PM"); 01526 else 01527 result += ap; 01528 break; 01529 } 01530 case 'z': // UTC offset in hours and minutes 01531 zone = UTCOffset; 01532 break; 01533 case 'Z': // time zone abbreviation 01534 zone = TZAbbrev; 01535 break; 01536 default: 01537 result += QLatin1Char('%'); 01538 result += format[i]; 01539 break; 01540 } 01541 } 01542 else if (flag == ':') 01543 { 01544 // It's a "%:" sequence 01545 switch (ch) 01546 { 01547 case 'A': // week day name in English 01548 result += QLatin1String(longDay[d->date().dayOfWeek() - 1]); 01549 break; 01550 case 'a': // week day name in English, short 01551 result += QLatin1String(shortDay[d->date().dayOfWeek() - 1]); 01552 break; 01553 case 'B': // month name in English 01554 result += QLatin1String(longMonth[d->date().month() - 1]); 01555 break; 01556 case 'b': // month name in English, short 01557 result += QLatin1String(shortMonth[d->date().month() - 1]); 01558 break; 01559 case 'm': // month, 1 - 12 01560 num = d->date().month(); 01561 break; 01562 case 'P': // am/pm 01563 result += (d->dt().time().hour() < 12) ? QLatin1String("am") : QLatin1String("pm"); 01564 break; 01565 case 'p': // AM/PM 01566 result += (d->dt().time().hour() < 12) ? QLatin1String("AM") : QLatin1String("PM"); 01567 break; 01568 case 'S': // seconds with ':' prefix, only if non-zero 01569 { 01570 int sec = d->dt().time().second(); 01571 if (sec || d->dt().time().msec()) 01572 { 01573 result += QLatin1Char(':'); 01574 num = sec; 01575 numLength = 2; 01576 } 01577 break; 01578 } 01579 case 's': // milliseconds 01580 result += s.sprintf("%03d", d->dt().time().msec()); 01581 break; 01582 case 'u': // UTC offset in hours 01583 zone = UTCOffsetShort; 01584 break; 01585 case 'z': // UTC offset in hours and minutes, with colon 01586 zone = UTCOffsetColon; 01587 break; 01588 case 'Z': // time zone name 01589 zone = TZName; 01590 break; 01591 default: 01592 result += QLatin1String("%:"); 01593 result += format[i]; 01594 break; 01595 } 01596 flag = 0; 01597 } 01598 if (!flag) 01599 escape = false; 01600 01601 // Append any required number or time zone information 01602 if (num != NO_NUMBER) 01603 { 01604 if (!numLength) 01605 result += QString::number(num); 01606 else if (numLength == 2 || numLength == 4) 01607 { 01608 if (num < 0) 01609 { 01610 num = -num; 01611 result += QLatin1Char('-'); 01612 } 01613 result += s.sprintf((numLength == 2 ? "%02d" : "%04d"), num); 01614 } 01615 } 01616 else if (zone != TZNone) 01617 { 01618 KTimeZone tz; 01619 int offset; 01620 switch (d->specType) 01621 { 01622 case UTC: 01623 case TimeZone: 01624 tz = (d->specType == TimeZone) ? d->specZone : KTimeZone::utc(); 01625 // fall through to OffsetFromUTC 01626 case OffsetFromUTC: 01627 offset = (d->specType == TimeZone) ? d->timeZoneOffset() 01628 : (d->specType == OffsetFromUTC) ? d->specUtcOffset : 0; 01629 offset /= 60; 01630 switch (zone) 01631 { 01632 case UTCOffsetShort: // UTC offset in hours 01633 case UTCOffset: // UTC offset in hours and minutes 01634 case UTCOffsetColon: // UTC offset in hours and minutes, with colon 01635 { 01636 if (offset >= 0) 01637 result += QLatin1Char('+'); 01638 else 01639 { 01640 result += QLatin1Char('-'); 01641 offset = -offset; 01642 } 01643 QString s; 01644 result += s.sprintf(((zone == UTCOffsetColon) ? "%02d:" : "%02d"), offset/60); 01645 if (ch != 'u' || offset % 60) 01646 result += s.sprintf("%02d", offset % 60); 01647 break; 01648 } 01649 case TZAbbrev: // time zone abbreviation 01650 if (tz.isValid() && d->specType != OffsetFromUTC) 01651 result += QString::fromLatin1(tz.abbreviation(d->toUtc())); 01652 break; 01653 case TZName: // time zone name 01654 if (tz.isValid() && d->specType != OffsetFromUTC) 01655 result += tz.name(); 01656 break; 01657 } 01658 break; 01659 default: 01660 break; 01661 } 01662 } 01663 } 01664 return result; 01665 } 01666 01667 QString KDateTime::toString(TimeFormat format) const 01668 { 01669 QString result; 01670 if (!isValid()) 01671 return result; 01672 01673 QString s; 01674 char tzsign = '+'; 01675 int offset = 0; 01676 const char *tzcolon = ""; 01677 KTimeZone tz; 01678 switch (format) 01679 { 01680 case RFCDateDay: 01681 result += QString::fromLatin1(shortDay[d->date().dayOfWeek() - 1]); 01682 result += QLatin1String(", "); 01683 // fall through to RFCDate 01684 case RFCDate: 01685 { 01686 char seconds[8] = { 0 }; 01687 if (d->dt().time().second()) 01688 sprintf(seconds, ":%02d", d->dt().time().second()); 01689 result += s.sprintf("%02d %s ", d->date().day(), shortMonth[d->date().month() - 1]); 01690 int year = d->date().year(); 01691 if (year < 0) 01692 { 01693 result += QLatin1Char('-'); 01694 year = -year; 01695 } 01696 result += s.sprintf("%04d %02d:%02d%s ", 01697 year, d->dt().time().hour(), d->dt().time().minute(), seconds); 01698 if (d->specType == ClockTime) 01699 tz = KSystemTimeZones::local(); 01700 break; 01701 } 01702 case ISODate: 01703 { 01704 // QDateTime::toString(Qt::ISODate) doesn't output fractions of a second 01705 int year = d->date().year(); 01706 if (year < 0) 01707 { 01708 result += QLatin1Char('-'); 01709 year = -year; 01710 } 01711 QString s; 01712 result += s.sprintf("%04d-%02d-%02d", 01713 year, d->date().month(), d->date().day()); 01714 if (!d->dateOnly() || d->specType != ClockTime) 01715 { 01716 result += s.sprintf("T%02d:%02d:%02d", 01717 d->dt().time().hour(), d->dt().time().minute(), d->dt().time().second()); 01718 if (d->dt().time().msec()) 01719 { 01720 // Comma is preferred by ISO8601 as the decimal point symbol, 01721 // so use it unless '.' is the symbol used in this locale or we don't have a locale. 01722 KLocale *locale = KGlobal::locale(); 01723 result += (locale && locale->decimalSymbol() == QLatin1String(".")) ? QLatin1Char('.') : QLatin1Char(','); 01724 result += s.sprintf("%03d", d->dt().time().msec()); 01725 } 01726 } 01727 if (d->specType == UTC) 01728 return result + QLatin1Char('Z'); 01729 if (d->specType == ClockTime) 01730 return result; 01731 tzcolon = ":"; // krazy:exclude=doublequote_chars 01732 break; 01733 } 01734 // fall through to QtTextDate 01735 case QtTextDate: 01736 case LocalDate: 01737 { 01738 Qt::DateFormat qtfmt = (format == QtTextDate) ? Qt::TextDate : Qt::LocalDate; 01739 if (d->dateOnly()) 01740 result = d->date().toString(qtfmt); 01741 else 01742 result = d->dt().toString(qtfmt); 01743 if (result.isEmpty() || d->specType == ClockTime) 01744 return result; 01745 result += QLatin1Char(' '); 01746 break; 01747 } 01748 default: 01749 return result; 01750 } 01751 01752 // Return the string with UTC offset ±hhmm appended 01753 if (d->specType == OffsetFromUTC || d->specType == TimeZone || tz.isValid()) 01754 { 01755 if (d->specType == TimeZone) 01756 offset = d->timeZoneOffset(); // calculate offset and cache UTC value 01757 else 01758 offset = tz.isValid() ? tz.offsetAtZoneTime(d->dt()) : d->specUtcOffset; 01759 if (offset < 0) 01760 { 01761 offset = -offset; 01762 tzsign = '-'; 01763 } 01764 } 01765 offset /= 60; 01766 return result + s.sprintf("%c%02d%s%02d", tzsign, offset/60, tzcolon, offset%60); 01767 } 01768 01769 KDateTime KDateTime::fromString(const QString &string, TimeFormat format, bool *negZero) 01770 { 01771 if (negZero) 01772 *negZero = false; 01773 QString str = string.trimmed(); 01774 if (str.isEmpty()) 01775 return KDateTime(); 01776 01777 switch (format) 01778 { 01779 case RFCDateDay: // format is Wdy, DD Mon YYYY hh:mm:ss ±hhmm 01780 case RFCDate: // format is [Wdy,] DD Mon YYYY hh:mm[:ss] ±hhmm 01781 { 01782 int nyear = 6; // indexes within string to values 01783 int nmonth = 4; 01784 int nday = 2; 01785 int nwday = 1; 01786 int nhour = 7; 01787 int nmin = 8; 01788 int nsec = 9; 01789 // Also accept obsolete form "Weekday, DD-Mon-YY HH:MM:SS ±hhmm" 01790 QRegExp rx(QString::fromLatin1("^(?:([A-Z][a-z]+),\\s*)?(\\d{1,2})(\\s+|-)([^-\\s]+)(\\s+|-)(\\d{2,4})\\s+(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s+(\\S+)$")); 01791 QStringList parts; 01792 if (!str.indexOf(rx)) 01793 { 01794 // Check that if date has '-' separators, both separators are '-'. 01795 parts = rx.capturedTexts(); 01796 bool h1 = (parts[3] == QLatin1String("-")); 01797 bool h2 = (parts[5] == QLatin1String("-")); 01798 if (h1 != h2) 01799 break; 01800 } 01801 else 01802 { 01803 // Check for the obsolete form "Wdy Mon DD HH:MM:SS YYYY" 01804 rx = QRegExp(QString::fromLatin1("^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$")); 01805 if (str.indexOf(rx)) 01806 break; 01807 nyear = 7; 01808 nmonth = 2; 01809 nday = 3; 01810 nwday = 1; 01811 nhour = 4; 01812 nmin = 5; 01813 nsec = 6; 01814 parts = rx.capturedTexts(); 01815 } 01816 bool ok[4]; 01817 int day = parts[nday].toInt(&ok[0]); 01818 int year = parts[nyear].toInt(&ok[1]); 01819 int hour = parts[nhour].toInt(&ok[2]); 01820 int minute = parts[nmin].toInt(&ok[3]); 01821 if (!ok[0] || !ok[1] || !ok[2] || !ok[3]) 01822 break; 01823 int second = 0; 01824 if (!parts[nsec].isEmpty()) 01825 { 01826 second = parts[nsec].toInt(&ok[0]); 01827 if (!ok[0]) 01828 break; 01829 } 01830 bool leapSecond = (second == 60); 01831 if (leapSecond) 01832 second = 59; // apparently a leap second - validate below, once time zone is known 01833 int month = 0; 01834 for ( ; month < 12 && parts[nmonth] != QLatin1String(shortMonth[month]); ++month) ; 01835 int dayOfWeek = -1; 01836 if (!parts[nwday].isEmpty()) 01837 { 01838 // Look up the weekday name 01839 while (++dayOfWeek < 7 && QLatin1String(shortDay[dayOfWeek]) != parts[nwday]) ; 01840 if (dayOfWeek >= 7) 01841 for (dayOfWeek = 0; dayOfWeek < 7 && QLatin1String(longDay[dayOfWeek]) != parts[nwday]; ++dayOfWeek) ; 01842 } 01843 if (month >= 12 || dayOfWeek >= 7 01844 || (dayOfWeek < 0 && format == RFCDateDay)) 01845 break; 01846 int i = parts[nyear].size(); 01847 if (i < 4) 01848 { 01849 // It's an obsolete year specification with less than 4 digits 01850 year += (i == 2 && year < 50) ? 2000 : 1900; 01851 } 01852 01853 // Parse the UTC offset part 01854 int offset = 0; // set default to '-0000' 01855 bool negOffset = false; 01856 if (parts.count() > 10) 01857 { 01858 rx = QRegExp(QString::fromLatin1("^([+-])(\\d\\d)(\\d\\d)$")); 01859 if (!parts[10].indexOf(rx)) 01860 { 01861 // It's a UTC offset ±hhmm 01862 parts = rx.capturedTexts(); 01863 offset = parts[2].toInt(&ok[0]) * 3600; 01864 int offsetMin = parts[3].toInt(&ok[1]); 01865 if (!ok[0] || !ok[1] || offsetMin > 59) 01866 break; 01867 offset += offsetMin * 60; 01868 negOffset = (parts[1] == QLatin1String("-")); 01869 if (negOffset) 01870 offset = -offset; 01871 } 01872 else 01873 { 01874 // Check for an obsolete time zone name 01875 QByteArray zone = parts[10].toLatin1(); 01876 if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J') 01877 negOffset = true; // military zone: RFC 2822 treats as '-0000' 01878 else if (zone != "UT" && zone != "GMT") // treated as '+0000' 01879 { 01880 offset = (zone == "EDT") ? -4*3600 01881 : (zone == "EST" || zone == "CDT") ? -5*3600 01882 : (zone == "CST" || zone == "MDT") ? -6*3600 01883 : (zone == "MST" || zone == "PDT") ? -7*3600 01884 : (zone == "PST") ? -8*3600 01885 : 0; 01886 if (!offset) 01887 { 01888 // Check for any other alphabetic time zone 01889 bool nonalpha = false; 01890 for (int i = 0, end = zone.size(); i < end && !nonalpha; ++i) 01891 nonalpha = !isalpha(zone[i]); 01892 if (nonalpha) 01893 break; 01894 // TODO: Attempt to recognize the time zone abbreviation? 01895 negOffset = true; // unknown time zone: RFC 2822 treats as '-0000' 01896 } 01897 } 01898 } 01899 } 01900 Status invalid = stValid; 01901 QDate qdate = checkDate(year, month+1, day, invalid); // convert date, and check for out-of-range 01902 if (!qdate.isValid()) 01903 break; 01904 KDateTime result(qdate, QTime(hour, minute, second), Spec(OffsetFromUTC, offset)); 01905 if (!result.isValid() 01906 || (dayOfWeek >= 0 && result.date().dayOfWeek() != dayOfWeek+1)) 01907 break; // invalid date/time, or weekday doesn't correspond with date 01908 if (!offset) 01909 { 01910 if (negOffset && negZero) 01911 *negZero = true; // UTC offset given as "-0000" 01912 result.setTimeSpec(UTC); 01913 } 01914 if (leapSecond) 01915 { 01916 // Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC. 01917 // Convert the time to UTC and check that it is 00:00:00. 01918 if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours) 01919 break; // the time isn't the last second of the day 01920 } 01921 if (invalid) 01922 { 01923 KDateTime dt; // date out of range - return invalid KDateTime ... 01924 dt.d->status = invalid; // ... with reason for error 01925 return dt; 01926 } 01927 return result; 01928 } 01929 case ISODate: 01930 { 01931 /* 01932 * Extended format: [±]YYYY-MM-DD[Thh[:mm[:ss.s]][TZ]] 01933 * Basic format: [±]YYYYMMDD[Thh[mm[ss.s]][TZ]] 01934 * Extended format: [±]YYYY-DDD[Thh[:mm[:ss.s]][TZ]] 01935 * Basic format: [±]YYYYDDD[Thh[mm[ss.s]][TZ]] 01936 * In the first three formats, the year may be expanded to more than 4 digits. 01937 * 01938 * QDateTime::fromString(Qt::ISODate) is a rather limited implementation 01939 * of parsing ISO 8601 format date/time strings, so it isn't used here. 01940 * This implementation isn't complete either, but it's better. 01941 * 01942 * ISO 8601 allows truncation, but for a combined date & time, the date part cannot 01943 * be truncated from the right, and the time part cannot be truncated from the left. 01944 * In other words, only the outer parts of the string can be omitted. 01945 * The standard does not actually define how to interpret omitted parts - it is up 01946 * to those interchanging the data to agree on a scheme. 01947 */ 01948 bool dateOnly = false; 01949 // Check first for the extended format of ISO 8601 01950 QRegExp rx(QString::fromLatin1("^([+-])?(\\d{4,})-(\\d\\d\\d|\\d\\d-\\d\\d)[T ](\\d\\d)(?::(\\d\\d)(?::(\\d\\d)(?:(?:\\.|,)(\\d+))?)?)?(Z|([+-])(\\d\\d)(?::(\\d\\d))?)?$")); 01951 if (str.indexOf(rx)) 01952 { 01953 // It's not the extended format - check for the basic format 01954 rx = QRegExp(QString::fromLatin1("^([+-])?(\\d{4,})(\\d{4})[T ](\\d\\d)(?:(\\d\\d)(?:(\\d\\d)(?:(?:\\.|,)(\\d+))?)?)?(Z|([+-])(\\d\\d)(\\d\\d)?)?$")); 01955 if (str.indexOf(rx)) 01956 { 01957 rx = QRegExp(QString::fromLatin1("^([+-])?(\\d{4})(\\d{3})[T ](\\d\\d)(?:(\\d\\d)(?:(\\d\\d)(?:(?:\\.|,)(\\d+))?)?)?(Z|([+-])(\\d\\d)(\\d\\d)?)?$")); 01958 if (str.indexOf(rx)) 01959 { 01960 // Check for date-only formats 01961 dateOnly = true; 01962 rx = QRegExp(QString::fromLatin1("^([+-])?(\\d{4,})-(\\d\\d\\d|\\d\\d-\\d\\d)$")); 01963 if (str.indexOf(rx)) 01964 { 01965 // It's not the extended format - check for the basic format 01966 rx = QRegExp(QString::fromLatin1("^([+-])?(\\d{4,})(\\d{4})$")); 01967 if (str.indexOf(rx)) 01968 { 01969 rx = QRegExp(QString::fromLatin1("^([+-])?(\\d{4})(\\d{3})$")); 01970 if (str.indexOf(rx)) 01971 break; 01972 } 01973 } 01974 } 01975 } 01976 } 01977 const QStringList parts = rx.capturedTexts(); 01978 bool ok, ok1; 01979 QDate d; 01980 int hour = 0; 01981 int minute = 0; 01982 int second = 0; 01983 int msecs = 0; 01984 bool leapSecond = false; 01985 int year = parts[2].toInt(&ok); 01986 if (!ok) 01987 break; 01988 if (parts[1] == QLatin1String("-")) 01989 year = -year; 01990 if (!dateOnly) 01991 { 01992 hour = parts[4].toInt(&ok); 01993 if (!ok) 01994 break; 01995 if (!parts[5].isEmpty()) 01996 { 01997 minute = parts[5].toInt(&ok); 01998 if (!ok) 01999 break; 02000 } 02001 if (!parts[6].isEmpty()) 02002 { 02003 second = parts[6].toInt(&ok); 02004 if (!ok) 02005 break; 02006 } 02007 leapSecond = (second == 60); 02008 if (leapSecond) 02009 second = 59; // apparently a leap second - validate below, once time zone is known 02010 if (!parts[7].isEmpty()) 02011 { 02012 QString ms = parts[7] + QLatin1String("00"); 02013 ms.truncate(3); 02014 msecs = ms.toInt(&ok); 02015 if (!ok) 02016 break; 02017 } 02018 } 02019 int month, day; 02020 Status invalid = stValid; 02021 if (parts[3].length() == 3) 02022 { 02023 // A day of the year is specified 02024 day = parts[3].toInt(&ok); 02025 if (!ok || day < 1 || day > 366) 02026 break; 02027 d = checkDate(year, 1, 1, invalid).addDays(day - 1); // convert date, and check for out-of-range 02028 if (!d.isValid() || (!invalid && d.year() != year)) 02029 break; 02030 day = d.day(); 02031 month = d.month(); 02032 } 02033 else 02034 { 02035 // A month and day are specified 02036 month = parts[3].left(2).toInt(&ok); 02037 day = parts[3].right(2).toInt(&ok1); 02038 if (!ok || !ok1) 02039 break; 02040 d = checkDate(year, month, day, invalid); // convert date, and check for out-of-range 02041 if (!d.isValid()) 02042 break; 02043 } 02044 if (dateOnly) 02045 { 02046 if (invalid) 02047 { 02048 KDateTime dt; // date out of range - return invalid KDateTime ... 02049 dt.d->status = invalid; // ... with reason for error 02050 return dt; 02051 } 02052 return KDateTime(d, Spec(ClockTime)); 02053 } 02054 if (hour == 24 && !minute && !second && !msecs) 02055 { 02056 // A time of 24:00:00 is allowed by ISO 8601, and means midnight at the end of the day 02057 d = d.addDays(1); 02058 hour = 0; 02059 } 02060 02061 QTime t(hour, minute, second, msecs); 02062 if (!t.isValid()) 02063 break; 02064 if (parts[8].isEmpty()) 02065 { 02066 // No UTC offset is specified. Don't try to validate leap seconds. 02067 if (invalid) 02068 { 02069 KDateTime dt; // date out of range - return invalid KDateTime ... 02070 dt.d->status = invalid; // ... with reason for error 02071 return dt; 02072 } 02073 return KDateTime(d, t, KDateTimePrivate::fromStringDefault()); 02074 } 02075 int offset = 0; 02076 SpecType spec = (parts[8] == QLatin1String("Z")) ? UTC : OffsetFromUTC; 02077 if (spec == OffsetFromUTC) 02078 { 02079 offset = parts[10].toInt(&ok) * 3600; 02080 if (!ok) 02081 break; 02082 if (!parts[11].isEmpty()) 02083 { 02084 offset += parts[11].toInt(&ok) * 60; 02085 if (!ok) 02086 break; 02087 } 02088 if (parts[9] == QLatin1String("-")) 02089 { 02090 offset = -offset; 02091 if (!offset && negZero) 02092 *negZero = true; 02093 } 02094 } 02095 if (leapSecond) 02096 { 02097 // Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC. 02098 // Convert the time to UTC and check that it is 00:00:00. 02099 if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours) 02100 break; // the time isn't the last second of the day 02101 } 02102 if (invalid) 02103 { 02104 KDateTime dt; // date out of range - return invalid KDateTime ... 02105 dt.d->status = invalid; // ... with reason for error 02106 return dt; 02107 } 02108 return KDateTime(d, t, Spec(spec, offset)); 02109 } 02110 case QtTextDate: // format is Wdy Mth DD [hh:mm:ss] YYYY [±hhmm] 02111 { 02112 int offset = 0; 02113 QRegExp rx(QString::fromLatin1("^(\\S+\\s+\\S+\\s+\\d\\d\\s+(\\d\\d:\\d\\d:\\d\\d\\s+)?\\d\\d\\d\\d)\\s*(.*)$")); 02114 if (str.indexOf(rx) < 0) 02115 break; 02116 QStringList parts = rx.capturedTexts(); 02117 QDate qd; 02118 QDateTime qdt; 02119 bool dateOnly = parts[2].isEmpty(); 02120 if (dateOnly) 02121 { 02122 qd = QDate::fromString(parts[1], Qt::TextDate); 02123 if (!qd.isValid()) 02124 break; 02125 } 02126 else 02127 { 02128 qdt = QDateTime::fromString(parts[1], Qt::TextDate); 02129 if (!qdt.isValid()) 02130 break; 02131 } 02132 if (parts[3].isEmpty()) 02133 { 02134 // No time zone offset specified, so return a local clock time 02135 if (dateOnly) 02136 return KDateTime(qd, KDateTimePrivate::fromStringDefault()); 02137 else 02138 { 02139 // Do it this way to prevent UTC conversions changing the time 02140 return KDateTime(qdt.date(), qdt.time(), KDateTimePrivate::fromStringDefault()); 02141 } 02142 } 02143 rx = QRegExp(QString::fromLatin1("([+-])([\\d][\\d])(?::?([\\d][\\d]))?$")); 02144 if (parts[3].indexOf(rx) < 0) 02145 break; 02146 02147 // Extract the UTC offset at the end of the string 02148 bool ok; 02149 parts = rx.capturedTexts(); 02150 offset = parts[2].toInt(&ok) * 3600; 02151 if (!ok) 02152 break; 02153 if (parts.count() > 3) 02154 { 02155 offset += parts[3].toInt(&ok) * 60; 02156 if (!ok) 02157 break; 02158 } 02159 if (parts[1] == QLatin1String("-")) 02160 { 02161 offset = -offset; 02162 if (!offset && negZero) 02163 *negZero = true; 02164 } 02165 if (dateOnly) 02166 return KDateTime(qd, Spec((offset ? OffsetFromUTC : UTC), offset)); 02167 qdt.setTimeSpec(offset ? Qt::LocalTime : Qt::UTC); 02168 return KDateTime(qdt, Spec((offset ? OffsetFromUTC : UTC), offset)); 02169 } 02170 case LocalDate: 02171 default: 02172 break; 02173 } 02174 return KDateTime(); 02175 } 02176 02177 KDateTime KDateTime::fromString(const QString &string, const QString &format, 02178 const KTimeZones *zones, bool offsetIfAmbiguous) 02179 { 02180 int utcOffset = 0; // UTC offset in seconds 02181 bool dateOnly = false; 02182 Status invalid = stValid; 02183 QString zoneName; 02184 QByteArray zoneAbbrev; 02185 QDateTime qdt = fromStr(string, format, utcOffset, zoneName, zoneAbbrev, dateOnly, invalid); 02186 if (!qdt.isValid()) 02187 return KDateTime(); 02188 if (zones) 02189 { 02190 // Try to find a time zone match 02191 bool zname = false; 02192 KTimeZone zone; 02193 if (!zoneName.isEmpty()) 02194 { 02195 // A time zone name has been found. 02196 // Use the time zone with that name. 02197 zone = zones->zone(zoneName); 02198 zname = true; 02199 } 02200 else if (!invalid) 02201 { 02202 if (!zoneAbbrev.isEmpty()) 02203 { 02204 // A time zone abbreviation has been found. 02205 // Use the time zone which contains it, if any, provided that the 02206 // abbreviation applies at the specified date/time. 02207 bool useUtcOffset = false; 02208 const KTimeZones::ZoneMap z = zones->zones(); 02209 for (KTimeZones::ZoneMap::ConstIterator it = z.constBegin(); it != z.constEnd(); ++it) 02210 { 02211 if (it.value().abbreviations().contains(zoneAbbrev)) 02212 { 02213 int offset2; 02214 int offset = it.value().offsetAtZoneTime(qdt, &offset2); 02215 QDateTime ut(qdt); 02216 ut.setTimeSpec(Qt::UTC); 02217 ut.addSecs(-offset); 02218 if (it.value().abbreviation(ut) != zoneAbbrev) 02219 { 02220 if (offset == offset2) 02221 continue; // abbreviation doesn't apply at specified time 02222 ut.addSecs(offset - offset2); 02223 if (it.value().abbreviation(ut) != zoneAbbrev) 02224 continue; // abbreviation doesn't apply at specified time 02225 offset = offset2; 02226 } 02227 // Found a time zone which uses this abbreviation at the specified date/time 02228 if (zone.isValid()) 02229 { 02230 // Abbreviation is used by more than one time zone 02231 if (!offsetIfAmbiguous || offset != utcOffset) 02232 return KDateTime(); 02233 useUtcOffset = true; 02234 } 02235 else 02236 { 02237 zone = it.value(); 02238 utcOffset = offset; 02239 } 02240 } 02241 } 02242 if (useUtcOffset) 02243 { 02244 zone = KTimeZone(); 02245 if (!utcOffset) 02246 qdt.setTimeSpec(Qt::UTC); 02247 } 02248 else 02249 zname = true; 02250 } 02251 else if (utcOffset || qdt.timeSpec() == Qt::UTC) 02252 { 02253 // A UTC offset has been found. 02254 // Use the time zone which contains it, if any. 02255 // For a date-only value, use the start of the day. 02256 QDateTime dtUTC = qdt; 02257 dtUTC.setTimeSpec(Qt::UTC); 02258 dtUTC.addSecs(-utcOffset); 02259 const KTimeZones::ZoneMap z = zones->zones(); 02260 for (KTimeZones::ZoneMap::ConstIterator it = z.constBegin(); it != z.constEnd(); ++it) 02261 { 02262 QList<int> offsets = it.value().utcOffsets(); 02263 if ((offsets.isEmpty() || offsets.contains(utcOffset)) 02264 && it.value().offsetAtUtc(dtUTC) == utcOffset) 02265 { 02266 // Found a time zone which uses this offset at the specified time 02267 if (zone.isValid() || !utcOffset) 02268 { 02269 // UTC offset is used by more than one time zone 02270 if (!offsetIfAmbiguous) 02271 return KDateTime(); 02272 if (invalid) 02273 { 02274 KDateTime dt; // date out of range - return invalid KDateTime ... 02275 dt.d->status = invalid; // ... with reason for error 02276 return dt; 02277 } 02278 if (dateOnly) 02279 return KDateTime(qdt.date(), Spec(OffsetFromUTC, utcOffset)); 02280 qdt.setTimeSpec(Qt::LocalTime); 02281 return KDateTime(qdt, Spec(OffsetFromUTC, utcOffset)); 02282 } 02283 zone = it.value(); 02284 } 02285 } 02286 } 02287 } 02288 if (!zone.isValid() && zname) 02289 return KDateTime(); // an unknown zone name or abbreviation was found 02290 if (zone.isValid() && !invalid) 02291 { 02292 if (dateOnly) 02293 return KDateTime(qdt.date(), Spec(zone)); 02294 return KDateTime(qdt, Spec(zone)); 02295 } 02296 } 02297 02298 // No time zone match was found 02299 if (invalid) 02300 { 02301 KDateTime dt; // date out of range - return invalid KDateTime ... 02302 dt.d->status = invalid; // ... with reason for error 02303 return dt; 02304 } 02305 KDateTime result; 02306 if (utcOffset) 02307 { 02308 qdt.setTimeSpec(Qt::LocalTime); 02309 result = KDateTime(qdt, Spec(OffsetFromUTC, utcOffset)); 02310 } 02311 else if (qdt.timeSpec() == Qt::UTC) 02312 result = KDateTime(qdt, UTC); 02313 else 02314 { 02315 result = KDateTime(qdt, Spec(ClockTime)); 02316 result.setTimeSpec(KDateTimePrivate::fromStringDefault()); 02317 } 02318 if (dateOnly) 02319 result.setDateOnly(true); 02320 return result; 02321 } 02322 02323 void KDateTime::setFromStringDefault(const Spec &spec) 02324 { 02325 KDateTimePrivate::fromStringDefault() = spec; 02326 } 02327 02328 void KDateTime::setSimulatedSystemTime(const KDateTime& newTime) 02329 { 02330 Q_UNUSED(newTime); 02331 #ifndef NDEBUG 02332 if (newTime.isValid()) 02333 { 02334 KDateTimePrivate::currentDateTimeOffset = realCurrentLocalDateTime().secsTo_long(newTime); 02335 KSystemTimeZones::setLocalZone(newTime.timeZone()); 02336 } 02337 else 02338 { 02339 KDateTimePrivate::currentDateTimeOffset = 0; 02340 KSystemTimeZones::setLocalZone(KTimeZone()); 02341 } 02342 #endif 02343 } 02344 02345 KDateTime KDateTime::realCurrentLocalDateTime() 02346 { 02347 #ifndef NDEBUG 02348 return KDateTime(QDateTime::currentDateTime(), KSystemTimeZones::realLocalZone()); 02349 #else 02350 return KDateTime(QDateTime::currentDateTime(), Spec(KSystemTimeZones::local())); 02351 #endif 02352 } 02353 02354 QDataStream & operator<<(QDataStream &s, const KDateTime &dt) 02355 { 02356 s << dt.date() << dt.time() << dt.timeSpec() << quint8(dt.isDateOnly() ? 0x01 : 0x00); 02357 return s; 02358 } 02359 02360 QDataStream & operator>>(QDataStream &s, KDateTime &kdt) 02361 { 02362 QDate d; 02363 QTime t; 02364 KDateTime::Spec spec; 02365 quint8 flags; 02366 s >> d >> t >> spec >> flags; 02367 if (flags & 0x01) 02368 kdt = KDateTime(d, spec); 02369 else 02370 kdt = KDateTime(d, t, spec); 02371 return s; 02372 } 02373 02374 02375 /* 02376 * Extracts a QDateTime from a string, given a format string. 02377 * The date/time is set to Qt::UTC if a zero UTC offset is found, 02378 * otherwise it is Qt::LocalTime. If Qt::LocalTime is returned and 02379 * utcOffset == 0, that indicates that no UTC offset was found. 02380 */ 02381 QDateTime fromStr(const QString& string, const QString& format, int& utcOffset, 02382 QString& zoneName, QByteArray& zoneAbbrev, bool& dateOnly, Status &status) 02383 { 02384 status = stValid; 02385 QString str = string.simplified(); 02386 int year = NO_NUMBER; 02387 int month = NO_NUMBER; 02388 int day = NO_NUMBER; 02389 int dayOfWeek = NO_NUMBER; 02390 int hour = NO_NUMBER; 02391 int minute = NO_NUMBER; 02392 int second = NO_NUMBER; 02393 int millisec = NO_NUMBER; 02394 int ampm = NO_NUMBER; 02395 int tzoffset = NO_NUMBER; 02396 zoneName.clear(); 02397 zoneAbbrev.clear(); 02398 02399 enum { TZNone, UTCOffset, UTCOffsetColon, TZAbbrev, TZName }; 02400 KLocale *locale = KGlobal::locale(); 02401 KCalendarSystemGregorian calendar(locale); 02402 int zone; 02403 int s = 0; 02404 int send = str.length(); 02405 bool escape = false; 02406 ushort flag = 0; 02407 for (int f = 0, fend = format.length(); f < fend && s < send; ++f) 02408 { 02409 zone = TZNone; 02410 ushort ch = format[f].unicode(); 02411 if (!escape) 02412 { 02413 if (ch == '%') 02414 escape = true; 02415 else if (format[f].isSpace()) 02416 { 02417 if (str[s].isSpace()) 02418 ++s; 02419 } 02420 else if (format[f] == str[s]) 02421 ++s; 02422 else 02423 return QDateTime(); 02424 continue; 02425 } 02426 if (!flag) 02427 { 02428 switch (ch) 02429 { 02430 case '%': 02431 if (str[s++] != QLatin1Char('%')) 02432 return QDateTime(); 02433 break; 02434 case ':': 02435 flag = ch; 02436 break; 02437 case 'Y': // full year, 4 digits 02438 if (!getNumber(str, s, 4, 4, NO_NUMBER, -1, year)) 02439 return QDateTime(); 02440 break; 02441 case 'y': // year, 2 digits 02442 if (!getNumber(str, s, 2, 2, 0, 99, year)) 02443 return QDateTime(); 02444 year += (year <= 50) ? 2000 : 1999; 02445 break; 02446 case 'm': // month, 2 digits, 01 - 12 02447 if (!getNumber(str, s, 2, 2, 1, 12, month)) 02448 return QDateTime(); 02449 break; 02450 case 'B': 02451 case 'b': // month name, translated or English 02452 { 02453 int m = matchMonth(str, s, &calendar); 02454 if (m <= 0 || (month != NO_NUMBER && month != m)) 02455 return QDateTime(); 02456 month = m; 02457 break; 02458 } 02459 case 'd': // day of month, 2 digits, 01 - 31 02460 if (!getNumber(str, s, 2, 2, 1, 31, day)) 02461 return QDateTime(); 02462 break; 02463 case 'e': // day of month, 1 - 31 02464 if (!getNumber(str, s, 1, 2, 1, 31, day)) 02465 return QDateTime(); 02466 break; 02467 case 'A': 02468 case 'a': // week day name, translated or English 02469 { 02470 int dow = matchDay(str, s, &calendar); 02471 if (dow <= 0 || (dayOfWeek != NO_NUMBER && dayOfWeek != dow)) 02472 return QDateTime(); 02473 dayOfWeek = dow; 02474 break; 02475 } 02476 case 'H': // hour, 2 digits, 00 - 23 02477 if (!getNumber(str, s, 2, 2, 0, 23, hour)) 02478 return QDateTime(); 02479 break; 02480 case 'k': // hour, 0 - 23 02481 if (!getNumber(str, s, 1, 2, 0, 23, hour)) 02482 return QDateTime(); 02483 break; 02484 case 'I': // hour, 2 digits, 01 - 12 02485 if (!getNumber(str, s, 2, 2, 1, 12, hour)) 02486 return QDateTime(); 02487 break; 02488 case 'l': // hour, 1 - 12 02489 if (!getNumber(str, s, 1, 2, 1, 12, hour)) 02490 return QDateTime(); 02491 break; 02492 case 'M': // minutes, 2 digits, 00 - 59 02493 if (!getNumber(str, s, 2, 2, 0, 59, minute)) 02494 return QDateTime(); 02495 break; 02496 case 'S': // seconds, 2 digits, 00 - 59 02497 if (!getNumber(str, s, 2, 2, 0, 59, second)) 02498 return QDateTime(); 02499 break; 02500 case 's': // seconds, 0 - 59 02501 if (!getNumber(str, s, 1, 2, 0, 59, second)) 02502 return QDateTime(); 02503 break; 02504 case 'P': 02505 case 'p': // am/pm 02506 { 02507 int ap = getAmPm(str, s, locale); 02508 if (!ap || (ampm != NO_NUMBER && ampm != ap)) 02509 return QDateTime(); 02510 ampm = ap; 02511 break; 02512 } 02513 case 'z': // UTC offset in hours and optionally minutes 02514 zone = UTCOffset; 02515 break; 02516 case 'Z': // time zone abbreviation 02517 zone = TZAbbrev; 02518 break; 02519 case 't': // whitespace 02520 if (str[s++] != QLatin1Char(' ')) 02521 return QDateTime(); 02522 break; 02523 default: 02524 if (s + 2 > send 02525 || str[s++] != QLatin1Char('%') 02526 || str[s++] != format[f]) 02527 return QDateTime(); 02528 break; 02529 } 02530 } 02531 else if (flag == ':') 02532 { 02533 // It's a "%:" sequence 02534 switch (ch) 02535 { 02536 case 'Y': // full year, >= 4 digits 02537 if (!getNumber(str, s, 4, 100, NO_NUMBER, -1, year)) 02538 return QDateTime(); 02539 break; 02540 case 'A': 02541 case 'a': // week day name in English 02542 { 02543 int dow = matchDay(str, s, 0); 02544 if (dow <= 0 || (dayOfWeek != NO_NUMBER && dayOfWeek != dow)) 02545 return QDateTime(); 02546 dayOfWeek = dow; 02547 break; 02548 } 02549 case 'B': 02550 case 'b': // month name in English 02551 { 02552 int m = matchMonth(str, s, 0); 02553 if (m <= 0 || (month != NO_NUMBER && month != m)) 02554 return QDateTime(); 02555 month = m; 02556 break; 02557 } 02558 case 'm': // month, 1 - 12 02559 if (!getNumber(str, s, 1, 2, 1, 12, month)) 02560 return QDateTime(); 02561 break; 02562 case 'P': 02563 case 'p': // am/pm in English 02564 { 02565 int ap = getAmPm(str, s, 0); 02566 if (!ap || (ampm != NO_NUMBER && ampm != ap)) 02567 return QDateTime(); 02568 ampm = ap; 02569 break; 02570 } 02571 case 'M': // minutes, 0 - 59 02572 if (!getNumber(str, s, 1, 2, 0, 59, minute)) 02573 return QDateTime(); 02574 break; 02575 case 'S': // seconds with ':' prefix, defaults to zero 02576 if (str[s] != QLatin1Char(':')) 02577 { 02578 second = 0; 02579 break; 02580 } 02581 ++s; 02582 if (!getNumber(str, s, 1, 2, 0, 59, second)) 02583 return QDateTime(); 02584 break; 02585 case 's': // milliseconds, with decimal point prefix 02586 { 02587 if (str[s] != QLatin1Char('.')) 02588 { 02589 // If no locale, try comma, it is preferred by ISO8601 as the decimal point symbol 02590 QString dpt = locale == 0 ? QString::fromLatin1(",") : locale->decimalSymbol(); 02591 if (!str.mid(s).startsWith(dpt)) 02592 return QDateTime(); 02593 s += dpt.length() - 1; 02594 } 02595 ++s; 02596 if (s >= send) 02597 return QDateTime(); 02598 QString val = str.mid(s); 02599 int i = 0; 02600 for (int end = val.length(); i < end && val[i].isDigit(); ++i) ; 02601 if (!i) 02602 return QDateTime(); 02603 val.truncate(i); 02604 val += QLatin1String("00"); 02605 val.truncate(3); 02606 int ms = val.toInt(); 02607 if (millisec != NO_NUMBER && millisec != ms) 02608 return QDateTime(); 02609 millisec = ms; 02610 s += i; 02611 break; 02612 } 02613 case 'u': // UTC offset in hours and optionally minutes 02614 zone = UTCOffset; 02615 break; 02616 case 'z': // UTC offset in hours and minutes, with colon 02617 zone = UTCOffsetColon; 02618 break; 02619 case 'Z': // time zone name 02620 zone = TZName; 02621 break; 02622 default: 02623 if (s + 3 > send 02624 || str[s++] != QLatin1Char('%') 02625 || str[s++] != QLatin1Char(':') 02626 || str[s++] != format[f]) 02627 return QDateTime(); 02628 break; 02629 } 02630 flag = 0; 02631 } 02632 if (!flag) 02633 escape = false; 02634 02635 if (zone != TZNone) 02636 { 02637 // Read time zone or UTC offset 02638 switch (zone) 02639 { 02640 case UTCOffset: 02641 case UTCOffsetColon: 02642 if (!zoneAbbrev.isEmpty() || !zoneName.isEmpty()) 02643 return QDateTime(); 02644 if (!getUTCOffset(str, s, (zone == UTCOffsetColon), tzoffset)) 02645 return QDateTime(); 02646 break; 02647 case TZAbbrev: // time zone abbreviation 02648 { 02649 if (tzoffset != NO_NUMBER || !zoneName.isEmpty()) 02650 return QDateTime(); 02651 int start = s; 02652 while (s < send && str[s].isLetterOrNumber()) 02653 ++s; 02654 if (s == start) 02655 return QDateTime(); 02656 QString z = str.mid(start, s - start); 02657 if (!zoneAbbrev.isEmpty() && z.toLatin1() != zoneAbbrev) 02658 return QDateTime(); 02659 zoneAbbrev = z.toLatin1(); 02660 break; 02661 } 02662 case TZName: // time zone name 02663 { 02664 if (tzoffset != NO_NUMBER || !zoneAbbrev.isEmpty()) 02665 return QDateTime(); 02666 QString z; 02667 if (f + 1 >= fend) 02668 { 02669 z = str.mid(s); 02670 s = send; 02671 } 02672 else 02673 { 02674 // Get the terminating character for the zone name 02675 QChar endchar = format[f + 1]; 02676 if (endchar == QLatin1Char('%') && f + 2 < fend) 02677 { 02678 QChar endchar2 = format[f + 2]; 02679 if (endchar2 == QLatin1Char('n') || endchar2 == QLatin1Char('t')) 02680 endchar = QLatin1Char(' '); 02681 } 02682 // Extract from the input string up to the terminating character 02683 int start = s; 02684 for ( ; s < send && str[s] != endchar; ++s) ; 02685 if (s == start) 02686 return QDateTime(); 02687 z = str.mid(start, s - start); 02688 } 02689 if (!zoneName.isEmpty() && z != zoneName) 02690 return QDateTime(); 02691 zoneName = z; 02692 break; 02693 } 02694 default: 02695 break; 02696 } 02697 } 02698 } 02699 02700 if (year == NO_NUMBER) 02701 year = KDateTime::currentLocalDate().year(); 02702 if (month == NO_NUMBER) 02703 month = 1; 02704 QDate d = checkDate(year, month, (day > 0 ? day : 1), status); // convert date, and check for out-of-range 02705 if (!d.isValid()) 02706 return QDateTime(); 02707 if (dayOfWeek != NO_NUMBER && !status) 02708 { 02709 if (day == NO_NUMBER) 02710 { 02711 day = 1 + dayOfWeek - QDate(year, month, 1).dayOfWeek(); 02712 if (day <= 0) 02713 day += 7; 02714 } 02715 else 02716 { 02717 if (QDate(year, month, day).dayOfWeek() != dayOfWeek) 02718 return QDateTime(); 02719 } 02720 } 02721 if (day == NO_NUMBER) 02722 day = 1; 02723 dateOnly = (hour == NO_NUMBER && minute == NO_NUMBER && second == NO_NUMBER && millisec == NO_NUMBER); 02724 if (hour == NO_NUMBER) 02725 hour = 0; 02726 if (minute == NO_NUMBER) 02727 minute = 0; 02728 if (second == NO_NUMBER) 02729 second = 0; 02730 if (millisec == NO_NUMBER) 02731 millisec = 0; 02732 if (ampm != NO_NUMBER) 02733 { 02734 if (!hour || hour > 12) 02735 return QDateTime(); 02736 if (ampm == 1 && hour == 12) 02737 hour = 0; 02738 else if (ampm == 2 && hour < 12) 02739 hour += 12; 02740 } 02741 02742 QDateTime dt(d, QTime(hour, minute, second, millisec), (tzoffset == 0 ? Qt::UTC : Qt::LocalTime)); 02743 02744 utcOffset = (tzoffset == NO_NUMBER) ? 0 : tzoffset*60; 02745 02746 return dt; 02747 } 02748 02749 02750 /* 02751 * Find which day name matches the specified part of a string. 02752 * 'offset' is incremented by the length of the match. 02753 * Reply = day number (1 - 7), or <= 0 if no match. 02754 */ 02755 int matchDay(const QString &string, int &offset, KCalendarSystem *calendar) 02756 { 02757 int dayOfWeek; 02758 QString part = string.mid(offset); 02759 if (part.isEmpty()) 02760 return -1; 02761 if (calendar) 02762 { 02763 // Check for localised day name first 02764 for (dayOfWeek = 1; dayOfWeek <= 7; ++dayOfWeek) 02765 { 02766 QString name = calendar->weekDayName(dayOfWeek, KCalendarSystem::LongDayName); 02767 if (part.startsWith(name, Qt::CaseInsensitive)) 02768 { 02769 offset += name.length(); 02770 return dayOfWeek; 02771 } 02772 } 02773 for (dayOfWeek = 1; dayOfWeek <= 7; ++dayOfWeek) 02774 { 02775 QString name = calendar->weekDayName(dayOfWeek, KCalendarSystem::ShortDayName); 02776 if (part.startsWith(name, Qt::CaseInsensitive)) 02777 { 02778 offset += name.length(); 02779 return dayOfWeek; 02780 } 02781 } 02782 } 02783 02784 // Check for English day name 02785 dayOfWeek = findString(part, longDay, 7, offset); 02786 if (dayOfWeek < 0) 02787 dayOfWeek = findString(part, shortDay, 7, offset); 02788 return dayOfWeek + 1; 02789 } 02790 02791 /* 02792 * Find which month name matches the specified part of a string. 02793 * 'offset' is incremented by the length of the match. 02794 * Reply = month number (1 - 12), or <= 0 if no match. 02795 */ 02796 int matchMonth(const QString &string, int &offset, KCalendarSystem *calendar) 02797 { 02798 int month; 02799 QString part = string.mid(offset); 02800 if (part.isEmpty()) 02801 return -1; 02802 if (calendar) 02803 { 02804 // Check for localised month name first 02805 for (month = 1; month <= 12; ++month) 02806 { 02807 QString name = calendar->monthName(month, 2000, KCalendarSystem::LongName); 02808 if (part.startsWith(name, Qt::CaseInsensitive)) 02809 { 02810 offset += name.length(); 02811 return month; 02812 } 02813 } 02814 for (month = 1; month <= 12; ++month) 02815 { 02816 QString name = calendar->monthName(month, 2000, KCalendarSystem::ShortName); 02817 if (part.startsWith(name, Qt::CaseInsensitive)) 02818 { 02819 offset += name.length(); 02820 return month; 02821 } 02822 } 02823 } 02824 // Check for English month name 02825 month = findString(part, longMonth, 12, offset); 02826 if (month < 0) 02827 month = findString(part, shortMonth, 12, offset); 02828 return month + 1; 02829 } 02830 02831 /* 02832 * Read a UTC offset from the input string. 02833 */ 02834 bool getUTCOffset(const QString &string, int &offset, bool colon, int &result) 02835 { 02836 int sign; 02837 int len = string.length(); 02838 if (offset >= len) 02839 return false; 02840 switch (string[offset++].unicode()) 02841 { 02842 case '+': 02843 sign = 1; 02844 break; 02845 case '-': 02846 sign = -1; 02847 break; 02848 default: 02849 return false; 02850 } 02851 int tzhour = NO_NUMBER; 02852 int tzmin = NO_NUMBER; 02853 if (!getNumber(string, offset, 2, 2, 0, 99, tzhour)) 02854 return false; 02855 if (colon) 02856 { 02857 if (offset >= len || string[offset++] != QLatin1Char(':')) 02858 return false; 02859 } 02860 if (offset >= len || !string[offset].isDigit()) 02861 tzmin = 0; 02862 else 02863 { 02864 if (!getNumber(string, offset, 2, 2, 0, 59, tzmin)) 02865 return false; 02866 } 02867 tzmin += tzhour * 60; 02868 if (result != NO_NUMBER && result != tzmin) 02869 return false; 02870 result = tzmin; 02871 return true; 02872 } 02873 02874 /* 02875 * Read an am/pm indicator from the input string. 02876 * 'offset' is incremented by the length of the match. 02877 * Reply = 1 (am), 2 (pm), or 0 if no match. 02878 */ 02879 int getAmPm(const QString &string, int &offset, KLocale *locale) 02880 { 02881 QString part = string.mid(offset); 02882 int ap = 0; 02883 int n = 2; 02884 if (locale) 02885 { 02886 // Check localised form first 02887 QString aps = ki18n("am").toString(locale); 02888 if (part.startsWith(aps, Qt::CaseInsensitive)) 02889 { 02890 ap = 1; 02891 n = aps.length(); 02892 } 02893 else 02894 { 02895 aps = ki18n("pm").toString(locale); 02896 if (part.startsWith(aps, Qt::CaseInsensitive)) 02897 { 02898 ap = 2; 02899 n = aps.length(); 02900 } 02901 } 02902 } 02903 if (!ap) 02904 { 02905 if (part.startsWith(QLatin1String("am"), Qt::CaseInsensitive)) 02906 ap = 1; 02907 else if (part.startsWith(QLatin1String("pm"), Qt::CaseInsensitive)) 02908 ap = 2; 02909 } 02910 if (ap) 02911 offset += n; 02912 return ap; 02913 } 02914 02915 /* Convert part of 'string' to a number. 02916 * If converted number differs from any current value in 'result', the function fails. 02917 * Reply = true if successful. 02918 */ 02919 bool getNumber(const QString& string, int& offset, int mindigits, int maxdigits, int minval, int maxval, int& result) 02920 { 02921 int end = string.size(); 02922 bool neg = false; 02923 if (minval == NO_NUMBER && offset < end && string[offset] == QLatin1Char('-')) 02924 { 02925 neg = true; 02926 ++offset; 02927 } 02928 if (offset + maxdigits > end) 02929 maxdigits = end - offset; 02930 int ndigits; 02931 for (ndigits = 0; ndigits < maxdigits && string[offset + ndigits].isDigit(); ++ndigits) ; 02932 if (ndigits < mindigits) 02933 return false; 02934 bool ok; 02935 int n = string.mid(offset, ndigits).toInt(&ok); 02936 if (neg) 02937 n = -n; 02938 if (!ok || (result != NO_NUMBER && n != result) || (minval != NO_NUMBER && n < minval) || (n > maxval && maxval >= 0)) 02939 return false; 02940 result = n; 02941 offset += ndigits; 02942 return true; 02943 } 02944 02945 int findString_internal(const QString &string, const char *array, int count, int &offset, int disp) 02946 { 02947 for (int i = 0; i < count; ++i) 02948 { 02949 if (string.startsWith(QLatin1String(array + i * disp), Qt::CaseInsensitive)) 02950 { 02951 offset += qstrlen(array + i * disp); 02952 return i; 02953 } 02954 } 02955 return -1; 02956 } 02957 02958 /* 02959 * Return the QDate for a given year, month and day. 02960 * If in error, check whether the reason is that the year is out of range. 02961 * If so, return a valid (but wrong) date but with 'status' set to the 02962 * appropriate error code. If no error, 'status' is set to stValid. 02963 */ 02964 QDate checkDate(int year, int month, int day, Status &status) 02965 { 02966 status = stValid; 02967 QDate qdate(year, month, day); 02968 if (qdate.isValid()) 02969 return qdate; 02970 02971 // Invalid date - check whether it's simply out of range 02972 if (year < MIN_YEAR) 02973 { 02974 bool leap = (year % 4 == 0) && (year % 100 || year % 400 == 0); 02975 qdate.setYMD((leap ? 2000 : 2001), month, day); 02976 if (qdate.isValid()) 02977 status = stTooEarly; 02978 } 02979 return qdate; 02980 } 02981
KDE 4.6 API Reference