00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "config.h"
00022
00023 #ifdef HAVE_SYS_TIME_H
00024 #include <sys/time.h>
00025 #endif
00026 #ifdef HAVE_TIME_H
00027 #include <time.h>
00028 #endif
00029
00030 #include "kdatetime.h"
00031
00032 #include <stdlib.h>
00033 #include <stdio.h>
00034 #include <ctype.h>
00035
00036 #include <QtCore/QDateTime>
00037 #include <QtCore/QRegExp>
00038 #include <QtCore/QStringList>
00039 #include <QtCore/QSharedData>
00040
00041 #include <kglobal.h>
00042 #include <klocale.h>
00043 #include <kcalendarsystemgregorian.h>
00044 #include <ksystemtimezone.h>
00045 #include <kdebug.h>
00046
00047 #ifdef Q_OS_WIN
00048 #include <windows.h>
00049 #endif
00050
00051
00052 static const char shortDay[][4] = {
00053 "Mon", "Tue", "Wed",
00054 "Thu", "Fri", "Sat",
00055 "Sun"
00056 };
00057 static const char longDay[][10] = {
00058 "Monday", "Tuesday", "Wednesday",
00059 "Thursday", "Friday", "Saturday",
00060 "Sunday"
00061 };
00062 static const char shortMonth[][4] = {
00063 "Jan", "Feb", "Mar", "Apr",
00064 "May", "Jun", "Jul", "Aug",
00065 "Sep", "Oct", "Nov", "Dec"
00066 };
00067 static const char longMonth[][10] = {
00068 "January", "February", "March",
00069 "April", "May", "June",
00070 "July", "August", "September",
00071 "October", "November", "December"
00072 };
00073
00074
00075
00076 enum Status {
00077 stValid = 0,
00078 stTooEarly
00079 };
00080
00081
00082 static QDateTime fromStr(const QString& string, const QString& format, int& utcOffset,
00083 QString& zoneName, QByteArray& zoneAbbrev, bool& dateOnly, Status&);
00084 static int matchDay(const QString &string, int &offset, KCalendarSystem*);
00085 static int matchMonth(const QString &string, int &offset, KCalendarSystem*);
00086 static bool getUTCOffset(const QString &string, int &offset, bool colon, int &result);
00087 static int getAmPm(const QString &string, int &offset, KLocale*);
00088 static bool getNumber(const QString &string, int &offset, int mindigits, int maxdigits, int minval, int maxval, int &result);
00089 static int findString_internal(const QString &string, const char *ptr, int count, int &offset, int disp);
00090 template<int disp> static inline
00091 int findString(const QString &string, const char array[][disp], int count, int &offset)
00092 { return findString_internal(string, array[0], count, offset, disp); }
00093 static QDate checkDate(int year, int month, int day, Status&);
00094
00095 static const int MIN_YEAR = -4712;
00096 static const int NO_NUMBER = 0x8000000;
00097
00098 #ifdef COMPILING_TESTS
00099 KDECORE_EXPORT int KDateTime_utcCacheHit = 0;
00100 KDECORE_EXPORT int KDateTime_zoneCacheHit = 0;
00101 #endif
00102
00103
00104
00105 class KDateTimeSpecPrivate
00106 {
00107 public:
00108 KDateTimeSpecPrivate() {}
00109
00110 KTimeZone tz;
00111 int utcOffset;
00112 KDateTime::SpecType type;
00113 };
00114
00115
00116 KDateTime::Spec::Spec()
00117 : d(new KDateTimeSpecPrivate)
00118 {
00119 d->type = KDateTime::Invalid;
00120 }
00121
00122 KDateTime::Spec::Spec(const KTimeZone &tz)
00123 : d(new KDateTimeSpecPrivate())
00124 {
00125 setType(tz);
00126 }
00127
00128 KDateTime::Spec::Spec(SpecType type, int utcOffset)
00129 : d(new KDateTimeSpecPrivate())
00130 {
00131 setType(type, utcOffset);
00132 }
00133
00134 KDateTime::Spec::Spec(const Spec& spec)
00135 : d(new KDateTimeSpecPrivate())
00136 {
00137 operator=(spec);
00138 }
00139
00140 KDateTime::Spec::~Spec()
00141 {
00142 delete d;
00143 }
00144
00145 KDateTime::Spec &KDateTime::Spec::operator=(const Spec& spec)
00146 {
00147 d->type = spec.d->type;
00148 if (d->type == KDateTime::TimeZone)
00149 d->tz = spec.d->tz;
00150 else if (d->type == KDateTime::OffsetFromUTC)
00151 d->utcOffset = spec.d->utcOffset;
00152 return *this;
00153 }
00154
00155 void KDateTime::Spec::setType(SpecType type, int utcOffset)
00156 {
00157 switch (type)
00158 {
00159 case KDateTime::OffsetFromUTC:
00160 d->utcOffset = utcOffset;
00161
00162 case KDateTime::UTC:
00163 case KDateTime::ClockTime:
00164 d->type = type;
00165 break;
00166 case KDateTime::LocalZone:
00167 d->tz = KSystemTimeZones::local();
00168 d->type = KDateTime::TimeZone;
00169 break;
00170 case KDateTime::TimeZone:
00171 default:
00172 d->type = KDateTime::Invalid;
00173 break;
00174 }
00175 }
00176
00177 void KDateTime::Spec::setType(const KTimeZone &tz)
00178 {
00179 if (tz == KTimeZone::utc())
00180 d->type = KDateTime::UTC;
00181 else if (tz.isValid())
00182 {
00183 d->type = KDateTime::TimeZone;
00184 d->tz = tz;
00185 }
00186 else
00187 d->type = KDateTime::Invalid;
00188 }
00189
00190 KTimeZone KDateTime::Spec::timeZone() const
00191 {
00192 if (d->type == KDateTime::TimeZone)
00193 return d->tz;
00194 if (d->type == KDateTime::UTC)
00195 return KTimeZone::utc();
00196 return KTimeZone();
00197 }
00198
00199 bool KDateTime::Spec::isUtc() const
00200 {
00201 if (d->type == KDateTime::UTC
00202 || (d->type == KDateTime::OffsetFromUTC && d->utcOffset == 0))
00203 return true;
00204 return false;
00205 }
00206
00207 KDateTime::Spec KDateTime::Spec::UTC() { return Spec(KDateTime::UTC); }
00208 KDateTime::Spec KDateTime::Spec::ClockTime() { return Spec(KDateTime::ClockTime); }
00209 KDateTime::Spec KDateTime::Spec::LocalZone() { return Spec(KDateTime::LocalZone); }
00210 KDateTime::Spec KDateTime::Spec::OffsetFromUTC(int utcOffset) { return Spec(KDateTime::OffsetFromUTC, utcOffset); }
00211 KDateTime::SpecType KDateTime::Spec::type() const { return d->type; }
00212 bool KDateTime::Spec::isValid() const { return d->type != KDateTime::Invalid; }
00213 bool KDateTime::Spec::isLocalZone() const { return d->type == KDateTime::TimeZone && d->tz == KSystemTimeZones::local(); }
00214 bool KDateTime::Spec::isClockTime() const { return d->type == KDateTime::ClockTime; }
00215 bool KDateTime::Spec::isOffsetFromUtc() const { return d->type == KDateTime::OffsetFromUTC; }
00216 int KDateTime::Spec::utcOffset() const { return d->type == KDateTime::OffsetFromUTC ? d->utcOffset : 0; }
00217
00218 bool KDateTime::Spec::operator==(const Spec &other) const
00219 {
00220 if (d->type != other.d->type
00221 || (d->type == KDateTime::TimeZone && d->tz != other.d->tz)
00222 || (d->type == KDateTime::OffsetFromUTC && d->utcOffset != other.d->utcOffset))
00223 return false;
00224 return true;
00225 }
00226
00227 bool KDateTime::Spec::equivalentTo(const Spec &other) const
00228 {
00229 if (d->type == other.d->type)
00230 {
00231 if (d->type == KDateTime::TimeZone && d->tz != other.d->tz
00232 || (d->type == KDateTime::OffsetFromUTC && d->utcOffset != other.d->utcOffset))
00233 return false;
00234 return true;
00235 }
00236 else
00237 {
00238 if (d->type == KDateTime::UTC && other.d->type == KDateTime::OffsetFromUTC && other.d->utcOffset == 0
00239 || (other.d->type == KDateTime::UTC && d->type == KDateTime::OffsetFromUTC && d->utcOffset == 0))
00240 return true;
00241 return false;
00242 }
00243 }
00244
00245 QDataStream & operator<<(QDataStream &s, const KDateTime::Spec &spec)
00246 {
00247
00248
00249 switch (spec.type())
00250 {
00251 case KDateTime::UTC:
00252 s << static_cast<quint8>('u');
00253 break;
00254 case KDateTime::OffsetFromUTC:
00255 s << static_cast<quint8>('o') << spec.utcOffset();
00256 break;
00257 case KDateTime::TimeZone:
00258 #ifdef __GNUC__
00259 #warning TODO: write full time zone data?
00260 #endif
00261 s << static_cast<quint8>('z') << (spec.timeZone().isValid() ? spec.timeZone().name() : QString());
00262 break;
00263 case KDateTime::ClockTime:
00264 s << static_cast<quint8>('c');
00265 break;
00266 case KDateTime::Invalid:
00267 default:
00268 s << static_cast<quint8>(' ');
00269 break;
00270 }
00271 return s;
00272 }
00273
00274 QDataStream & operator>>(QDataStream &s, KDateTime::Spec &spec)
00275 {
00276
00277
00278 quint8 t;
00279 s >> t;
00280 switch (static_cast<char>(t))
00281 {
00282 case 'u':
00283 spec.setType(KDateTime::UTC);
00284 break;
00285 case 'o':
00286 {
00287 int utcOffset;
00288 s >> utcOffset;
00289 spec.setType(KDateTime::OffsetFromUTC, utcOffset);
00290 break;
00291 }
00292 case 'z':
00293 {
00294 QString zone;
00295 s >> zone;
00296 KTimeZone tz = KSystemTimeZones::zone(zone);
00297 #ifdef __GNUC__
00298 #warning TODO: read full time zone data?
00299 #endif
00300 spec.setType(tz);
00301 break;
00302 }
00303 case 'c':
00304 spec.setType(KDateTime::ClockTime);
00305 break;
00306 default:
00307 spec.setType(KDateTime::Invalid);
00308 break;
00309 }
00310 return s;
00311 }
00312
00313
00314
00315
00316 K_GLOBAL_STATIC_WITH_ARGS(KDateTime::Spec, s_fromStringDefault, (KDateTime::ClockTime))
00317
00318 class KDateTimePrivate : public QSharedData
00319 {
00320 public:
00321 KDateTimePrivate()
00322 : QSharedData(),
00323 specType(KDateTime::Invalid),
00324 status(stValid),
00325 utcCached(true),
00326 convertedCached(false),
00327 m2ndOccurrence(false),
00328 mDateOnly(false)
00329 {
00330 }
00331
00332 KDateTimePrivate(const QDateTime &d, const KDateTime::Spec &s, bool donly = false)
00333 : QSharedData(),
00334 mDt(d),
00335 specType(s.type()),
00336 status(stValid),
00337 utcCached(false),
00338 convertedCached(false),
00339 m2ndOccurrence(false),
00340 mDateOnly(donly)
00341 {
00342 switch (specType)
00343 {
00344 case KDateTime::TimeZone:
00345 specZone = s.timeZone();
00346 break;
00347 case KDateTime::OffsetFromUTC:
00348 specUtcOffset= s.utcOffset();
00349 break;
00350 case KDateTime::Invalid:
00351 utcCached = true;
00352
00353 case KDateTime::UTC:
00354 default:
00355 break;
00356 }
00357 }
00358
00359 KDateTimePrivate(const KDateTimePrivate &rhs)
00360 : QSharedData(rhs),
00361 mDt(rhs.mDt),
00362 specZone(rhs.specZone),
00363 specUtcOffset(rhs.specUtcOffset),
00364 ut(rhs.ut),
00365 converted(rhs.converted),
00366 specType(rhs.specType),
00367 status(rhs.status),
00368 utcCached(rhs.utcCached),
00369 convertedCached(rhs.convertedCached),
00370 m2ndOccurrence(rhs.m2ndOccurrence),
00371 mDateOnly(rhs.mDateOnly),
00372 converted2ndOccur(rhs.converted2ndOccur)
00373 {}
00374
00375 ~KDateTimePrivate() {}
00376 const QDateTime& dt() const { return mDt; }
00377 const QDate date() const { return mDt.date(); }
00378 KDateTime::Spec spec() const;
00379 QDateTime utc() const { return QDateTime(ut.date, ut.time, Qt::UTC); }
00380 bool dateOnly() const { return mDateOnly; }
00381 bool secondOccurrence() const { return m2ndOccurrence; }
00382 void setDt(const QDateTime &dt) { mDt = dt; utcCached = convertedCached = m2ndOccurrence = false; }
00383 void setDtFromUtc(const QDateTime &utcdt);
00384 void setDate(const QDate &d) { mDt.setDate(d); utcCached = convertedCached = m2ndOccurrence = false; }
00385 void setTime(const QTime &t) { mDt.setTime(t); utcCached = convertedCached = mDateOnly = m2ndOccurrence = false; }
00386 void setDtTimeSpec(Qt::TimeSpec s) { mDt.setTimeSpec(s); utcCached = convertedCached = m2ndOccurrence = false; }
00387 void setSpec(const KDateTime::Spec&);
00388 void setDateOnly(bool d);
00389 int timeZoneOffset() const;
00390 QDateTime toUtc(const KTimeZone &local = KTimeZone()) const;
00391 QDateTime toZone(const KTimeZone &zone, const KTimeZone &local = KTimeZone()) const;
00392 void newToZone(KDateTimePrivate *newd, const KTimeZone &zone, const KTimeZone &local = KTimeZone()) const;
00393 bool equalSpec(const KDateTimePrivate&) const;
00394 void clearCache() { utcCached = convertedCached = false; }
00395 void setDt(const QDateTime &dt, const QDateTime &utcDt)
00396 {
00397 mDt = dt;
00398 ut.date = utcDt.date();
00399 ut.time = utcDt.time();
00400 utcCached = true;
00401 convertedCached = false;
00402 m2ndOccurrence = false;
00403 }
00404 void setUtc(const QDateTime &dt) const
00405 {
00406 ut.date = dt.date();
00407 ut.time = dt.time();
00408 utcCached = true;
00409 convertedCached = false;
00410 }
00411
00412
00413
00414
00415 void setUtcFromTz(const QDateTime &dt, const KTimeZone &tz)
00416 {
00417 if (specType == KDateTime::UTC)
00418 {
00419 mDt = tz.toUtc(dt);
00420 utcCached = false;
00421 converted.date = dt.date();
00422 converted.time = dt.time();
00423 converted.tz = tz;
00424 convertedCached = true;
00425 converted2ndOccur = false;
00426 }
00427 }
00428
00429
00430 static KDateTime::Spec& fromStringDefault()
00431 {
00432 return *s_fromStringDefault;
00433 }
00434
00435
00436 static QTime sod;
00437
00438
00439
00440
00441
00442
00443
00444 private:
00445 QDateTime mDt;
00446 public:
00447 KTimeZone specZone;
00448
00449 int specUtcOffset;
00450 mutable struct ut {
00451 QDate date;
00452 QTime time;
00453 } ut;
00454 private:
00455 mutable struct converted {
00456 QDate date;
00457 QTime time;
00458 KTimeZone tz;
00459 } converted;
00460 public:
00461 KDateTime::SpecType specType : 3;
00462 Status status : 2;
00463 mutable bool utcCached : 1;
00464 mutable bool convertedCached : 1;
00465 mutable bool m2ndOccurrence : 1;
00466 private:
00467 bool mDateOnly : 1;
00468 mutable bool converted2ndOccur : 1;
00469 };
00470
00471
00472 QTime KDateTimePrivate::sod(0,0,0);
00473
00474 KDateTime::Spec KDateTimePrivate::spec() const
00475 {
00476 if (specType == KDateTime::TimeZone)
00477 return KDateTime::Spec(specZone);
00478 else
00479 return KDateTime::Spec(specType, specUtcOffset);
00480 }
00481
00482 void KDateTimePrivate::setSpec(const KDateTime::Spec &other)
00483 {
00484 if (specType == other.type())
00485 {
00486 switch (specType)
00487 {
00488 case KDateTime::TimeZone:
00489 {
00490 KTimeZone tz = other.timeZone();
00491 if (specZone == tz)
00492 return;
00493 specZone = tz;
00494 break;
00495 }
00496 case KDateTime::OffsetFromUTC:
00497 {
00498 int offset = other.utcOffset();
00499 if (specUtcOffset == offset)
00500 return;
00501 specUtcOffset = offset;
00502 break;
00503 }
00504 default:
00505 return;
00506 }
00507 utcCached = false;
00508 }
00509 else
00510 {
00511 specType = other.type();
00512 switch (specType)
00513 {
00514 case KDateTime::TimeZone:
00515 specZone = other.timeZone();
00516 break;
00517 case KDateTime::OffsetFromUTC:
00518 specUtcOffset = other.utcOffset();
00519 break;
00520 case KDateTime::Invalid:
00521 ut.date = QDate();
00522 utcCached = true;
00523
00524 case KDateTime::UTC:
00525 default:
00526 break;
00527 }
00528 }
00529 convertedCached = false;
00530 setDtTimeSpec((specType == KDateTime::UTC) ? Qt::UTC : Qt::LocalTime);
00531 }
00532
00533 bool KDateTimePrivate::equalSpec(const KDateTimePrivate &other) const
00534 {
00535 if (specType != other.specType
00536 || (specType == KDateTime::TimeZone && specZone != other.specZone)
00537 || (specType == KDateTime::OffsetFromUTC && specUtcOffset != other.specUtcOffset))
00538 return false;
00539 return true;
00540 }
00541
00542 void KDateTimePrivate::setDateOnly(bool dateOnly)
00543 {
00544 if (dateOnly != mDateOnly)
00545 {
00546 mDateOnly = dateOnly;
00547 if (dateOnly && mDt.time() != sod)
00548 {
00549 mDt.setTime(sod);
00550 utcCached = false;
00551 convertedCached = false;
00552 }
00553 m2ndOccurrence = false;
00554 }
00555 }
00556
00557
00558 void KDateTimePrivate::setDtFromUtc(const QDateTime &utcdt)
00559 {
00560 switch (specType)
00561 {
00562 case KDateTime::UTC:
00563 setDt(utcdt);
00564 break;
00565 case KDateTime::OffsetFromUTC:
00566 {
00567 QDateTime local = utcdt.addSecs(specUtcOffset);
00568 local.setTimeSpec(Qt::LocalTime);
00569 setDt(local, utcdt);
00570 break;
00571 }
00572 case KDateTime::TimeZone:
00573 {
00574 bool second;
00575 setDt(specZone.toZoneTime(utcdt, &second), utcdt);
00576 m2ndOccurrence = second;
00577 break;
00578 }
00579 case KDateTime::ClockTime:
00580 specZone = KSystemTimeZones::local();
00581 setDt(specZone.toZoneTime(utcdt), utcdt);
00582 break;
00583 default:
00584 break;
00585 }
00586 }
00587
00588
00589
00590
00591 int KDateTimePrivate::timeZoneOffset() const
00592 {
00593 if (specType != KDateTime::TimeZone)
00594 return KTimeZone::InvalidOffset;
00595 if (utcCached)
00596 {
00597 QDateTime dt = mDt;
00598 dt.setTimeSpec(Qt::UTC);
00599 return utc().secsTo(dt);
00600 }
00601 int secondOffset;
00602 if (!specZone.isValid()) {
00603 return KTimeZone::InvalidOffset;
00604 }
00605 int offset = specZone.offsetAtZoneTime(mDt, &secondOffset);
00606 if (m2ndOccurrence)
00607 {
00608 m2ndOccurrence = (secondOffset != offset);
00609 offset = secondOffset;
00610 }
00611 if (offset == KTimeZone::InvalidOffset)
00612 {
00613 ut.date = QDate();
00614 utcCached = true;
00615 convertedCached = false;
00616 }
00617 else
00618 {
00619
00620 QDateTime utcdt = mDt;
00621 utcdt.setTimeSpec(Qt::UTC);
00622 setUtc(utcdt.addSecs(-offset));
00623 }
00624 return offset;
00625 }
00626
00627
00628
00629
00630
00631
00632 QDateTime KDateTimePrivate::toUtc(const KTimeZone &local) const
00633 {
00634 KTimeZone loc(local);
00635 if (utcCached)
00636 {
00637
00638 if (specType == KDateTime::ClockTime)
00639 {
00640
00641
00642 if (!local.isValid())
00643 loc = KSystemTimeZones::local();
00644 if (specZone == loc)
00645 {
00646
00647 #ifdef COMPILING_TESTS
00648 ++KDateTime_utcCacheHit;
00649 #endif
00650 return utc();
00651 }
00652 }
00653 else
00654 {
00655
00656 #ifdef COMPILING_TESTS
00657 ++KDateTime_utcCacheHit;
00658 #endif
00659 return utc();
00660 }
00661 }
00662
00663
00664 switch (specType)
00665 {
00666 case KDateTime::UTC:
00667 return mDt;
00668 case KDateTime::OffsetFromUTC:
00669 {
00670 if (!mDt.isValid())
00671 break;
00672 QDateTime dt = QDateTime(mDt.date(), mDt.time(), Qt::UTC).addSecs(-specUtcOffset);
00673 setUtc(dt);
00674
00675 return dt;
00676 }
00677 case KDateTime::ClockTime:
00678 {
00679 if (!mDt.isValid())
00680 break;
00681 if (!loc.isValid())
00682 loc = KSystemTimeZones::local();
00683 const_cast<KDateTimePrivate*>(this)->specZone = loc;
00684 QDateTime dt(specZone.toUtc(mDt));
00685 setUtc(dt);
00686
00687 return dt;
00688 }
00689 case KDateTime::TimeZone:
00690 if (!mDt.isValid())
00691 break;
00692 timeZoneOffset();
00693
00694 return utc();
00695 default:
00696 break;
00697 }
00698
00699
00700 ut.date = QDate();
00701 utcCached = true;
00702 convertedCached = false;
00703
00704 return mDt;
00705 }
00706
00707
00708
00709
00710
00711 QDateTime KDateTimePrivate::toZone(const KTimeZone &zone, const KTimeZone &local) const
00712 {
00713 if (convertedCached && converted.tz == zone)
00714 {
00715
00716 #ifdef COMPILING_TESTS
00717
00718 ++KDateTime_zoneCacheHit;
00719 #endif
00720 return QDateTime(converted.date, converted.time, Qt::LocalTime);
00721 }
00722 else
00723 {
00724
00725 bool second;
00726 QDateTime result = zone.toZoneTime(toUtc(local), &second);
00727 converted.date = result.date();
00728 converted.time = result.time();
00729 converted.tz = zone;
00730 convertedCached = true;
00731 converted2ndOccur = second;
00732 return result;
00733 }
00734 }
00735
00736
00737
00738
00739
00740 void KDateTimePrivate::newToZone(KDateTimePrivate *newd, const KTimeZone &zone, const KTimeZone &local) const
00741 {
00742 newd->mDt = toZone(zone, local);
00743 newd->specZone = zone;
00744 newd->specType = KDateTime::TimeZone;
00745 newd->utcCached = utcCached;
00746 newd->mDateOnly = mDateOnly;
00747 newd->m2ndOccurrence = converted2ndOccur;
00748 switch (specType)
00749 {
00750 case KDateTime::UTC:
00751 newd->ut.date = mDt.date();
00752 newd->ut.time = mDt.time();
00753 break;
00754 case KDateTime::TimeZone:
00755
00756 newd->converted.date = mDt.date();
00757 newd->converted.time = mDt.time();
00758 newd->converted.tz = specZone;
00759 newd->convertedCached = true;
00760 newd->converted2ndOccur = m2ndOccurrence;
00761 newd->ut = ut;
00762 return;
00763 default:
00764 newd->ut = ut;
00765 break;
00766 }
00767 newd->convertedCached = false;
00768 }
00769
00770
00771
00772
00773 KDateTime::KDateTime()
00774 : d(new KDateTimePrivate)
00775 {
00776 }
00777
00778 KDateTime::KDateTime(const QDate &date, const Spec &spec)
00779 : d(new KDateTimePrivate(QDateTime(date, KDateTimePrivate::sod, Qt::LocalTime), spec, true))
00780 {
00781 if (spec.type() == UTC)
00782 d->setDtTimeSpec(Qt::UTC);
00783 }
00784
00785 KDateTime::KDateTime(const QDate &date, const QTime &time, const Spec &spec)
00786 : d(new KDateTimePrivate(QDateTime(date, time, Qt::LocalTime), spec))
00787 {
00788 if (spec.type() == UTC)
00789 d->setDtTimeSpec(Qt::UTC);
00790 }
00791
00792 KDateTime::KDateTime(const QDateTime &dt, const Spec &spec)
00793 : d(new KDateTimePrivate(dt, spec))
00794 {
00795
00796 if (spec.type() == UTC)
00797 {
00798 if (dt.timeSpec() == Qt::LocalTime)
00799 d->setUtcFromTz(dt, KSystemTimeZones::local());
00800 }
00801 else if (dt.timeSpec() == Qt::UTC)
00802 d->setDtFromUtc(dt);
00803 }
00804
00805 KDateTime::KDateTime(const QDateTime &dt)
00806 : d(new KDateTimePrivate(dt, (dt.timeSpec() == Qt::LocalTime ? Spec(LocalZone) : Spec(UTC))))
00807 {
00808 }
00809
00810 KDateTime::KDateTime(const KDateTime &other)
00811 : d(other.d)
00812 {
00813 }
00814
00815 KDateTime::~KDateTime()
00816 {
00817 }
00818
00819 KDateTime &KDateTime::operator=(const KDateTime &other)
00820 {
00821 d = other.d;
00822 return *this;
00823 }
00824
00825 void KDateTime::detach() { d.detach(); }
00826 bool KDateTime::isNull() const { return d->dt().isNull(); }
00827 bool KDateTime::isValid() const { return d->specType != Invalid && d->dt().isValid(); }
00828 bool KDateTime::outOfRange() const { return d->status == stTooEarly; }
00829 bool KDateTime::isDateOnly() const { return d->dateOnly(); }
00830 bool KDateTime::isLocalZone() const { return d->specType == TimeZone && d->specZone == KSystemTimeZones::local(); }
00831 bool KDateTime::isClockTime() const { return d->specType == ClockTime; }
00832 bool KDateTime::isUtc() const { return d->specType == UTC || (d->specType == OffsetFromUTC && d->specUtcOffset == 0); }
00833 bool KDateTime::isOffsetFromUtc() const { return d->specType == OffsetFromUTC; }
00834 bool KDateTime::isSecondOccurrence() const { return d->specType == TimeZone && d->secondOccurrence(); }
00835 QDate KDateTime::date() const { return d->date(); }
00836 QTime KDateTime::time() const { return d->dt().time(); }
00837 QDateTime KDateTime::dateTime() const { return d->dt(); }
00838
00839 KDateTime::Spec KDateTime::timeSpec() const { return d->spec(); }
00840 KDateTime::SpecType KDateTime::timeType() const { return d->specType; }
00841
00842 KTimeZone KDateTime::timeZone() const
00843 {
00844 switch (d->specType)
00845 {
00846 case TimeZone:
00847 return d->specZone;
00848 case UTC:
00849 return KTimeZone::utc();
00850 default:
00851 return KTimeZone();
00852 }
00853 }
00854
00855 int KDateTime::utcOffset() const
00856 {
00857 switch (d->specType)
00858 {
00859 case TimeZone:
00860 return d->timeZoneOffset();
00861 case OffsetFromUTC:
00862 return d->specUtcOffset;
00863 default:
00864 return 0;
00865 }
00866 }
00867
00868 KDateTime KDateTime::toUtc() const
00869 {
00870 if (!isValid())
00871 return KDateTime();
00872 if (d->specType == UTC)
00873 return *this;
00874 if (d->dateOnly())
00875 return KDateTime(d->date(), UTC);
00876 QDateTime udt = d->toUtc();
00877 if (!udt.isValid())
00878 return KDateTime();
00879 return KDateTime(udt, UTC);
00880 }
00881
00882 KDateTime KDateTime::toOffsetFromUtc() const
00883 {
00884 if (!isValid())
00885 return KDateTime();
00886 int offset = 0;
00887 switch (d->specType)
00888 {
00889 case OffsetFromUTC:
00890 return *this;
00891 case UTC:
00892 {
00893 if (d->dateOnly())
00894 return KDateTime(d->date(), Spec(OffsetFromUTC, 0));
00895 QDateTime qdt = d->dt();
00896 qdt.setTimeSpec(Qt::LocalTime);
00897 return KDateTime(qdt, Spec(OffsetFromUTC, 0));
00898 }
00899 case TimeZone:
00900 offset = d->timeZoneOffset();
00901 break;
00902 case ClockTime:
00903 offset = KSystemTimeZones::local().offsetAtZoneTime(d->dt());
00904 break;
00905 default:
00906 return KDateTime();
00907 }
00908 if (d->dateOnly())
00909 return KDateTime(d->date(), Spec(OffsetFromUTC, offset));
00910 return KDateTime(d->dt(), Spec(OffsetFromUTC, offset));
00911 }
00912
00913 KDateTime KDateTime::toOffsetFromUtc(int utcOffset) const
00914 {
00915 if (!isValid())
00916 return KDateTime();
00917 if (d->specType == OffsetFromUTC && d->specUtcOffset == utcOffset)
00918 return *this;
00919 if (d->dateOnly())
00920 return KDateTime(d->date(), Spec(OffsetFromUTC, utcOffset));
00921 return KDateTime(d->toUtc(), Spec(OffsetFromUTC, utcOffset));
00922 }
00923
00924 KDateTime KDateTime::toLocalZone() const
00925 {
00926 if (!isValid())
00927 return KDateTime();
00928 KTimeZone local = KSystemTimeZones::local();
00929 if (d->specType == TimeZone && d->specZone == local)
00930 return *this;
00931 if (d->dateOnly())
00932 return KDateTime(d->date(), Spec(local));
00933 switch (d->specType)
00934 {
00935 case TimeZone:
00936 case OffsetFromUTC:
00937 case UTC:
00938 {
00939 KDateTime result;
00940 d->newToZone(result.d, local, local);
00941 return result;
00942 }
00943 case ClockTime:
00944 return KDateTime(d->dt(), Spec(local));
00945 default:
00946 return KDateTime();
00947 }
00948 }
00949
00950 KDateTime KDateTime::toClockTime() const
00951 {
00952 if (!isValid())
00953 return KDateTime();
00954 if (d->specType == ClockTime)
00955 return *this;
00956 if (d->dateOnly())
00957 return KDateTime(d->date(), Spec(ClockTime));
00958 KDateTime result = toLocalZone();
00959 result.d->specType = ClockTime;
00960 return result;
00961 }
00962
00963 KDateTime KDateTime::toZone(const KTimeZone &zone) const
00964 {
00965 if (!zone.isValid() || !isValid())
00966 return KDateTime();
00967 if (d->specType == TimeZone && d->specZone == zone)
00968 return *this;
00969 if (d->dateOnly())
00970 return KDateTime(d->date(), Spec(zone));
00971 KDateTime result;
00972 d->newToZone(result.d, zone);
00973 return result;
00974 }
00975
00976 KDateTime KDateTime::toTimeSpec(const KDateTime &dt) const
00977 {
00978 return toTimeSpec(dt.timeSpec());
00979 }
00980
00981 KDateTime KDateTime::toTimeSpec(const Spec &spec) const
00982 {
00983 if (spec == d->spec())
00984 return *this;
00985 if (!isValid())
00986 return KDateTime();
00987 if (d->dateOnly())
00988 return KDateTime(d->date(), spec);
00989 if (spec.type() == TimeZone)
00990 {
00991 KDateTime result;
00992 d->newToZone(result.d, spec.timeZone());
00993 return result;
00994 }
00995 return KDateTime(d->toUtc(), spec);
00996 }
00997
00998 uint KDateTime::toTime_t() const
00999 {
01000 return d->toUtc().toTime_t();
01001 }
01002
01003 void KDateTime::setTime_t(qint64 seconds)
01004 {
01005 d->setSpec(UTC);
01006 int days = static_cast<int>(seconds / 86400);
01007 int secs = static_cast<int>(seconds % 86400);
01008 QDateTime dt;
01009 dt.setTimeSpec(Qt::UTC);
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 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;
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