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