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 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
00794 if (spec.type() == UTC)
00795 {
00796 if (dt.timeSpec() == Qt::LocalTime)
00797 d->setUtcFromTz(dt, KSystemTimeZones::local());
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();
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();
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;
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);
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;
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;
00967 if (d->dateOnly())
00968 return KDateTime(d->date(), Spec(zone));
00969 KDateTime result;
00970 d->newToZone(result.d, zone);
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());
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);
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());
01038 else
01039 d->setDt(dt);
01040 }
01041 else
01042 d->setDtFromUtc(dt);
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
01059
01060 d->timeZoneOffset();
01061 }
01062 }
01063 }
01064
01065 KDateTime KDateTime::addMSecs(qint64 msecs) const
01066 {
01067 if (!msecs)
01068 return *this;
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;
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);
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;
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;
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;
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
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();
01213 break;
01214 case ClockTime:
01215 {
01216 KTimeZone local = KSystemTimeZones::local();
01217 dat = t2.d->toZone(local, local).date();
01218 break;
01219 }
01220 default:
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
01261
01262 start1 = d->toUtc();
01263 start2 = other.d->toUtc();
01264 }
01265 else
01266 {
01267
01268 start1 = d->dt();
01269 start2 = other.d->dt();
01270 }
01271 if (d->dateOnly() || other.d->dateOnly())
01272 {
01273
01274
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;
01328 if (d->dateOnly() != other.d->dateOnly())
01329 return false;
01330 if (d->equalSpec(*other.d))
01331 {
01332
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
01342
01343 if (qAbs(d->date().daysTo(other.d->date())) > 2)
01344 return false;
01345 if (d->toUtc() != other.d->toUtc())
01346 return false;
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;
01360 if (d->equalSpec(*other.d))
01361 {
01362
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
01368
01369
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
01379
01380
01381
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;
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':
01426 num = d->date().year();
01427 numLength = 4;
01428 break;
01429 case 'y':
01430 num = d->date().year() % 100;
01431 numLength = 2;
01432 break;
01433 case 'm':
01434 numLength = 2;
01435 num = d->date().month();
01436 break;
01437 case 'B':
01438 result += calendar.monthName(d->date().month(), 2000, KCalendarSystem::LongName);
01439 break;
01440 case 'b':
01441 result += calendar.monthName(d->date().month(), 2000, KCalendarSystem::ShortName);
01442 break;
01443 case 'd':
01444 numLength = 2;
01445
01446 case 'e':
01447 num = d->date().day();
01448 break;
01449 case 'A':
01450 result += calendar.weekDayName(d->date().dayOfWeek(), KCalendarSystem::LongDayName);
01451 break;
01452 case 'a':
01453 result += calendar.weekDayName(d->date().dayOfWeek(), KCalendarSystem::ShortDayName);
01454 break;
01455 case 'H':
01456 numLength = 2;
01457
01458 case 'k':
01459 num = d->dt().time().hour();
01460 break;
01461 case 'I':
01462 numLength = 2;
01463
01464 case 'l':
01465 num = (d->dt().time().hour() + 11) % 12 + 1;
01466 break;
01467 case 'M':
01468 num = d->dt().time().minute();
01469 numLength = 2;
01470 break;
01471 case 'S':
01472 num = d->dt().time().second();
01473 numLength = 2;
01474 break;
01475 case 'P':
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':
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':
01496 zone = UTCOffset;
01497 break;
01498 case 'Z':
01499 zone = TZAbbrev;
01500 break;
01501 default:
01502 result += QLatin1Char('%');
01503 result += format[i];
01504 break;
01505 }
01506 }
01507 else if (flag == ':')
01508 {
01509
01510 switch (ch)
01511 {
01512 case 'A':
01513 result += longDay[d->date().dayOfWeek() - 1];
01514 break;
01515 case 'a':
01516 result += shortDay[d->date().dayOfWeek() - 1];
01517 break;
01518 case 'B':
01519 result += longMonth[d->date().month() - 1];
01520 break;
01521 case 'b':
01522 result += shortMonth[d->date().month() - 1];
01523 break;
01524 case 'm':
01525 num = d->date().month();
01526 break;
01527 case 'P':
01528 result += (d->dt().time().hour() < 12) ? QLatin1String("am") : QLatin1String("pm");
01529 break;
01530 case 'p':
01531 result += (d->dt().time().hour() < 12) ? QLatin1String("AM") : QLatin1String("PM");
01532 break;
01533 case 'S':
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':
01545 result += s.sprintf("%03d", d->dt().time().msec());
01546 break;
01547 case 'u':
01548 zone = UTCOffsetShort;
01549 break;
01550 case 'z':
01551 zone = UTCOffsetColon;
01552 break;
01553 case 'Z':
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
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
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:
01598 case UTCOffset:
01599 case UTCOffsetColon:
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:
01615 if (tz.isValid() && d->specType != OffsetFromUTC)
01616 result += tz.abbreviation(d->toUtc());
01617 break;
01618 case TZName:
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
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
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
01686
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
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
01718 if (d->specType == OffsetFromUTC || d->specType == TimeZone || tz.isValid())
01719 {
01720 if (d->specType == TimeZone)
01721 offset = d->timeZoneOffset();
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:
01745 case RFCDate:
01746 {
01747 int nyear = 6;
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
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
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
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;
01798 int month = 0;
01799 for ( ; month < 12 && parts[nmonth] != shortMonth[month]; ++month) ;
01800 int dayOfWeek = -1;
01801 if (!parts[nwday].isEmpty())
01802 {
01803
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
01815 year += (i == 2 && year < 50) ? 2000 : 1900;
01816 }
01817
01818
01819 int offset = 0;
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
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
01840 QByteArray zone = parts[10].toLatin1();
01841 if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J')
01842 negOffset = true;
01843 else if (zone != "UT" && zone != "GMT")
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
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
01860 negOffset = true;
01861 }
01862 }
01863 }
01864 }
01865 Status invalid = stValid;
01866 QDate qdate = checkDate(year, month+1, day, invalid);
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;
01873 if (!offset)
01874 {
01875 if (negOffset && negZero)
01876 *negZero = true;
01877 result.setTimeSpec(UTC);
01878 }
01879 if (leapSecond)
01880 {
01881
01882
01883 if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400)
01884 break;
01885 }
01886 if (invalid)
01887 {
01888 KDateTime dt;
01889 dt.d->status = invalid;
01890 return dt;
01891 }
01892 return result;
01893 }
01894 case ISODate:
01895 {
01896
01897
01898
01899
01900
01901
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912
01913 bool dateOnly = false;
01914
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
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
01926 dateOnly = true;
01927 rx = QRegExp("^([+-])?(\\d{4,})-(\\d\\d\\d|\\d\\d-\\d\\d)$");
01928 if (str.indexOf(rx))
01929 {
01930
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;
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
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);
01993 if (!d.isValid() || (!invalid && d.year() != year))
01994 break;
01995 day = d.day();
01996 month = d.month();
01997 }
01998 else
01999 {
02000
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);
02006 if (!d.isValid())
02007 break;
02008 }
02009 if (dateOnly)
02010 {
02011 if (invalid)
02012 {
02013 KDateTime dt;
02014 dt.d->status = invalid;
02015 return dt;
02016 }
02017 return KDateTime(d, Spec(ClockTime));
02018 }
02019 if (hour == 24 && !minute && !second && !msecs)
02020 {
02021
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
02032 if (invalid)
02033 {
02034 KDateTime dt;
02035 dt.d->status = invalid;
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
02063
02064 if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400)
02065 break;
02066 }
02067 if (invalid)
02068 {
02069 KDateTime dt;
02070 dt.d->status = invalid;
02071 return dt;
02072 }
02073 return KDateTime(d, t, Spec(spec, offset));
02074 }
02075 case QtTextDate:
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
02100 if (dateOnly)
02101 return KDateTime(qd, KDateTimePrivate::fromStringDefault());
02102 else
02103 {
02104
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
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;
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
02156 bool zname = false;
02157 KTimeZone zone;
02158 if (!zoneName.isEmpty())
02159 {
02160
02161
02162 zone = zones->zone(zoneName);
02163 zname = true;
02164 }
02165 else if (!invalid)
02166 {
02167 if (!zoneAbbrev.isEmpty())
02168 {
02169
02170
02171
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;
02187 ut.addSecs(offset - offset2);
02188 if (it.value().abbreviation(ut) != zoneAbbrev)
02189 continue;
02190 offset = offset2;
02191 }
02192
02193 if (zone.isValid())
02194 {
02195
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
02219
02220
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
02232 if (zone.isValid() || !utcOffset)
02233 {
02234
02235 if (!offsetIfAmbiguous)
02236 return KDateTime();
02237 if (invalid)
02238 {
02239 KDateTime dt;
02240 dt.d->status = invalid;
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();
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
02264 if (invalid)
02265 {
02266 KDateTime dt;
02267 dt.d->status = invalid;
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
02315
02316
02317
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':
02376 if (!getNumber(str, s, 4, 4, NO_NUMBER, -1, year))
02377 return QDateTime();
02378 break;
02379 case 'y':
02380 if (!getNumber(str, s, 2, 2, 0, 99, year))
02381 return QDateTime();
02382 year += (year <= 50) ? 2000 : 1999;
02383 break;
02384 case 'm':
02385 if (!getNumber(str, s, 2, 2, 1, 12, month))
02386 return QDateTime();
02387 break;
02388 case 'B':
02389 case 'b':
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':
02398 if (!getNumber(str, s, 2, 2, 1, 31, day))
02399 return QDateTime();
02400 break;
02401 case 'e':
02402 if (!getNumber(str, s, 1, 2, 1, 31, day))
02403 return QDateTime();
02404 break;
02405 case 'A':
02406 case 'a':
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':
02415 if (!getNumber(str, s, 2, 2, 0, 23, hour))
02416 return QDateTime();
02417 break;
02418 case 'k':
02419 if (!getNumber(str, s, 1, 2, 0, 23, hour))
02420 return QDateTime();
02421 break;
02422 case 'I':
02423 if (!getNumber(str, s, 2, 2, 1, 12, hour))
02424 return QDateTime();
02425 break;
02426 case 'l':
02427 if (!getNumber(str, s, 1, 2, 1, 12, hour))
02428 return QDateTime();
02429 break;
02430 case 'M':
02431 if (!getNumber(str, s, 2, 2, 0, 59, minute))
02432 return QDateTime();
02433 break;
02434 case 'S':
02435 if (!getNumber(str, s, 2, 2, 0, 59, second))
02436 return QDateTime();
02437 break;
02438 case 's':
02439 if (!getNumber(str, s, 1, 2, 0, 59, second))
02440 return QDateTime();
02441 break;
02442 case 'P':
02443 case 'p':
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':
02452 zone = UTCOffset;
02453 break;
02454 case 'Z':
02455 zone = TZAbbrev;
02456 break;
02457 case 't':
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
02472 switch (ch)
02473 {
02474 case 'Y':
02475 if (!getNumber(str, s, 4, 100, NO_NUMBER, -1, year))
02476 return QDateTime();
02477 break;
02478 case 'A':
02479 case 'a':
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':
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':
02497 if (!getNumber(str, s, 1, 2, 1, 12, month))
02498 return QDateTime();
02499 break;
02500 case 'P':
02501 case 'p':
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':
02510 if (!getNumber(str, s, 1, 2, 0, 59, minute))
02511 return QDateTime();
02512 break;
02513 case 'S':
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':
02524 {
02525 if (str[s] != QLatin1Char('.'))
02526 {
02527
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':
02552 zone = UTCOffset;
02553 break;
02554 case 'z':
02555 zone = UTCOffsetColon;
02556 break;
02557 case 'Z':
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
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:
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:
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
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
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);
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
02690
02691
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
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
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
02731
02732
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
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
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
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
02814
02815
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
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
02854
02855
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
02898
02899
02900
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
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