• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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 

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal