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

KDECore

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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.5
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