00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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>
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
00075 enum Status {
00076 stValid = 0,
00077 stTooEarly
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;
00095 static const int NO_NUMBER = 0x8000000;
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
00109 KTimeZone tz;
00110 int utcOffset;
00111 KDateTime::SpecType 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
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
00247
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
00276
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
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
00412
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;
00425 }
00426 }
00427
00428
00429 static KDateTime::Spec& fromStringDefault()
00430 {
00431 return *s_fromStringDefault;
00432 }
00433
00434
00435 static QTime sod;
00436
00437
00438
00439
00440
00441 private:
00442 QDateTime mDt;
00443 public:
00444 KTimeZone specZone;
00445
00446 int specUtcOffset;
00447 mutable struct ut {
00448 QDate date;
00449 QTime time;
00450 } ut;
00451 private:
00452 mutable struct converted {
00453 QDate date;
00454 QTime time;
00455 KTimeZone tz;
00456 } converted;
00457 public:
00458 KDateTime::SpecType specType : 3;
00459 Status status : 2;
00460 mutable bool utcCached : 1;
00461 mutable bool convertedCached : 1;
00462 mutable bool m2ndOccurrence : 1;
00463 private:
00464 bool mDateOnly : 1;
00465 mutable bool converted2ndOccur : 1;
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();
00519 utcCached = true;
00520
00521 case KDateTime::UTC:
00522 default:
00523 break;
00524 }
00525 }
00526 convertedCached = false;
00527 setDtTimeSpec((specType == KDateTime::UTC) ? Qt::UTC : Qt::LocalTime);
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
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:
00581 break;
00582 }
00583 }
00584
00585
00586
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);
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
00617 QDateTime utcdt = mDt;
00618 utcdt.setTimeSpec(Qt::UTC);
00619 setUtc(utcdt.addSecs(-offset));
00620 }
00621 return offset;
00622 }
00623
00624
00625
00626
00627
00628
00629 QDateTime KDateTimePrivate::toUtc(const KTimeZone &local) const
00630 {
00631 KTimeZone loc(local);
00632 if (utcCached)
00633 {
00634
00635 if (specType == KDateTime::ClockTime)
00636 {
00637
00638
00639 if (!local.isValid())
00640 loc = KSystemTimeZones::local();
00641 if (specZone == loc)
00642 {
00643
00644 #ifdef COMPILING_TESTS
00645 ++KDateTime_utcCacheHit;
00646 #endif
00647 return utc();
00648 }
00649 }
00650 else
00651 {
00652
00653 #ifdef COMPILING_TESTS
00654 ++KDateTime_utcCacheHit;
00655 #endif
00656 return utc();
00657 }
00658 }
00659
00660
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
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
00684 return dt;
00685 }
00686 case KDateTime::TimeZone:
00687 if (!mDt.isValid())
00688 break;
00689 timeZoneOffset();
00690
00691 return utc();
00692 default:
00693 break;
00694 }
00695
00696
00697 ut.date = QDate();
00698 utcCached = true;
00699 convertedCached = false;
00700
00701 return mDt;
00702 }
00703
00704
00705
00706
00707
00708 QDateTime KDateTimePrivate::toZone(const KTimeZone &zone, const KTimeZone &local) const
00709 {
00710 if (convertedCached && converted.tz == zone)
00711 {
00712
00713 #ifdef COMPILING_TESTS
00714
00715 ++KDateTime_zoneCacheHit;
00716 #endif
00717 return QDateTime(converted.date, converted.time, Qt::LocalTime);
00718 }
00719 else
00720 {
00721
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
00734
00735
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();
00749 newd->ut.time = mDt.time();
00750 break;
00751 case KDateTime::TimeZone:
00752
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
00770 KDateTime::KDateTime()
00771 : d(new KDateTimePrivate)
00772 {
00773 }
00774
00775 KDateTime::KDateTime(const QDate &date, const Spec &spec)
00776 : d(new KDateTimePrivate(QDateTime(date, KDateTimePrivate::sod, Qt::LocalTime), spec, true))
00777 {
00778 if (spec.type() == UTC)
00779 d->setDtTimeSpec(Qt::UTC);
00780 }
00781
00782 KDateTime::KDateTime(const QDate &date, const QTime &time, const Spec &spec)
00783 : d(new KDateTimePrivate(QDateTime(date, time, Qt::LocalTime), spec))
00784 {
00785 if (spec.type() == UTC)
00786 d->setDtTimeSpec(Qt::UTC);
00787 }
00788
00789 KDateTime::KDateTime(const QDateTime &dt, const Spec &spec)
00790 : d(new KDateTimePrivate(dt, spec))
00791 {
00792
00793 if (spec.type() == UTC)
00794 {
00795 if (dt.timeSpec() == Qt::LocalTime)
00796 d->setUtcFromTz(dt, KSystemTimeZones::local());
00797 }
00798 else if (dt.timeSpec() == Qt::UTC)
00799 d->setDtFromUtc(dt);
00800 }
00801
00802 KDateTime::KDateTime(const QDateTime &dt)
00803 : d(new KDateTimePrivate(dt, (dt.timeSpec() == Qt::LocalTime ? Spec(LocalZone) : Spec(UTC))))
00804 {
00805 }
00806
00807 KDateTime::KDateTime(const KDateTime &other)
00808 : d(other.d)
00809 {
00810 }
00811
00812 KDateTime::~KDateTime()
00813 {
00814 }
00815
00816 KDateTime &KDateTime::operator=(const KDateTime &other)
00817 {
00818 d = other.d;
00819 return *this;
00820 }
00821
00822 void KDateTime::detach() { d.detach(); }
00823 bool KDateTime::isNull() const { return d->dt().isNull(); }
00824 bool KDateTime::isValid() const { return d->specType != Invalid && d->dt().isValid(); }
00825 bool KDateTime::outOfRange() const { return d->status == stTooEarly; }
00826 bool KDateTime::isDateOnly() const { return d->dateOnly(); }
00827 bool KDateTime::isLocalZone() const { return d->specType == TimeZone && d->specZone == KSystemTimeZones::local(); }
00828 bool KDateTime::isClockTime() const { return d->specType == ClockTime; }
00829 bool KDateTime::isUtc() const { return d->specType == UTC || (d->specType == OffsetFromUTC && d->specUtcOffset == 0); }
00830 bool KDateTime::isOffsetFromUtc() const { return d->specType == OffsetFromUTC; }
00831 bool KDateTime::isSecondOccurrence() const { return d->specType == TimeZone && d->secondOccurrence(); }
00832 QDate KDateTime::date() const { return d->date(); }
00833 QTime KDateTime::time() const { return d->dt().time(); }
00834 QDateTime KDateTime::dateTime() const { return d->dt(); }
00835
00836 KDateTime::Spec KDateTime::timeSpec() const { return d->spec(); }
00837 KDateTime::SpecType KDateTime::timeType() const { return d->specType; }
00838
00839 KTimeZone KDateTime::timeZone() const
00840 {
00841 switch (d->specType)
00842 {
00843 case TimeZone:
00844 return d->specZone;
00845 case UTC:
00846 return KTimeZone::utc();
00847 default:
00848 return KTimeZone();
00849 }
00850 }
00851
00852 int KDateTime::utcOffset() const
00853 {
00854 switch (d->specType)
00855 {
00856 case TimeZone:
00857 return d->timeZoneOffset();
00858 case OffsetFromUTC:
00859 return d->specUtcOffset;
00860 default:
00861 return 0;
00862 }
00863 }
00864
00865 KDateTime KDateTime::toUtc() const
00866 {
00867 if (!isValid())
00868 return KDateTime();
00869 if (d->specType == UTC)
00870 return *this;
00871 if (d->dateOnly())
00872 return KDateTime(d->date(), UTC);
00873 QDateTime udt = d->toUtc();
00874 if (!udt.isValid())
00875 return KDateTime();
00876 return KDateTime(udt, UTC);
00877 }
00878
00879 KDateTime KDateTime::toOffsetFromUtc() const
00880 {
00881 if (!isValid())
00882 return KDateTime();
00883 int offset = 0;
00884 switch (d->specType)
00885 {
00886 case OffsetFromUTC:
00887 return *this;
00888 case UTC:
00889 {
00890 if (d->dateOnly())
00891 return KDateTime(d->date(), Spec(OffsetFromUTC, 0));
00892 QDateTime qdt = d->dt();
00893 qdt.setTimeSpec(Qt::LocalTime);
00894 return KDateTime(qdt, Spec(OffsetFromUTC, 0));
00895 }
00896 case TimeZone:
00897 offset = d->timeZoneOffset();
00898 break;
00899 case ClockTime:
00900 offset = KSystemTimeZones::local().offsetAtZoneTime(d->dt());
00901 break;
00902 default:
00903 return KDateTime();
00904 }
00905 if (d->dateOnly())
00906 return KDateTime(d->date(), Spec(OffsetFromUTC, offset));
00907 return KDateTime(d->dt(), Spec(OffsetFromUTC, offset));
00908 }
00909
00910 KDateTime KDateTime::toOffsetFromUtc(int utcOffset) const
00911 {
00912 if (!isValid())
00913 return KDateTime();
00914 if (d->specType == OffsetFromUTC && d->specUtcOffset == utcOffset)
00915 return *this;
00916 if (d->dateOnly())
00917 return KDateTime(d->date(), Spec(OffsetFromUTC, utcOffset));
00918 return KDateTime(d->toUtc(), Spec(OffsetFromUTC, utcOffset));
00919 }
00920
00921 KDateTime KDateTime::toLocalZone() const
00922 {
00923 if (!isValid())
00924 return KDateTime();
00925 KTimeZone local = KSystemTimeZones::local();
00926 if (d->specType == TimeZone && d->specZone == local)
00927 return *this;
00928 if (d->dateOnly())
00929 return KDateTime(d->date(), Spec(local));
00930 switch (d->specType)
00931 {
00932 case TimeZone:
00933 case OffsetFromUTC:
00934 case UTC:
00935 {
00936 KDateTime result;
00937 d->newToZone(result.d, local, local);
00938 return result;
00939 }
00940 case ClockTime:
00941 return KDateTime(d->dt(), Spec(local));
00942 default:
00943 return KDateTime();
00944 }
00945 }
00946
00947 KDateTime KDateTime::toClockTime() const
00948 {
00949 if (!isValid())
00950 return KDateTime();
00951 if (d->specType == ClockTime)
00952 return *this;
00953 if (d->dateOnly())
00954 return KDateTime(d->date(), Spec(ClockTime));
00955 KDateTime result = toLocalZone();
00956 result.d->specType = ClockTime;
00957 return result;
00958 }
00959
00960 KDateTime KDateTime::toZone(const KTimeZone &zone) const
00961 {
00962 if (!zone.isValid() || !isValid())
00963 return KDateTime();
00964 if (d->specType == TimeZone && d->specZone == zone)
00965 return *this;
00966 if (d->dateOnly())
00967 return KDateTime(d->date(), Spec(zone));
00968 KDateTime result;
00969 d->newToZone(result.d, zone);
00970 return result;
00971 }
00972
00973 KDateTime KDateTime::toTimeSpec(const KDateTime &dt) const
00974 {
00975 return toTimeSpec(dt.timeSpec());
00976 }
00977
00978 KDateTime KDateTime::toTimeSpec(const Spec &spec) const
00979 {
00980 if (spec == d->spec())
00981 return *this;
00982 if (!isValid())
00983 return KDateTime();
00984 if (d->dateOnly())
00985 return KDateTime(d->date(), spec);
00986 if (spec.type() == TimeZone)
00987 {
00988 KDateTime result;
00989 d->newToZone(result.d, spec.timeZone());
00990 return result;
00991 }
00992 return KDateTime(d->toUtc(), spec);
00993 }
00994
00995 uint KDateTime::toTime_t() const
00996 {
00997 QDateTime qdt = d->toUtc();
00998 if (!qdt.isValid())
00999 return uint(-1);
01000 return qdt.toTime_t();
01001 }
01002
01003 void KDateTime::setTime_t(qint64 seconds)
01004 {
01005 d->setSpec(UTC);
01006 int days = static_cast<int>(seconds / 86400);
01007 int secs = static_cast<int>(seconds % 86400);
01008 QDateTime dt;
01009 dt.setTimeSpec(Qt::UTC);
01010 dt.setTime_t(0);
01011 d->setDt(dt.addDays(days).addSecs(secs));
01012 }
01013
01014 void KDateTime::setDateOnly(bool dateOnly)
01015 {
01016 d->setDateOnly(dateOnly);
01017 }
01018
01019 void KDateTime::setDate(const QDate &date)
01020 {
01021 d->setDate(date);
01022 }
01023
01024 void KDateTime::setTime(const QTime &time)
01025 {
01026 d->setTime(time);
01027 }
01028
01029 void KDateTime::setDateTime(const QDateTime &dt)
01030 {
01031 d->clearCache();
01032 d->setDateOnly(false);
01033 if (dt.timeSpec() == Qt::LocalTime)
01034 {
01035 if (d->specType == UTC)
01036 d->setUtcFromTz(dt, KSystemTimeZones::local());
01037 else
01038 d->setDt(dt);
01039 }
01040 else
01041 d->setDtFromUtc(dt);
01042 }
01043
01044 void KDateTime::setTimeSpec(const Spec &other)
01045 {
01046 d->setSpec(other);
01047 }
01048
01049 void KDateTime::setSecondOccurrence(bool second)
01050 {
01051 if (d->specType == KDateTime::TimeZone && second != d->m2ndOccurrence)
01052 {
01053 d->m2ndOccurrence = second;
01054 d->clearCache();
01055 if (second)
01056 {
01057
01058
01059 d->timeZoneOffset();
01060 }
01061 }
01062 }
01063
01064 KDateTime KDateTime::addMSecs(qint64 msecs) const
01065 {
01066 if (!msecs)
01067 return *this;
01068 if (!isValid())
01069 return KDateTime();
01070 if (d->dateOnly())
01071 {
01072 KDateTime result(*this);
01073 result.d->setDate(d->date().addDays(static_cast<int>(msecs / 86400000)));
01074 return result;
01075 }
01076 qint64 secs = msecs / 1000;
01077 int oldms = d->dt().time().msec();
01078 int ms = oldms + static_cast<int>(msecs % 1000);
01079 if (msecs >= 0)
01080 {
01081 if (ms >= 1000)
01082 {
01083 ++secs;
01084 ms -= 1000;
01085 }
01086 }
01087 else
01088 {
01089 if (ms < 0)
01090 {
01091 --secs;
01092 ms += 1000;
01093 }
01094 }
01095 KDateTime result = addSecs(secs);
01096 QTime t = result.time();
01097 result.d->setTime(QTime(t.hour(), t.minute(), t.second(), ms));
01098 return result;
01099 }
01100
01101 KDateTime KDateTime::addSecs(qint64 secs) const
01102 {
01103 if (!secs)
01104 return *this;
01105 if (!isValid())
01106 return KDateTime();
01107 int days = static_cast<int>(secs / 86400);
01108 int seconds = static_cast<int>(secs % 86400);
01109 if (d->dateOnly())
01110 {
01111 KDateTime result(*this);
01112 result.d->setDate(d->date().addDays(days));
01113 return result;
01114 }
01115 if (d->specType == ClockTime)
01116 {
01117 QDateTime qdt = d->dt();
01118 qdt.setTimeSpec(Qt::UTC);
01119 qdt = qdt.addDays(days).addSecs(seconds);
01120 qdt.setTimeSpec(Qt::LocalTime);
01121 return KDateTime(qdt, Spec(ClockTime));
01122 }
01123 return KDateTime(d->toUtc().addDays(days).addSecs(seconds), d->spec());
01124 }
01125
01126 KDateTime KDateTime::addDays(int days) const
01127 {
01128 if (!days)
01129 return *this;
01130 KDateTime result(*this);
01131 result.d->setDate(d->date().addDays(days));
01132 return result;
01133 }
01134
01135 KDateTime KDateTime::addMonths(int months) const
01136 {
01137 if (!months)
01138 return *this;
01139 KDateTime result(*this);
01140 result.d->setDate(d->date().addMonths(months));
01141 return result;
01142 }
01143
01144 KDateTime KDateTime::addYears(int years) const
01145 {
01146 if (!years)
01147 return *this;
01148 KDateTime result(*this);
01149 result.d->setDate(d->date().addYears(years));
01150 return result;
01151 }
01152
01153 int KDateTime::secsTo(const KDateTime &t2) const
01154 {
01155 return static_cast<int>(secsTo_long(t2));
01156 }
01157
01158 qint64 KDateTime::secsTo_long(const KDateTime &t2) const
01159 {
01160 if (!isValid() || !t2.isValid())
01161 return 0;
01162 if (d->dateOnly())
01163 {
01164 QDate dat = t2.d->dateOnly() ? t2.d->date() : t2.toTimeSpec(d->spec()).d->date();
01165 return static_cast<qint64>(d->date().daysTo(dat)) * 86400;
01166 }
01167 if (t2.d->dateOnly())
01168 return static_cast<qint64>(toTimeSpec(t2.d->spec()).d->date().daysTo(t2.d->date())) * 86400;
01169
01170 QDateTime dt1, dt2;
01171 if (d->specType == ClockTime && t2.d->specType == ClockTime)
01172 {
01173
01174 dt1 = d->dt();
01175 dt1.setTimeSpec(Qt::UTC);
01176 dt2 = t2.d->dt();
01177 dt2.setTimeSpec(Qt::UTC);
01178 return dt1.secsTo(dt2);
01179 }
01180 else
01181 {
01182 dt1 = d->toUtc();
01183 dt2 = t2.d->toUtc();
01184 }
01185 return static_cast<qint64>(dt1.date().daysTo(dt2.date())) * 86400
01186 + dt1.time().secsTo(dt2.time());
01187 }
01188
01189 int KDateTime::daysTo(const KDateTime &t2) const
01190 {
01191 if (!isValid() || !t2.isValid())
01192 return 0;
01193 if (d->dateOnly())
01194 {
01195 QDate dat = t2.d->dateOnly() ? t2.d->date() : t2.toTimeSpec(d->spec()).d->date();
01196 return d->date().daysTo(dat);
01197 }
01198 if (t2.d->dateOnly())
01199 return toTimeSpec(t2.d->spec()).d->date().daysTo(t2.d->date());
01200
01201 QDate dat;
01202 switch (d->specType)
01203 {
01204 case UTC:
01205 dat = t2.d->toUtc().date();
01206 break;
01207 case OffsetFromUTC:
01208 dat = t2.d->toUtc().addSecs(d->specUtcOffset).date();
01209 break;
01210 case TimeZone:
01211 dat = t2.d->toZone(d->specZone).date();
01212 break;
01213 case ClockTime:
01214 {
01215 KTimeZone local = KSystemTimeZones::local();
01216 dat = t2.d->toZone(local, local).date();
01217 break;
01218 }
01219 default:
01220 return 0;
01221 }
01222 return d->date().daysTo(dat);
01223 }
01224
01225 KDateTime KDateTime::currentLocalDateTime()
01226 {
01227 return KDateTime(QDateTime::currentDateTime(), Spec(KSystemTimeZones::local()));
01228 }
01229
01230 KDateTime KDateTime::currentUtcDateTime()
01231 {
01232 #ifdef Q_OS_WIN
01233 SYSTEMTIME st;
01234 memset(&st, 0, sizeof(SYSTEMTIME));
01235 GetSystemTime(&st);
01236 return KDateTime(QDate(st.wYear, st.wMonth, st.wDay),
01237 QTime(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds),
01238 UTC);
01239 #else
01240 time_t t;
01241 ::time(&t);
01242 KDateTime result;
01243 result.setTime_t(static_cast<qint64>(t));
01244 return result;
01245 #endif
01246 }
01247
01248 KDateTime KDateTime::currentDateTime(const Spec &spec)
01249 {
01250 return currentUtcDateTime().toTimeSpec(spec);
01251 }
01252
01253 KDateTime::Comparison KDateTime::compare(const KDateTime &other) const
01254 {
01255 QDateTime start1, start2;
01256 bool conv = (!d->equalSpec(*other.d) || d->secondOccurrence() != other.d->secondOccurrence());
01257 if (conv)
01258 {
01259
01260
01261 start1 = d->toUtc();
01262 start2 = other.d->toUtc();
01263 }
01264 else
01265 {
01266
01267 start1 = d->dt();
01268 start2 = other.d->dt();
01269 }
01270 if (d->dateOnly() || other.d->dateOnly())
01271 {
01272
01273
01274 QDateTime end1, end2;
01275 if (conv)
01276 {
01277 if (d->dateOnly())
01278 {
01279 KDateTime kdt(*this);
01280 kdt.setTime(QTime(23,59,59,999));
01281 end1 = kdt.d->toUtc();
01282 }
01283 else
01284 end1 = start1;
01285 if (other.d->dateOnly())
01286 {
01287 KDateTime kdt(other);
01288 kdt.setTime(QTime(23,59,59,999));
01289 end2 = kdt.d->toUtc();
01290 }
01291 else
01292 end2 = start2;
01293 }
01294 else
01295 {
01296 if (d->dateOnly())
01297 end1 = QDateTime(d->date(), QTime(23,59,59,999), Qt::LocalTime);
01298 else
01299 end1 = d->dt();
01300 if (other.d->dateOnly())
01301 end2 = QDateTime(other.d->date(), QTime(23,59,59,999), Qt::LocalTime);
01302 else
01303 end2 = other.d->dt();
01304 }
01305 if (start1 == start2)
01306 return !d->dateOnly() ? AtStart : (end1 == end2) ? Equal
01307 : (end1 < end2) ? static_cast<Comparison>(AtStart|Inside)
01308 : static_cast<Comparison>(AtStart|Inside|AtEnd|After);
01309 if (start1 < start2)
01310 return (end1 < start2) ? Before
01311 : (end1 == end2) ? static_cast<Comparison>(Before|AtStart|Inside|AtEnd)
01312 : (end1 == start2) ? static_cast<Comparison>(Before|AtStart)
01313 : (end1 < end2) ? static_cast<Comparison>(Before|AtStart|Inside) : Outside;
01314 else
01315 return (start1 > end2) ? After
01316 : (start1 == end2) ? (end1 == end2 ? AtEnd : static_cast<Comparison>(AtEnd|After))
01317 : (end1 == end2) ? static_cast<Comparison>(Inside|AtEnd)
01318 : (end1 < end2) ? Inside : static_cast<Comparison>(Inside|AtEnd|After);
01319 }
01320 return (start1 == start2) ? Equal : (start1 < start2) ? Before : After;
01321 }
01322
01323 bool KDateTime::operator==(const KDateTime &other) const
01324 {
01325 if (d == other.d)
01326 return true;
01327 if (d->dateOnly() != other.d->dateOnly())
01328 return false;
01329 if (d->equalSpec(*other.d))
01330 {
01331
01332 if (d->dateOnly())
01333 return d->date() == other.d->date();
01334 else
01335 return d->secondOccurrence() == other.d->secondOccurrence()
01336 && d->dt() == other.d->dt();
01337 }
01338 if (d->dateOnly())
01339 {
01340
01341
01342 if (qAbs(d->date().daysTo(other.d->date())) > 2)
01343 return false;
01344 if (d->toUtc() != other.d->toUtc())
01345 return false;
01346 KDateTime end1(*this);
01347 end1.setTime(QTime(23,59,59,999));
01348 KDateTime end2(other);
01349 end2.setTime(QTime(23,59,59,999));
01350 return end1.d->toUtc() == end2.d->toUtc();
01351 }
01352 return d->toUtc() == other.d->toUtc();
01353 }
01354
01355 bool KDateTime::operator<(const KDateTime &other) const
01356 {
01357 if (d == other.d)
01358 return false;
01359 if (d->equalSpec(*other.d))
01360 {
01361
01362 if (d->dateOnly() || other.d->dateOnly())
01363 return d->date() < other.d->date();
01364 if (d->secondOccurrence() == other.d->secondOccurrence())
01365 return d->dt() < other.d->dt();
01366
01367
01368
01369 int diff = d->dt().date().daysTo(other.d->dt().date());
01370 if (diff > 1)
01371 return true;
01372 if (diff < -1)
01373 return false;
01374 }
01375 if (d->dateOnly())
01376 {
01377
01378
01379
01380
01381 KDateTime kdt(*this);
01382 kdt.setTime(QTime(23,59,59,999));
01383 return kdt.d->toUtc() < other.d->toUtc();
01384 }
01385 return d->toUtc() < other.d->toUtc();
01386 }
01387
01388 QString KDateTime::toString(const QString &format) const
01389 {
01390 if (!isValid())
01391 return QString();
01392 enum { TZNone, UTCOffsetShort, UTCOffset, UTCOffsetColon, TZAbbrev, TZName };
01393 KLocale *locale = KGlobal::locale();
01394 KCalendarSystemGregorian calendar(locale);
01395 QString result;
01396 QString s;
01397 int num, numLength, zone;
01398 bool escape = false;
01399 ushort flag = 0;
01400 for (int i = 0, end = format.length(); i < end; ++i)
01401 {
01402 zone = TZNone;
01403 num = NO_NUMBER;
01404 numLength = 0;
01405 ushort ch = format[i].unicode();
01406 if (!escape)
01407 {
01408 if (ch == '%')
01409 escape = true;
01410 else
01411 result += format[i];
01412 continue;
01413 }
01414 if (!flag)
01415 {
01416 switch (ch)
01417 {
01418 case '%':
01419 result += QLatin1Char('%');
01420 break;
01421 case ':':
01422 flag = ch;
01423 break;
01424 case 'Y':
01425 num = d->date().year();
01426 numLength = 4;
01427 break;
01428 case 'y':
01429 num = d->date().year() % 100;
01430 numLength = 2;
01431 break;
01432 case 'm':
01433 numLength = 2;
01434 num = d->date().month();
01435 break;
01436 case 'B':
01437 result += calendar.monthName(d->date().month(), 2000, KCalendarSystem::LongName);
01438 break;
01439 case 'b':
01440 result += calendar.monthName(d->date().month(), 2000, KCalendarSystem::ShortName);
01441 break;
01442 case 'd':
01443 numLength = 2;
01444
01445 case 'e':
01446 num = d->date().day();
01447 break;
01448 case 'A':
01449 result += calendar.weekDayName(d->date().dayOfWeek(), KCalendarSystem::LongDayName);
01450 break;
01451 case 'a':
01452 result += calendar.weekDayName(d->date().dayOfWeek(), KCalendarSystem::ShortDayName);
01453 break;
01454 case 'H':
01455 numLength = 2;
01456
01457 case 'k':
01458 num = d->dt().time().hour();
01459 break;
01460 case 'I':
01461 numLength = 2;
01462
01463 case 'l':
01464 num = (d->dt().time().hour() + 11) % 12 + 1;
01465 break;
01466 case 'M':
01467 num = d->dt().time().minute();
01468 numLength = 2;
01469 break;
01470 case 'S':
01471 num = d->dt().time().second();
01472 numLength = 2;
01473 break;
01474 case 'P':
01475 {
01476 bool am = (d->dt().time().hour() < 12);
01477 QString ap = ki18n(am ? "am" : "pm").toString(locale);
01478 if (ap.isEmpty())
01479 result += am ? QLatin1String("am") : QLatin1String("pm");
01480 else
01481 result += ap;
01482 break;
01483 }
01484 case 'p':
01485 {
01486 bool am = (d->dt().time().hour() < 12);
01487 QString ap = ki18n(am ? "am" : "pm").toString(locale).toUpper();
01488 if (ap.isEmpty())
01489 result += am ? QLatin1String("AM") : QLatin1String("PM");
01490 else
01491 result += ap;
01492 break;
01493 }
01494 case 'z':
01495 zone = UTCOffset;
01496 break;
01497 case 'Z':
01498 zone = TZAbbrev;
01499 break;
01500 default:
01501 result += QLatin1Char('%');
01502 result += format[i];
01503 break;
01504 }
01505 }
01506 else if (flag == ':')
01507 {
01508
01509 switch (ch)
01510 {
01511 case 'A':
01512 result += longDay[d->date().dayOfWeek() - 1];
01513 break;
01514 case 'a':
01515 result += shortDay[d->date().dayOfWeek() - 1];
01516 break;
01517 case 'B':
01518 result += longMonth[d->date().month() - 1];
01519 break;
01520 case 'b':
01521 result += shortMonth[d->date().month() - 1];
01522 break;
01523 case 'm':
01524 num = d->date().month();
01525 break;
01526 case 'P':
01527 result += (d->dt().time().hour() < 12) ? QLatin1String("am") : QLatin1String("pm");
01528 break;
01529 case 'p':
01530 result += (d->dt().time().hour() < 12) ? QLatin1String("AM") : QLatin1String("PM");
01531 break;
01532 case 'S':
01533 {
01534 int sec = d->dt().time().second();
01535 if (sec || d->dt().time().msec())
01536 {
01537 result += QLatin1Char(':');
01538 num = sec;
01539 numLength = 2;
01540 }
01541 break;
01542 }
01543 case 's':
01544 result += s.sprintf("%03d", d->dt().time().msec());
01545 break;
01546 case 'u':
01547 zone = UTCOffsetShort;
01548 break;
01549 case 'z':
01550 zone = UTCOffsetColon;
01551 break;
01552 case 'Z':
01553 zone = TZName;
01554 break;
01555 default:
01556 result += QLatin1String("%:");
01557 result += format[i];
01558 break;
01559 }
01560 flag = 0;
01561 }
01562 if (!flag)
01563 escape = false;
01564
01565
01566 if (num != NO_NUMBER)
01567 {
01568 if (!numLength)
01569 result += QString::number(num);
01570 else if (numLength == 2 || numLength == 4)
01571 {
01572 if (num < 0)
01573 {
01574 num = -num;
01575 result += '-';
01576 }
01577 result += s.sprintf((numLength == 2 ? "%02d" : "%04d"), num);
01578 }
01579 }
01580 else if (zone != TZNone)
01581 {
01582 KTimeZone tz;
01583 int offset;
01584 switch (d->specType)
01585 {
01586 case UTC:
01587 case TimeZone:
01588 tz = (d->specType == TimeZone) ? d->specZone : KTimeZone::utc();
01589
01590 case OffsetFromUTC:
01591 offset = (d->specType == TimeZone) ? d->timeZoneOffset()
01592 : (d->specType == OffsetFromUTC) ? d->specUtcOffset : 0;
01593 offset /= 60;
01594 switch (zone)
01595 {
01596 case UTCOffsetShort:
01597 case UTCOffset:
01598 case UTCOffsetColon:
01599 {
01600 if (offset >= 0)
01601 result += QLatin1Char('+');
01602 else
01603 {
01604 result += QLatin1Char('-');
01605 offset = -offset;
01606 }
01607 QString s;
01608 result += s.sprintf(((zone == UTCOffsetColon) ? "%02d:" : "%02d"), offset/60);
01609 if (ch != 'u' || offset % 60)
01610 result += s.sprintf("%02d", offset % 60);
01611 break;
01612 }
01613 case TZAbbrev:
01614 if (tz.isValid() && d->specType != OffsetFromUTC)
01615 result += tz.abbreviation(d->toUtc());
01616 break;
01617 case TZName:
01618 if (tz.isValid() && d->specType != OffsetFromUTC)
01619 result += tz.name();
01620 break;
01621 }
01622 break;
01623 default:
01624 break;
01625 }
01626 }
01627 }
01628 return result;
01629 }
01630
01631 QString KDateTime::toString(TimeFormat format) const
01632 {
01633 QString result;
01634 if (!isValid())
01635 return result;
01636
01637 QString s;
01638 char tzsign = '+';
01639 int offset = 0;
01640 const char *tzcolon = "";
01641 KTimeZone tz;
01642 switch (format)
01643 {
01644 case RFCDateDay:
01645 result += shortDay[d->date().dayOfWeek() - 1];
01646 result += QLatin1String(", ");
01647
01648 case RFCDate:
01649 {
01650 char seconds[8] = { 0 };
01651 if (d->dt().time().second())
01652 sprintf(seconds, ":%02d", d->dt().time().second());
01653 result += s.sprintf("%02d %s ", d->date().day(), shortMonth[d->date().month() - 1]);
01654 int year = d->date().year();
01655 if (year < 0)
01656 {
01657 result += QLatin1Char('-');
01658 year = -year;
01659 }
01660 result += s.sprintf("%04d %02d:%02d%s ",
01661 year, d->dt().time().hour(), d->dt().time().minute(), seconds);
01662 if (d->specType == ClockTime)
01663 tz = KSystemTimeZones::local();
01664 break;
01665 }
01666 case ISODate:
01667 {
01668
01669 int year = d->date().year();
01670 if (year < 0)
01671 {
01672 result += QLatin1Char('-');
01673 year = -year;
01674 }
01675 QString s;
01676 result += s.sprintf("%04d-%02d-%02d",
01677 year, d->date().month(), d->date().day());
01678 if (!d->dateOnly() || d->specType != ClockTime)
01679 {
01680 result += s.sprintf("T%02d:%02d:%02d",
01681 d->dt().time().hour(), d->dt().time().minute(), d->dt().time().second());
01682 if (d->dt().time().msec())
01683 {
01684
01685
01686 KLocale *locale = KGlobal::locale();
01687 result += (locale && locale->decimalSymbol() == QLatin1String(".")) ? QLatin1Char('.') : QLatin1Char(',');
01688 result += s.sprintf("%03d", d->dt().time().msec());
01689 }
01690 }
01691 if (d->specType == UTC)
01692 return result + QLatin1Char('Z');
01693 if (d->specType == ClockTime)
01694 return result;
01695 tzcolon = ":";
01696 break;
01697 }
01698
01699 case QtTextDate:
01700 case LocalDate:
01701 {
01702 Qt::DateFormat qtfmt = (format == QtTextDate) ? Qt::TextDate : Qt::LocalDate;
01703 if (d->dateOnly())
01704 result = d->date().toString(qtfmt);
01705 else
01706 result = d->dt().toString(qtfmt);
01707 if (result.isEmpty() || d->specType == ClockTime)
01708 return result;
01709 result += QLatin1Char(' ');
01710 break;
01711 }
01712 default:
01713 return result;
01714 }
01715
01716
01717 if (d->specType == OffsetFromUTC || d->specType == TimeZone || tz.isValid())
01718 {
01719 if (d->specType == TimeZone)
01720 offset = d->timeZoneOffset();
01721 else
01722 offset = tz.isValid() ? tz.offsetAtZoneTime(d->dt()) : d->specUtcOffset;
01723 if (offset < 0)
01724 {
01725 offset = -offset;
01726 tzsign = '-';
01727 }
01728 }
01729 offset /= 60;
01730 return result + s.sprintf("%c%02d%s%02d", tzsign, offset/60, tzcolon, offset%60);
01731 }
01732
01733 KDateTime KDateTime::fromString(const QString &string, TimeFormat format, bool *negZero)
01734 {
01735 if (negZero)
01736 *negZero = false;
01737 QString str = string.trimmed();
01738 if (str.isEmpty())
01739 return KDateTime();
01740
01741 switch (format)
01742 {
01743 case RFCDateDay:
01744 case RFCDate:
01745 {
01746 int nyear = 6;
01747 int nmonth = 4;
01748 int nday = 2;
01749 int nwday = 1;
01750 int nhour = 7;
01751 int nmin = 8;
01752 int nsec = 9;
01753
01754 QRegExp rx("^(?:([A-Z][a-z]+),\\s*)?(\\d{1,2})(\\s+|-)([^-\\s]+)(\\s+|-)(\\d{2,4})\\s+(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s+(\\S+)$");
01755 QStringList parts;
01756 if (!str.indexOf(rx))
01757 {
01758
01759 parts = rx.capturedTexts();
01760 bool h1 = (parts[3] == QLatin1String("-"));
01761 bool h2 = (parts[5] == QLatin1String("-"));
01762 if (h1 != h2)
01763 break;
01764 }
01765 else
01766 {
01767
01768 rx = QRegExp("^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$");
01769 if (str.indexOf(rx))
01770 break;
01771 nyear = 7;
01772 nmonth = 2;
01773 nday = 3;
01774 nwday = 1;
01775 nhour = 4;
01776 nmin = 5;
01777 nsec = 6;
01778 parts = rx.capturedTexts();
01779 }
01780 bool ok[4];
01781 int day = parts[nday].toInt(&ok[0]);
01782 int year = parts[nyear].toInt(&ok[1]);
01783 int hour = parts[nhour].toInt(&ok[2]);
01784 int minute = parts[nmin].toInt(&ok[3]);
01785 if (!ok[0] || !ok[1] || !ok[2] || !ok[3])
01786 break;
01787 int second = 0;
01788 if (!parts[nsec].isEmpty())
01789 {
01790 second = parts[nsec].toInt(&ok[0]);
01791 if (!ok[0])
01792 break;
01793 }
01794 bool leapSecond = (second == 60);
01795 if (leapSecond)
01796 second = 59;
01797 int month = 0;
01798 for ( ; month < 12 && parts[nmonth] != shortMonth[month]; ++month) ;
01799 int dayOfWeek = -1;
01800 if (!parts[nwday].isEmpty())
01801 {
01802
01803 while (++dayOfWeek < 7 && shortDay[dayOfWeek] != parts[nwday]) ;
01804 if (dayOfWeek >= 7)
01805 for (dayOfWeek = 0; dayOfWeek < 7 && longDay[dayOfWeek] != parts[nwday]; ++dayOfWeek) ;
01806 }
01807 if (month >= 12 || dayOfWeek >= 7
01808 || (dayOfWeek < 0 && format == RFCDateDay))
01809 break;
01810 int i = parts[nyear].size();
01811 if (i < 4)
01812 {
01813
01814 year += (i == 2 && year < 50) ? 2000 : 1900;
01815 }
01816
01817
01818 int offset = 0;
01819 bool negOffset = false;
01820 if (parts.count() > 10)
01821 {
01822 rx = QRegExp("^([+-])(\\d\\d)(\\d\\d)$");
01823 if (!parts[10].indexOf(rx))
01824 {
01825
01826 parts = rx.capturedTexts();
01827 offset = parts[2].toInt(&ok[0]) * 3600;
01828 int offsetMin = parts[3].toInt(&ok[1]);
01829 if (!ok[0] || !ok[1] || offsetMin > 59)
01830 break;
01831 offset += offsetMin * 60;
01832 negOffset = (parts[1] == QLatin1String("-"));
01833 if (negOffset)
01834 offset = -offset;
01835 }
01836 else
01837 {
01838
01839 QByteArray zone = parts[10].toLatin1();
01840 if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J')
01841 negOffset = true;
01842 else if (zone != "UT" && zone != "GMT")
01843 {
01844 offset = (zone == "EDT") ? -4*3600
01845 : (zone == "EST" || zone == "CDT") ? -5*3600
01846 : (zone == "CST" || zone == "MDT") ? -6*3600
01847 : (zone == "MST" || zone == "PDT") ? -7*3600
01848 : (zone == "PST") ? -8*3600
01849 : 0;
01850 if (!offset)
01851 {
01852
01853 bool nonalpha = false;
01854 for (int i = 0, end = zone.size(); i < end && !nonalpha; ++i)
01855 nonalpha = !isalpha(zone[i]);
01856 if (nonalpha)
01857 break;
01858
01859 negOffset = true;
01860 }
01861 }
01862 }
01863 }
01864 Status invalid = stValid;
01865 QDate qdate = checkDate(year, month+1, day, invalid);
01866 if (!qdate.isValid())
01867 break;
01868 KDateTime result(qdate, QTime(hour, minute, second), Spec(OffsetFromUTC, offset));
01869 if (!result.isValid()
01870 || (dayOfWeek >= 0 && result.date().dayOfWeek() != dayOfWeek+1))
01871 break;
01872 if (!offset)
01873 {
01874 if (negOffset && negZero)
01875 *negZero = true;
01876 result.setTimeSpec(UTC);
01877 }
01878 if (leapSecond)
01879 {
01880
01881
01882 if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400)
01883 break;
01884 }
01885 if (invalid)
01886 {
01887 KDateTime dt;
01888 dt.d->status = invalid;
01889 return dt;
01890 }
01891 return result;
01892 }
01893 case ISODate:
01894 {
01895
01896
01897
01898
01899
01900
01901
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912 bool dateOnly = false;
01913
01914 QRegExp rx("^([+-])?(\\d{4,})-(\\d\\d\\d|\\d\\d-\\d\\d)[T ](\\d\\d)(?::(\\d\\d)(?::(\\d\\d)(?:(?:\\.|,)(\\d+))?)?)?(Z|([+-])(\\d\\d)(?::(\\d\\d))?)?$");
01915 if (str.indexOf(rx))
01916 {
01917
01918 rx = QRegExp("^([+-])?(\\d{4,})(\\d{4})[T ](\\d\\d)(?:(\\d\\d)(?:(\\d\\d)(?:(?:\\.|,)(\\d+))?)?)?(Z|([+-])(\\d\\d)(\\d\\d)?)?$");
01919 if (str.indexOf(rx))
01920 {
01921 rx = QRegExp("^([+-])?(\\d{4})(\\d{3})[T ](\\d\\d)(?:(\\d\\d)(?:(\\d\\d)(?:(?:\\.|,)(\\d+))?)?)?(Z|([+-])(\\d\\d)(\\d\\d)?)?$");
01922 if (str.indexOf(rx))
01923 {
01924
01925 dateOnly = true;
01926 rx = QRegExp("^([+-])?(\\d{4,})-(\\d\\d\\d|\\d\\d-\\d\\d)$");
01927 if (str.indexOf(rx))
01928 {
01929
01930 rx = QRegExp("^([+-])?(\\d{4,})(\\d{4})$");
01931 if (str.indexOf(rx))
01932 {
01933 rx = QRegExp("^([+-])?(\\d{4})(\\d{3})$");
01934 if (str.indexOf(rx))
01935 break;
01936 }
01937 }
01938 }
01939 }
01940 }
01941 const QStringList parts = rx.capturedTexts();
01942 bool ok, ok1;
01943 QDate d;
01944 int hour = 0;
01945 int minute = 0;
01946 int second = 0;
01947 int msecs = 0;
01948 bool leapSecond = false;
01949 int year = parts[2].toInt(&ok);
01950 if (!ok)
01951 break;
01952 if (parts[1] == QLatin1String("-"))
01953 year = -year;
01954 if (!dateOnly)
01955 {
01956 hour = parts[4].toInt(&ok);
01957 if (!ok)
01958 break;
01959 if (!parts[5].isEmpty())
01960 {
01961 minute = parts[5].toInt(&ok);
01962 if (!ok)
01963 break;
01964 }
01965 if (!parts[6].isEmpty())
01966 {
01967 second = parts[6].toInt(&ok);
01968 if (!ok)
01969 break;
01970 }
01971 leapSecond = (second == 60);
01972 if (leapSecond)
01973 second = 59;
01974 if (!parts[7].isEmpty())
01975 {
01976 QString ms = parts[7] + QLatin1String("00");
01977 ms.truncate(3);
01978 msecs = ms.toInt(&ok);
01979 if (!ok)
01980 break;
01981 }
01982 }
01983 int month, day;
01984 Status invalid = stValid;
01985 if (parts[3].length() == 3)
01986 {
01987
01988 day = parts[3].toInt(&ok);
01989 if (!ok || day < 1 || day > 366)
01990 break;
01991 d = checkDate(year, 1, 1, invalid).addDays(day - 1);
01992 if (!d.isValid() || (!invalid && d.year() != year))
01993 break;
01994 day = d.day();
01995 month = d.month();
01996 }
01997 else
01998 {
01999
02000 month = parts[3].left(2).toInt(&ok);
02001 day = parts[3].right(2).toInt(&ok1);
02002 if (!ok || !ok1)
02003 break;
02004 d = checkDate(year, month, day, invalid);
02005 if (!d.isValid())
02006 break;
02007 }
02008 if (dateOnly)
02009 {
02010 if (invalid)
02011 {
02012 KDateTime dt;
02013 dt.d->status = invalid;
02014 return dt;
02015 }
02016 return KDateTime(d, Spec(ClockTime));
02017 }
02018 if (hour == 24 && !minute && !second && !msecs)
02019 {
02020
02021 d = d.addDays(1);
02022 hour = 0;
02023 }
02024
02025 QTime t(hour, minute, second, msecs);
02026 if (!t.isValid())
02027 break;
02028 if (parts[8].isEmpty())
02029 {
02030
02031 if (invalid)
02032 {
02033 KDateTime dt;
02034 dt.d->status = invalid;
02035 return dt;
02036 }
02037 return KDateTime(d, t, KDateTimePrivate::fromStringDefault());
02038 }
02039 int offset = 0;
02040 SpecType spec = (parts[8] == QLatin1String("Z")) ? UTC : OffsetFromUTC;
02041 if (spec == OffsetFromUTC)
02042 {
02043 offset = parts[10].toInt(&ok) * 3600;
02044 if (!ok)
02045 break;
02046 if (!parts[11].isEmpty())
02047 {
02048 offset += parts[11].toInt(&ok) * 60;
02049 if (!ok)
02050 break;
02051 }
02052 if (parts[9] == QLatin1String("-"))
02053 {
02054 offset = -offset;
02055 if (!offset && negZero)
02056 *negZero = true;
02057 }
02058 }
02059 if (leapSecond)
02060 {
02061
02062
02063 if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400)
02064 break;
02065 }
02066 if (invalid)
02067 {
02068 KDateTime dt;
02069 dt.d->status = invalid;
02070 return dt;
02071 }
02072 return KDateTime(d, t, Spec(spec, offset));
02073 }
02074 case QtTextDate:
02075 {
02076 int offset = 0;
02077 QRegExp rx("^(\\S+\\s+\\S+\\s+\\d\\d\\s+(\\d\\d:\\d\\d:\\d\\d\\s+)?\\d\\d\\d\\d)\\s*(.*)$");
02078 if (str.indexOf(rx) < 0)
02079 break;
02080 QStringList parts = rx.capturedTexts();
02081 QDate qd;
02082 QDateTime qdt;
02083 bool dateOnly = parts[2].isEmpty();
02084 if (dateOnly)
02085 {
02086 qd = QDate::fromString(parts[1], Qt::TextDate);
02087 if (!qd.isValid())
02088 break;
02089 }
02090 else
02091 {
02092 qdt = QDateTime::fromString(parts[1], Qt::TextDate);
02093 if (!qdt.isValid())
02094 break;
02095 }
02096 if (parts[3].isEmpty())
02097 {
02098
02099 if (dateOnly)
02100 return KDateTime(qd, KDateTimePrivate::fromStringDefault());
02101 else
02102 {
02103
02104 return KDateTime(qdt.date(), qdt.time(), KDateTimePrivate::fromStringDefault());
02105 }
02106 }
02107 rx = QRegExp("([+-])([\\d][\\d])(?::?([\\d][\\d]))?$");
02108 if (parts[3].indexOf(rx) < 0)
02109 break;
02110
02111
02112 bool ok;
02113 parts = rx.capturedTexts();
02114 offset = parts[2].toInt(&ok) * 3600;
02115 if (!ok)
02116 break;
02117 if (parts.count() > 3)
02118 {
02119 offset += parts[3].toInt(&ok) * 60;
02120 if (!ok)
02121 break;
02122 }
02123 if (parts[1] == QLatin1String("-"))
02124 {
02125 offset = -offset;
02126 if (!offset && negZero)
02127 *negZero = true;
02128 }
02129 if (dateOnly)
02130 return KDateTime(qd, Spec((offset ? OffsetFromUTC : UTC), offset));
02131 qdt.setTimeSpec(offset ? Qt::LocalTime : Qt::UTC);
02132 return KDateTime(qdt, Spec((offset ? OffsetFromUTC : UTC), offset));
02133 }
02134 case LocalDate:
02135 default:
02136 break;
02137 }
02138 return KDateTime();
02139 }
02140
02141 KDateTime KDateTime::fromString(const QString &string, const QString &format,
02142 const KTimeZones *zones, bool offsetIfAmbiguous)
02143 {
02144 int utcOffset = 0;
02145 bool dateOnly = false;
02146 Status invalid = stValid;
02147 QString zoneName;
02148 QByteArray zoneAbbrev;
02149 QDateTime qdt = fromStr(string, format, utcOffset, zoneName, zoneAbbrev, dateOnly, invalid);
02150 if (!qdt.isValid())
02151 return KDateTime();
02152 if (zones)
02153 {
02154
02155 bool zname = false;
02156 KTimeZone zone;
02157 if (!zoneName.isEmpty())
02158 {
02159
02160
02161 zone = zones->zone(zoneName);
02162 zname = true;
02163 }
02164 else if (!invalid)
02165 {
02166 if (!zoneAbbrev.isEmpty())
02167 {
02168
02169
02170
02171 bool useUtcOffset = false;
02172 const KTimeZones::ZoneMap z = zones->zones();
02173 for (KTimeZones::ZoneMap::ConstIterator it = z.begin(); it != z.end(); ++it)
02174 {
02175 if (it.value().abbreviations().contains(zoneAbbrev))
02176 {
02177 int offset2;
02178 int offset = it.value().offsetAtZoneTime(qdt, &offset2);
02179 QDateTime ut(qdt);
02180 ut.setTimeSpec(Qt::UTC);
02181 ut.addSecs(-offset);
02182 if (it.value().abbreviation(ut) != zoneAbbrev)
02183 {
02184 if (offset == offset2)
02185 continue;
02186 ut.addSecs(offset - offset2);
02187 if (it.value().abbreviation(ut) != zoneAbbrev)
02188 continue;
02189 offset = offset2;
02190 }
02191
02192 if (zone.isValid())
02193 {
02194
02195 if (!offsetIfAmbiguous || offset != utcOffset)
02196 return KDateTime();
02197 useUtcOffset = true;
02198 }
02199 else
02200 {
02201 zone = it.value();
02202 utcOffset = offset;
02203 }
02204 }
02205 }
02206 if (useUtcOffset)
02207 {
02208 zone = KTimeZone();
02209 if (!utcOffset)
02210 qdt.setTimeSpec(Qt::UTC);
02211 }
02212 else
02213 zname = true;
02214 }
02215 else if (utcOffset || qdt.timeSpec() == Qt::UTC)
02216 {
02217
02218
02219
02220 QDateTime dtUTC = qdt;
02221 dtUTC.setTimeSpec(Qt::UTC);
02222 dtUTC.addSecs(-utcOffset);
02223 const KTimeZones::ZoneMap z = zones->zones();
02224 for (KTimeZones::ZoneMap::ConstIterator it = z.begin(); it != z.end(); ++it)
02225 {
02226 QList<int> offsets = it.value().utcOffsets();
02227 if ((offsets.isEmpty() || offsets.contains(utcOffset))
02228 && it.value().offsetAtUtc(dtUTC) == utcOffset)
02229 {
02230
02231 if (zone.isValid() || !utcOffset)
02232 {
02233
02234 if (!offsetIfAmbiguous)
02235 return KDateTime();
02236 if (invalid)
02237 {
02238 KDateTime dt;
02239 dt.d->status = invalid;
02240 return dt;
02241 }
02242 if (dateOnly)
02243 return KDateTime(qdt.date(), Spec(OffsetFromUTC, utcOffset));
02244 qdt.setTimeSpec(Qt::LocalTime);
02245 return KDateTime(qdt, Spec(OffsetFromUTC, utcOffset));
02246 }
02247 zone = it.value();
02248 }
02249 }
02250 }
02251 }
02252 if (!zone.isValid() && zname)
02253 return KDateTime();
02254 if (zone.isValid() && !invalid)
02255 {
02256 if (dateOnly)
02257 return KDateTime(qdt.date(), Spec(zone));
02258 return KDateTime(qdt, Spec(zone));
02259 }
02260 }
02261
02262
02263 if (invalid)
02264 {
02265 KDateTime dt;
02266 dt.d->status = invalid;
02267 return dt;
02268 }
02269 KDateTime result;
02270 if (utcOffset)
02271 {
02272 qdt.setTimeSpec(Qt::LocalTime);
02273 result = KDateTime(qdt, Spec(OffsetFromUTC, utcOffset));
02274 }
02275 else if (qdt.timeSpec() == Qt::UTC)
02276 result = KDateTime(qdt, UTC);
02277 else
02278 {
02279 result = KDateTime(qdt, Spec(ClockTime));
02280 result.setTimeSpec(KDateTimePrivate::fromStringDefault());
02281 }
02282 if (dateOnly)
02283 result.setDateOnly(true);
02284 return result;
02285 }
02286
02287 void KDateTime::setFromStringDefault(const Spec &spec)
02288 {
02289 KDateTimePrivate::fromStringDefault() = spec;
02290 }
02291
02292 QDataStream & operator<<(QDataStream &s, const KDateTime &dt)
02293 {
02294 s << dt.dateTime() << dt.timeSpec() << quint8(dt.isDateOnly() ? 0x01 : 0x00);
02295 return s;
02296 }
02297
02298 QDataStream & operator>>(QDataStream &s, KDateTime &kdt)
02299 {
02300 QDateTime dt;
02301 KDateTime::Spec spec;
02302 quint8 flags;
02303 s >> dt >> spec >> flags;
02304 kdt.setDateTime(dt);
02305 kdt.setTimeSpec(spec);
02306 if (flags & 0x01)
02307 kdt.setDateOnly(true);
02308 return s;
02309 }
02310
02311
02312
02313
02314
02315
02316
02317
02318 QDateTime fromStr(const QString& string, const QString& format, int& utcOffset,
02319 QString& zoneName, QByteArray& zoneAbbrev, bool& dateOnly, Status &status)
02320 {
02321 status = stValid;
02322 QString str = string.simplified();
02323 int year = NO_NUMBER;
02324 int month = NO_NUMBER;
02325 int day = NO_NUMBER;
02326 int dayOfWeek = NO_NUMBER;
02327 int hour = NO_NUMBER;
02328 int minute = NO_NUMBER;
02329 int second = NO_NUMBER;
02330 int millisec = NO_NUMBER;
02331 int ampm = NO_NUMBER;
02332 int tzoffset = NO_NUMBER;
02333 zoneName.clear();
02334 zoneAbbrev.clear();
02335
02336 enum { TZNone, UTCOffset, UTCOffsetColon, TZAbbrev, TZName };
02337 KLocale *locale = KGlobal::locale();
02338 KCalendarSystemGregorian calendar(locale);
02339 int zone;
02340 int s = 0;
02341 int send = str.length();
02342 bool escape = false;
02343 ushort flag = 0;
02344 for (int f = 0, fend = format.length(); f < fend && s < send; ++f)
02345 {
02346 zone = TZNone;
02347 ushort ch = format[f].unicode();
02348 if (!escape)
02349 {
02350 if (ch == '%')
02351 escape = true;
02352 else if (format[f].isSpace())
02353 {
02354 if (str[s].isSpace())
02355 ++s;
02356 }
02357 else if (format[f] == str[s])
02358 ++s;
02359 else
02360 return QDateTime();
02361 continue;
02362 }
02363 if (!flag)
02364 {
02365 switch (ch)
02366 {
02367 case '%':
02368 if (str[s++] != QLatin1Char('%'))
02369 return QDateTime();
02370 break;
02371 case ':':
02372 flag = ch;
02373 break;
02374 case 'Y':
02375 if (!getNumber(str, s, 4, 4, NO_NUMBER, -1, year))
02376 return QDateTime();
02377 break;
02378 case 'y':
02379 if (!getNumber(str, s, 2, 2, 0, 99, year))
02380 return QDateTime();
02381 year += (year <= 50) ? 2000 : 1999;
02382 break;
02383 case 'm':
02384 if (!getNumber(str, s, 2, 2, 1, 12, month))
02385 return QDateTime();
02386 break;
02387 case 'B':
02388 case 'b':
02389 {
02390 int m = matchMonth(str, s, &calendar);
02391 if (m <= 0 || (month != NO_NUMBER && month != m))
02392 return QDateTime();
02393 month = m;
02394 break;
02395 }
02396 case 'd':
02397 if (!getNumber(str, s, 2, 2, 1, 31, day))
02398 return QDateTime();
02399 break;
02400 case 'e':
02401 if (!getNumber(str, s, 1, 2, 1, 31, day))
02402 return QDateTime();
02403 break;
02404 case 'A':
02405 case 'a':
02406 {
02407 int dow = matchDay(str, s, &calendar);
02408 if (dow <= 0 || (dayOfWeek != NO_NUMBER && dayOfWeek != dow))
02409 return QDateTime();
02410 dayOfWeek = dow;
02411 break;
02412 }
02413 case 'H':
02414 if (!getNumber(str, s, 2, 2, 0, 23, hour))
02415 return QDateTime();
02416 break;
02417 case 'k':
02418 if (!getNumber(str, s, 1, 2, 0, 23, hour))
02419 return QDateTime();
02420 break;
02421 case 'I':
02422 if (!getNumber(str, s, 2, 2, 1, 12, hour))
02423 return QDateTime();
02424 break;
02425 case 'l':
02426 if (!getNumber(str, s, 1, 2, 1, 12, hour))
02427 return QDateTime();
02428 break;
02429 case 'M':
02430 if (!getNumber(str, s, 2, 2, 0, 59, minute))
02431 return QDateTime();
02432 break;
02433 case 'S':
02434 if (!getNumber(str, s, 2, 2, 0, 59, second))
02435 return QDateTime();
02436 break;
02437 case 's':
02438 if (!getNumber(str, s, 1, 2, 0, 59, second))
02439 return QDateTime();
02440 break;
02441 case 'P':
02442 case 'p':
02443 {
02444 int ap = getAmPm(str, s, locale);
02445 if (!ap || (ampm != NO_NUMBER && ampm != ap))
02446 return QDateTime();
02447 ampm = ap;
02448 break;
02449 }
02450 case 'z':
02451 zone = UTCOffset;
02452 break;
02453 case 'Z':
02454 zone = TZAbbrev;
02455 break;
02456 case 't':
02457 if (str[s++] != QLatin1Char(' '))
02458 return QDateTime();
02459 break;
02460 default:
02461 if (s + 2 > send
02462 || str[s++] != QLatin1Char('%')
02463 || str[s++] != format[f])
02464 return QDateTime();
02465 break;
02466 }
02467 }
02468 else if (flag == ':')
02469 {
02470
02471 switch (ch)
02472 {
02473 case 'Y':
02474 if (!getNumber(str, s, 4, 100, NO_NUMBER, -1, year))
02475 return QDateTime();
02476 break;
02477 case 'A':
02478 case 'a':
02479 {
02480 int dow = matchDay(str, s, 0);
02481 if (dow <= 0 || (dayOfWeek != NO_NUMBER && dayOfWeek != dow))
02482 return QDateTime();
02483 dayOfWeek = dow;
02484 break;
02485 }
02486 case 'B':
02487 case 'b':
02488 {
02489 int m = matchMonth(str, s, 0);
02490 if (m <= 0 || (month != NO_NUMBER && month != m))
02491 return QDateTime();
02492 month = m;
02493 break;
02494 }
02495 case 'm':
02496 if (!getNumber(str, s, 1, 2, 1, 12, month))
02497 return QDateTime();
02498 break;
02499 case 'P':
02500 case 'p':
02501 {
02502 int ap = getAmPm(str, s, 0);
02503 if (!ap || (ampm != NO_NUMBER && ampm != ap))
02504 return QDateTime();
02505 ampm = ap;
02506 break;
02507 }
02508 case 'M':
02509 if (!getNumber(str, s, 1, 2, 0, 59, minute))
02510 return QDateTime();
02511 break;
02512 case 'S':
02513 if (str[s] != QLatin1Char(':'))
02514 {
02515 second = 0;
02516 break;
02517 }
02518 ++s;
02519 if (!getNumber(str, s, 1, 2, 0, 59, second))
02520 return QDateTime();
02521 break;
02522 case 's':
02523 {
02524 if (str[s] != QLatin1Char('.'))
02525 {
02526
02527 QString dpt = locale == 0 ? "," : locale->decimalSymbol();
02528 if (!str.mid(s).startsWith(dpt))
02529 return QDateTime();
02530 s += dpt.length() - 1;
02531 }
02532 ++s;
02533 if (s >= send)
02534 return QDateTime();
02535 QString val = str.mid(s);
02536 int i = 0;
02537 for (int end = val.length(); i < end && val[i].isDigit(); ++i) ;
02538 if (!i)
02539 return QDateTime();
02540 val.truncate(i);
02541 val += QLatin1String("00");
02542 val.truncate(3);
02543 int ms = val.toInt();
02544 if (millisec != NO_NUMBER && millisec != ms)
02545 return QDateTime();
02546 millisec = ms;
02547 s += i;
02548 break;
02549 }
02550 case 'u':
02551 zone = UTCOffset;
02552 break;
02553 case 'z':
02554 zone = UTCOffsetColon;
02555 break;
02556 case 'Z':
02557 zone = TZName;
02558 break;
02559 default:
02560 if (s + 3 > send
02561 || str[s++] != QLatin1Char('%')
02562 || str[s++] != QLatin1Char(':')
02563 || str[s++] != format[f])
02564 return QDateTime();
02565 break;
02566 }
02567 flag = 0;
02568 }
02569 if (!flag)
02570 escape = false;
02571
02572 if (zone != TZNone)
02573 {
02574
02575 switch (zone)
02576 {
02577 case UTCOffset:
02578 case UTCOffsetColon:
02579 if (!zoneAbbrev.isEmpty() || !zoneName.isEmpty())
02580 return QDateTime();
02581 if (!getUTCOffset(str, s, (zone == UTCOffsetColon), tzoffset))
02582 return QDateTime();
02583 break;
02584 case TZAbbrev:
02585 {
02586 if (tzoffset != NO_NUMBER || !zoneName.isEmpty())
02587 return QDateTime();
02588 int start = s;
02589 while (s < send && str[s].isLetterOrNumber())
02590 ++s;
02591 if (s == start)
02592 return QDateTime();
02593 QString z = str.mid(start, s - start);
02594 if (!zoneAbbrev.isEmpty() && z.toLatin1() != zoneAbbrev)
02595 return QDateTime();
02596 zoneAbbrev = z.toLatin1();
02597 break;
02598 }
02599 case TZName:
02600 {
02601 if (tzoffset != NO_NUMBER || !zoneAbbrev.isEmpty())
02602 return QDateTime();
02603 QString z;
02604 if (f + 1 >= fend)
02605 {
02606 z = str.mid(s);
02607 s = send;
02608 }
02609 else
02610 {
02611
02612 QChar endchar = format[f + 1];
02613 if (endchar == QLatin1Char('%') && f + 2 < fend)
02614 {
02615 QChar endchar2 = format[f + 2];
02616 if (endchar2 == QLatin1Char('n') || endchar2 == QLatin1Char('t'))
02617 endchar = QLatin1Char(' ');
02618 }
02619
02620 int start = s;
02621 for ( ; s < send && str[s] != endchar; ++s) ;
02622 if (s == start)
02623 return QDateTime();
02624 z = str.mid(start, s - start);
02625 }
02626 if (!zoneName.isEmpty() && z != zoneName)
02627 return QDateTime();
02628 zoneName = z;
02629 break;
02630 }
02631 default:
02632 break;
02633 }
02634 }
02635 }
02636
02637 if (year == NO_NUMBER)
02638 year = QDate::currentDate().year();
02639 if (month == NO_NUMBER)
02640 month = 1;
02641 QDate d = checkDate(year, month, (day > 0 ? day : 1), status);
02642 if (!d.isValid())
02643 return QDateTime();
02644 if (dayOfWeek != NO_NUMBER && !status)
02645 {
02646 if (day == NO_NUMBER)
02647 {
02648 day = 1 + dayOfWeek - QDate(year, month, 1).dayOfWeek();
02649 if (day <= 0)
02650 day += 7;
02651 }
02652 else
02653 {
02654 if (QDate(year, month, day).dayOfWeek() != dayOfWeek)
02655 return QDateTime();
02656 }
02657 }
02658 if (day == NO_NUMBER)
02659 day = 1;
02660 dateOnly = (hour == NO_NUMBER && minute == NO_NUMBER && second == NO_NUMBER && millisec == NO_NUMBER);
02661 if (hour == NO_NUMBER)
02662 hour = 0;
02663 if (minute == NO_NUMBER)
02664 minute = 0;
02665 if (second == NO_NUMBER)
02666 second = 0;
02667 if (millisec == NO_NUMBER)
02668 millisec = 0;
02669 if (ampm != NO_NUMBER)
02670 {
02671 if (!hour || hour > 12)
02672 return QDateTime();
02673 if (ampm == 1 && hour == 12)
02674 hour = 0;
02675 else if (ampm == 2 && hour < 12)
02676 hour += 12;
02677 }
02678
02679 QDateTime dt(d, QTime(hour, minute, second, millisec), (tzoffset == 0 ? Qt::UTC : Qt::LocalTime));
02680
02681 utcOffset = (tzoffset == NO_NUMBER) ? 0 : tzoffset*60;
02682
02683 return dt;
02684 }
02685
02686
02687
02688
02689
02690
02691
02692 int matchDay(const QString &string, int &offset, KCalendarSystem *calendar)
02693 {
02694 int dayOfWeek;
02695 QString part = string.mid(offset);
02696 if (part.isEmpty())
02697 return -1;
02698 if (calendar)
02699 {
02700
02701 for (dayOfWeek = 1; dayOfWeek <= 7; ++dayOfWeek)
02702 {
02703 QString name = calendar->weekDayName(dayOfWeek, KCalendarSystem::LongDayName);
02704 if (part.startsWith(name, Qt::CaseInsensitive))
02705 {
02706 offset += name.length();
02707 return dayOfWeek;
02708 }
02709 }
02710 for (dayOfWeek = 1; dayOfWeek <= 7; ++dayOfWeek)
02711 {
02712 QString name = calendar->weekDayName(dayOfWeek, KCalendarSystem::ShortDayName);
02713 if (part.startsWith(name, Qt::CaseInsensitive))
02714 {
02715 offset += name.length();
02716 return dayOfWeek;
02717 }
02718 }
02719 }
02720
02721
02722 dayOfWeek = findString(part, longDay, 7, offset);
02723 if (dayOfWeek < 0)
02724 dayOfWeek = findString(part, shortDay, 7, offset);
02725 return dayOfWeek + 1;
02726 }
02727
02728
02729
02730
02731
02732
02733 int matchMonth(const QString &string, int &offset, KCalendarSystem *calendar)
02734 {
02735 int month;
02736 QString part = string.mid(offset);
02737 if (part.isEmpty())
02738 return -1;
02739 if (calendar)
02740 {
02741
02742 for (month = 1; month <= 12; ++month)
02743 {
02744 QString name = calendar->monthName(month, 2000, KCalendarSystem::LongName);
02745 if (part.startsWith(name, Qt::CaseInsensitive))
02746 {
02747 offset += name.length();
02748 return month;
02749 }
02750 }
02751 for (month = 1; month <= 12; ++month)
02752 {
02753 QString name = calendar->monthName(month, 2000, KCalendarSystem::ShortName);
02754 if (part.startsWith(name, Qt::CaseInsensitive))
02755 {
02756 offset += name.length();
02757 return month;
02758 }
02759 }
02760 }
02761
02762 month = findString(part, longMonth, 12, offset);
02763 if (month < 0)
02764 month = findString(part, shortMonth, 12, offset);
02765 return month + 1;
02766 }
02767
02768
02769
02770
02771 bool getUTCOffset(const QString &string, int &offset, bool colon, int &result)
02772 {
02773 int sign;
02774 int len = string.length();
02775 if (offset >= len)
02776 return false;
02777 switch (string[offset++].unicode())
02778 {
02779 case '+':
02780 sign = 1;
02781 break;
02782 case '-':
02783 sign = -1;
02784 break;
02785 default:
02786 return false;
02787 }
02788 int tzhour = NO_NUMBER;
02789 int tzmin = NO_NUMBER;
02790 if (!getNumber(string, offset, 2, 2, 0, 99, tzhour))
02791 return false;
02792 if (colon)
02793 {
02794 if (offset >= len || string[offset++] != QLatin1Char(':'))
02795 return false;
02796 }
02797 if (offset >= len || !string[offset].isDigit())
02798 tzmin = 0;
02799 else
02800 {
02801 if (!getNumber(string, offset, 2, 2, 0, 59, tzmin))
02802 return false;
02803 }
02804 tzmin += tzhour * 60;
02805 if (result != NO_NUMBER && result != tzmin)
02806 return false;
02807 result = tzmin;
02808 return true;
02809 }
02810
02811
02812
02813
02814
02815
02816 int getAmPm(const QString &string, int &offset, KLocale *locale)
02817 {
02818 QString part = string.mid(offset);
02819 int ap = 0;
02820 int n = 2;
02821 if (locale)
02822 {
02823
02824 QString aps = ki18n("am").toString(locale);
02825 if (part.startsWith(aps, Qt::CaseInsensitive))
02826 {
02827 ap = 1;
02828 n = aps.length();
02829 }
02830 else
02831 {
02832 aps = ki18n("pm").toString(locale);
02833 if (part.startsWith(aps, Qt::CaseInsensitive))
02834 {
02835 ap = 2;
02836 n = aps.length();
02837 }
02838 }
02839 }
02840 if (!ap)
02841 {
02842 if (part.startsWith(QLatin1String("am"), Qt::CaseInsensitive))
02843 ap = 1;
02844 else if (part.startsWith(QLatin1String("pm"), Qt::CaseInsensitive))
02845 ap = 2;
02846 }
02847 if (ap)
02848 offset += n;
02849 return ap;
02850 }
02851
02852
02853
02854
02855
02856 bool getNumber(const QString& string, int& offset, int mindigits, int maxdigits, int minval, int maxval, int& result)
02857 {
02858 int end = string.size();
02859 bool neg = false;
02860 if (minval == NO_NUMBER && offset < end && string[offset] == QLatin1Char('-'))
02861 {
02862 neg = true;
02863 ++offset;
02864 }
02865 if (offset + maxdigits > end)
02866 maxdigits = end - offset;
02867 int ndigits;
02868 for (ndigits = 0; ndigits < maxdigits && string[offset + ndigits].isDigit(); ++ndigits) ;
02869 if (ndigits < mindigits)
02870 return false;
02871 bool ok;
02872 int n = string.mid(offset, ndigits).toInt(&ok);
02873 if (neg)
02874 n = -n;
02875 if (!ok || (result != NO_NUMBER && n != result) || (minval != NO_NUMBER && n < minval) || (n > maxval && maxval >= 0))
02876 return false;
02877 result = n;
02878 offset += ndigits;
02879 return true;
02880 }
02881
02882 int findString_internal(const QString &string, const char *array, int count, int &offset, int disp)
02883 {
02884 for (int i = 0; i < count; ++i)
02885 {
02886 if (string.startsWith(array + i * disp, Qt::CaseInsensitive))
02887 {
02888 offset += qstrlen(array + i * disp);
02889 return i;
02890 }
02891 }
02892 return -1;
02893 }
02894
02895
02896
02897
02898
02899
02900
02901 QDate checkDate(int year, int month, int day, Status &status)
02902 {
02903 status = stValid;
02904 QDate qdate(year, month, day);
02905 if (qdate.isValid())
02906 return qdate;
02907
02908
02909 if (year < MIN_YEAR)
02910 {
02911 bool leap = (year % 4 == 0) && (year % 100 || year % 400 == 0);
02912 qdate.setYMD((leap ? 2000 : 2001), month, day);
02913 if (qdate.isValid())
02914 status = stTooEarly;
02915 }
02916 return qdate;
02917 }
02918