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

KDECore

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   WTF
  • KJSEmbed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  •   core
  • Phonon
  •   Backend
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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