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

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
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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