/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qplatformdefs.h" //#include "private/qdatetime_p.h" #include #include #include #include "qdatastream.h" #include "qlocale.h" #include "qtimespan.h" #include "qdebug.h" #include "qcoreapplication.h" #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) #include #endif #ifndef Q_WS_WIN #include #endif #include #if defined(Q_OS_WINCE) #include "qfunctions_wince.h" #endif #if defined(Q_WS_MAC) #include #endif #if defined(Q_OS_SYMBIAN) #include #endif /*! \class QTimeSpan \brief The QTimeSpan represents a span of time \since 4.8 QTimeSpan represents a span of time, which is optionally in reference to a specific point in time. A QTimeSpan behaves slightly different if it has a reference date or time or not. \section1 Constructing a QTimeSpan A QTimeSpan can be created by initializing it directly with a length and optionally with a reference (start) date, or by substracting two QDate or QDateTime values. By substracting QDate or QDateTime values, you create a QTimeSpan with the QDate or QDateTime on the right hand side of the - operator as the reference date. \code //Creates a QTimeSpan representing the time from October 10, 1975 to now QDate birthDay(1975, 10, 10); QTimeSpan age = QDate::currentDate() - birthDay; \endcode QTimeSpan defines a series of constants that can be used for initializing a QTimeSpan. Second, Minute, Hour, Day and Week are all QTimeSpan instances with the corresponding length and no reference date. You can use those to create new instances. See the section on Date arithmatic below. \code //Creates a QTimeSpan representing 2 days, 4 hours and 31 minutes. QTimeSpan span(2 * QTimeSpan::Day + 4 * QTimeSpan::Hour + 31 * QTimeSpan::Minute); \endcode Finally, a QTimeSpan can be constructed by using one of the static constructors fromString or fromTimeUnit. \section1 Date arithmatic A negative QTimeSpan means that the reference date lies before the referenced date. Call normalize() to ensure that the reference date is smaller or equal than the referenced date. Basic arithmatic can be done with QTimeSpan. QTimeSpans can be added up or substracted, or be multiplied by a scalar factor. For this, the usual operators are implemented. The union of QTimeSpans will yield the minimal QTimeSpan that covers both the original QTimeSpans, while the intersection will yield the overlap between them (or an empty one if there is no overlap). Please refer to the method documentation for details on what happens to a reference date when using these methods. QTimeSpans can also be added to or substracted from a QDate, QTime or QDateTime. This will yield a new QDate, QTime or QDateTime moved by the value of the QTimeSpan. Note that the QTimeSpan must be the right-hand argument of the operator. You can not add a QDate to a QTimeSpan, but you can do the reverse. \code QTimeSpan span(QTimeSpan::Hour * 5 + 45 * QTimeSpan::Minute); QDateTime t1 = QDateTime::currentDateTime(); QDateTime t2 = t1 + span; // t2 is now the date time 5 hours and 45 minutes in the future. \endcode \section1 Accessing the length of a QTimeSpan There are two sets of methods that return the length of a QTimeSpan. The to* methods such as toSeconds and toMinutes return the total time in the requested unit. That may be a fractional number. \code QTimeSpan span = QTimeSpan::Hour * 6; qreal days = span.toDays(); //yields 0.25 \endcode On the other hand, you may be interested in a number of units at the same time. If you want to know the number of days, hours and minutes in a QTimeSpan, you can use the to*Part methods such as toDayPart and toHourPart. These functions take a QTimeSpan::TimeSpanFormat argument to indicate the units you want to use for the presentation of the QTimeSpan. This is used to calculate the number of the requested time units. You can also use the parts method directly, passing pointers to ints for the units you are interested in and 0 for the other units. \section1 Using months and years QTimeSpan can be used to describe any length of time, ranging from milliseconds to decades and beyond (up to the maximum value of a qint64 milliseconds; enough for any application not dealing with geological or astronomical time scales.) It is natural to use units like months and years when dealing with longer time periods, such as the age of people. The problem with these units is that unlike the time units for a week or shorter, the length of a month or a year is not fixed. It it dependent on the reference date. The time period '1 month' has a different meaning when we are speaking of februari or januari. QTimeSpan can only use the month and year time units if a valid reference date has been set. Without a valid reference date, month and year as time units are meaningless and their use will result in an assert. The largest unit of time that can be expressed without a reference date is a week. The time period of one month is understood to mean the period from a day and time one month to the same date and time in the next. If the next month does not have that date and time, one month will be taken to mean the period to the end of that month. \example The time from januari 2, 12 PM to februari 2, 12 PM will be understood as exactly one month. \example The time from januari 30, 2 PM to march 1, 00:00:00.000 will also be one month, because februari does not have 30 days but 28 or 29 depending on the year. \example The time from januari 30, 2 PM to march 30, 2 PM will be 2 months. The same goes for years. QTimeSpan stores the length of time intervals as a 64 bits integer representing milliseconds. That means that arithmatic with time periods set as months or years may not always yield what you expect. A time period set as the year describing the whole of 2007 that you multiply by two or add to itself, will not end up having a length of two years, but of 1 year, 11 months and 30 days, as 2008 is one day longer than 2007. When months and years are used, they are converted to the exact time span they describe in reference to the reference date set for the QTimeSpan. With another reference date, or when negated, that time span may or may not describe the same number of years and months. */ QT_BEGIN_NAMESPACE const QTimeSpan QTimeSpan::Second = QTimeSpan(qint64(1000)); const QTimeSpan QTimeSpan::Minute = QTimeSpan(qint64(1000 * 60)); const QTimeSpan QTimeSpan::Hour = QTimeSpan(qint64(1000 * 60 * 60)); const QTimeSpan QTimeSpan::Day = QTimeSpan(qint64(1000 * 60 * 60 * 24)); const QTimeSpan QTimeSpan::Week = QTimeSpan(qint64(1000 * 60 * 60 * 24 * 7)); class QTimeSpanPrivate : public QSharedData { public: qint64 interval; QDateTime reference; static const int s_daysPerMonth[12]; void addUnit(QTimeSpan* self, Qt::TimeSpanUnit unit, qreal value) { if (unit >= Qt::Months) { QTimeSpan tempSpan(self->referencedDate()); tempSpan.setFromTimeUnit(unit, value); interval += tempSpan.toMSecs(); } else { switch (unit) { case Qt::Weeks: interval += value * 1000LL * 60LL * 60LL * 24LL * 7LL; break; case Qt::Days: interval += value * 1000LL * 60LL * 60LL * 24LL; break; case Qt::Hours: interval += value * 1000LL * 60LL * 60LL ; break; case Qt::Minutes: interval += value * 1000LL * 60LL; break; case Qt::Seconds: interval += value * 1000LL; break; case Qt::Milliseconds: interval += value; break; default: break; } } } class TimePartHash: public QHash { public: TimePartHash(Qt::TimeSpanFormat format) { for (int i(Qt::Milliseconds); i <= Qt::Years; i *= 2) { Qt::TimeSpanUnit u = static_cast(i); if (format.testFlag(u)) { int* newValue = new int; *newValue = 0; insert(u, newValue); //perhaps we can optimize this not to new each int individually? } else { insert(u, 0); } } } ~TimePartHash() { qDeleteAll(*this); } inline bool fill(const QTimeSpan& span) { bool result = span.parts(value(Qt::Milliseconds), value(Qt::Seconds), value(Qt::Minutes), value(Qt::Hours), value(Qt::Days), value(Qt::Weeks), value(Qt::Months), value(Qt::Years)); return result; } inline void addUnit(const Qt::TimeSpanUnit unit) { if (value(unit) != 0) return; int* newValue = new int; *newValue = 0; insert(unit, newValue); } }; //returns a string representation of time in a single time unit QString unitString(Qt::TimeSpanUnit unit, int num) const { switch (unit) { case::Qt::Milliseconds: return qApp->translate("QTimeSpanPrivate", "%n millisecond(s)", 0, num); case::Qt::Seconds: return qApp->translate("QTimeSpanPrivate", "%n second(s)", 0, num); case::Qt::Minutes: return qApp->translate("QTimeSpanPrivate", "%n minute(s)", 0, num); case::Qt::Hours: return qApp->translate("QTimeSpanPrivate", "%n hour(s)", 0, num); case::Qt::Days: return qApp->translate("QTimeSpanPrivate", "%n day(s)", 0, num); case::Qt::Weeks: return qApp->translate("QTimeSpanPrivate", "%n week(s)", 0, num); case::Qt::Months: return qApp->translate("QTimeSpanPrivate", "%n month(s)", 0, num); case::Qt::Years: return qApp->translate("QTimeSpanPrivate", "%n year(s)", 0, num); default: return QString(); } } #ifndef QT_NO_DATESTRING struct TimeFormatToken { Qt::TimeSpanUnit type; //Qt::NoUnit is used for string literal types int length; //number of characters to use QString string; //only used for string literals }; QList parseFormatString(const QString& formatString, Qt::TimeSpanFormat &format) const { QHash tokenHash; tokenHash.insert(QChar('y'), Qt::Years); tokenHash.insert(QChar('M'), Qt::Months); tokenHash.insert(QChar('w'), Qt::Weeks); tokenHash.insert(QChar('d'), Qt::Days); tokenHash.insert(QChar('h'), Qt::Hours); tokenHash.insert(QChar('m'), Qt::Minutes); tokenHash.insert(QChar('s'), Qt::Seconds); tokenHash.insert(QChar('z'), Qt::Milliseconds); QList tokenList; format = Qt::NoUnit; int pos(0); int length(formatString.length()); bool inLiteral(false); while (pos < length) { const QChar currentChar(formatString[pos]); if (inLiteral) { if (currentChar == QLatin1Char('\'')) { inLiteral = false; //exit literal string mode if ((pos+1)interval = 0; } /*! \brief Constructor Constructs QTimeSpan of size msecs milliseconds. The reference date will be invalid. */ QTimeSpan::QTimeSpan(qint64 msecs) : d(new QTimeSpanPrivate) { d->interval = msecs; } /*! \brief Copy Constructor */ QTimeSpan::QTimeSpan(const QTimeSpan& other): d(other.d) { } /*! \brief Constructor Constructs QTimeSpan of size msecs milliseconds from the given reference date and time. */ QTimeSpan::QTimeSpan(const QDateTime &reference, qint64 msecs) : d(new QTimeSpanPrivate) { d->interval = msecs; d->reference = reference; } /*! \brief Constructor Constructs QTimeSpan of size msecs milliseconds from the given reference date. The reference time will be 0:00:00.000 */ QTimeSpan::QTimeSpan(const QDate &reference, quint64 msecs) : d(new QTimeSpanPrivate) { d->interval = msecs; d->reference = QDateTime(reference); } /*! \brief Constructor Constructs QTimeSpan of size msecs milliseconds from the given reference time. The reference date will be today's date. */ QTimeSpan::QTimeSpan(const QTime &reference, quint64 msecs) : d(new QTimeSpanPrivate) { d->interval = msecs; QDateTime todayReference(QDate::currentDate()); todayReference.setTime(reference); d->reference = todayReference; } /*! \brief Constructor Constructs a QTimeSpan of the same length as other from the given reference date time. */ QTimeSpan::QTimeSpan(const QDateTime& reference, const QTimeSpan& other) : d(new QTimeSpanPrivate) { d->reference = reference; d->interval = other.d->interval; } /*! \brief Constructor Constructs a QTimeSpan of the same length as other from the given reference date. The reference time will be 00:00:00.000 */ QTimeSpan::QTimeSpan(const QDate& reference, const QTimeSpan& other) : d(new QTimeSpanPrivate) { d->reference = QDateTime(reference); d->interval = other.d->interval; } /*! \brief Constructor Constructs a QTimeSpan of the same length as other from the given reference time. The reference date will be today's date. */ QTimeSpan::QTimeSpan(const QTime& reference, const QTimeSpan& other) : d(new QTimeSpanPrivate) { QDateTime todayReference(QDate::currentDate()); todayReference.setTime(reference); d->reference = todayReference; d->interval = other.d->interval; } /*! \brief Destructor */ QTimeSpan::~QTimeSpan() { } /*! \returns true if the time span is 0; that is, if no time is spanned by this instance. There may or may not be a valid reference date. \sa isNull hasValidReference */ bool QTimeSpan::isEmpty() const { return d->interval == 0; } /*! \returns true if the time span is 0; that is, if no time is spanned by this instance and there is no valid reference date. \sa isEmpty */ bool QTimeSpan::isNull() const { return isEmpty() && (!hasValidReference()); } /*! \brief Assignment operator */ QTimeSpan& QTimeSpan::operator=(const QTimeSpan& other) { if (&other == this) return *this; d = other.d; return *this; } /*! \returns a new QTimeSpan instance initialized to the indicated number of time units. The default reference date is invalid. \note that you can only construct a valid QTimeSpan using the Months or Years time units if you supply a valid reference date. \sa setFromTimeUnit */ QTimeSpan QTimeSpan::fromTimeUnit(Qt::TimeSpanUnit unit, qreal interval, const QDateTime& reference ) { switch (unit){ //note: fall through is intentional! case Qt::Weeks: interval *= 7.0; case Qt::Days: interval *= 24.0; case Qt::Hours: interval *= 60.0; case Qt::Minutes: interval *= 60.0; case Qt::Seconds: interval *= 1000.0; case Qt::Milliseconds: break; default: if (reference.isValid()) { QTimeSpan result(reference); result.setFromTimeUnit(unit, interval); return result; } Q_ASSERT_X(false, "static constructor", "Can not construct QTimeSpan from Month or Year TimeSpanUnit without a valid reference date."); return QTimeSpan(); } return QTimeSpan(reference, qint64(interval)); } /*! \returns the number of the requested units indicated by unit when formatted as format. \sa parts() */ int QTimeSpan::part(Qt::TimeSpanUnit unit, Qt::TimeSpanFormat format) const { if (!format.testFlag(unit)) return 0; if (!hasValidReference()) { if (unit == Qt::Months || unit == Qt::Years) { Q_ASSERT_X(false, "part", "Can not calculate Month or Year part without a reference date"); } if (format.testFlag(Qt::Months) || format.testFlag(Qt::Years)) { qWarning() << "Unsetting Qt::Months and Qt::Years flags from format. Not supported without a reference date"; //should this assert instead? format&= (Qt::AllUnits ^ (Qt::Months | Qt::Years)); } } //build up hash with pointers to ints for the units that are set in format, and 0's for those that are not. QTimeSpanPrivate::TimePartHash partsHash(format); bool result = partsHash.fill(*this); if (!result) { //what to do? Assert perhaps? qWarning() << "Result is invalid!"; return 0; } int val = *(partsHash.value(unit)); return val; } #define CHECK_INT_LIMIT(interval, unitFactor) if (interval >= (qint64(unitFactor) * qint64(std::numeric_limits::max()) ) ) {qWarning() << "out of range" << unitFactor; return false;} /*! Retreives a breakup of the length of the QTimeSpan in different time units. While part() allows you to retreive the value of a single unit for a specific representation of time, this method allows you to retreive all these values with a single call. The units that you want to use in the representation of the time span is defined implicitly by the pointers you pass. Passing a valid pointer for a time unit will include that unit in the representation, while passing 0 for that pointer will exclude it. The passed integer pointers will be set to the correct value so that together they represent the whole time span. This function will then return true. If it is impossible to represent the whole time span in the requested units, this function returns false. The fractionalSmallestUnit qreal pointer can optionally be passed in to retreive the value for the smallest time unit passed in as a fractional number. For instance, if your time span contains 4 minutes and 30 seconds, but the smallest time unit you pass in an integer pointer for is the minute unit, then the minute integer will be set to 4 and the fractionalSmallestUnit will be set to 4.5. A negative QTimeSpan will result in all the parts of the representation to be negative, while a positive QTimeSpan will result in an all positive representation. \note Months and years are only valid as units for time spans that have a valid reference date. Requesting the number of months or years for time spans without a valid reference date will return false. If this function returns false, the value of the passed in pointers is undefined. \sa part() */ bool QTimeSpan::parts(int *msecondsPtr, int *secondsPtr, int *minutesPtr, int *hoursPtr, int *daysPtr, int *weeksPtr, int *monthsPtr, int *yearsPtr, qreal *fractionalSmallestUnit) const { /* \todo We should probably cache the results of this operation. However, that requires keeping a dirty flag in the private data store, or a copy of the reference date, interval and last used parts. Is that worth it? */ // Has the user asked for a fractional component? If yes, find which unit it corresponds to. Qt::TimeSpanUnit smallestUnit = Qt::NoUnit; if (fractionalSmallestUnit) { if (yearsPtr) smallestUnit = Qt::Years; if (monthsPtr) smallestUnit = Qt::Months; if (weeksPtr) smallestUnit = Qt::Weeks; if (daysPtr) smallestUnit = Qt::Days; if (hoursPtr) smallestUnit = Qt::Hours; if (minutesPtr) smallestUnit = Qt::Minutes; if (secondsPtr) smallestUnit = Qt::Seconds; if (msecondsPtr) smallestUnit = Qt::Milliseconds; } QTimeSpan ts(*this); if (yearsPtr || monthsPtr) { //deal with months and years //we can not deal with months or years if there is no valid reference date if (!hasValidReference()) { qWarning() << "Can not request month or year parts of a QTimeSpan without a valid reference date."; return false; } int* _years = yearsPtr; if (!yearsPtr) _years = new int; *_years = 0; QDate startDate = ts.startDate().date(); QDate endDate = ts.endDate().date(); *_years = endDate.year() - startDate.year(); if (endDate.month() < startDate.month()) { (*_years)--; } else if (endDate.month() == startDate.month()) { if (endDate.day() < startDate.day()) { (*_years)--; } } /** \todo Handle fractional years component */ int* _months = monthsPtr; if (!monthsPtr) _months = new int; *_months = endDate.month() - startDate.month(); if (*_months < 0) (*_months) += 12; if (endDate.day() < startDate.day()) { (*_months)--; } int totalMonths = (*_months); if (!yearsPtr) totalMonths += (*_years) * 12; QDate newStartDate(startDate); newStartDate = newStartDate.addYears(*_years); newStartDate = newStartDate.addMonths(*_months); //qDebug() << "working with new start date" << newStartDate << "and end date" << endDate; ts = QDateTime(endDate, ts.endDate().time()) - QDateTime(newStartDate, ts.startDate().time()); *_months = totalMonths; /** \todo Handle fractional months component */ //clean up temporary variables on the heap if (!monthsPtr) delete _months; if (!yearsPtr) delete _years; } //from here on, we use ts as the time span! qint64 intervalLeft = ts.toMSecs(); qint64 unitFactor; //qDebug() << "intervalLeft" << intervalLeft; if (weeksPtr) { unitFactor = (7 * 24 * 60 * 60 * 1000); CHECK_INT_LIMIT(intervalLeft, unitFactor) *weeksPtr = intervalLeft / unitFactor; if (smallestUnit == Qt::Weeks) { QTimeSpan leftOverTime(referencedDate(), -intervalLeft); leftOverTime.normalize(); *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); } if (*weeksPtr != 0) { intervalLeft = intervalLeft % unitFactor; } } if (daysPtr) { unitFactor = (24 * 60 * 60 * 1000); CHECK_INT_LIMIT(intervalLeft, unitFactor) *daysPtr = intervalLeft / unitFactor; if (smallestUnit == Qt::Days) { QTimeSpan leftOverTime(referencedDate(), -intervalLeft); leftOverTime.normalize(); *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); } if (*daysPtr != 0 ) { intervalLeft = intervalLeft % unitFactor; } } if (hoursPtr) { unitFactor = (60 * 60 * 1000); CHECK_INT_LIMIT(intervalLeft, unitFactor) *hoursPtr = intervalLeft / unitFactor; if (smallestUnit == Qt::Hours) { QTimeSpan leftOverTime(referencedDate(), -intervalLeft); leftOverTime.normalize(); *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); } if (*hoursPtr != 0 ) { intervalLeft = intervalLeft % unitFactor; } } if (minutesPtr) { unitFactor = (60 * 1000); CHECK_INT_LIMIT(intervalLeft, unitFactor) *minutesPtr = intervalLeft / unitFactor; if (smallestUnit == Qt::Minutes) { QTimeSpan leftOverTime(referencedDate(), -intervalLeft); leftOverTime.normalize(); *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); } if (*minutesPtr != 0 ) { intervalLeft = intervalLeft % unitFactor; } } if (secondsPtr) { unitFactor = (1000); CHECK_INT_LIMIT(intervalLeft, unitFactor) *secondsPtr = intervalLeft / unitFactor; if (smallestUnit == Qt::Seconds) { QTimeSpan leftOverTime(referencedDate(), -intervalLeft); leftOverTime.normalize(); *fractionalSmallestUnit = leftOverTime.toTimeUnit(smallestUnit); } if (*secondsPtr > 0 ) { intervalLeft = intervalLeft % unitFactor; } } if (msecondsPtr) { unitFactor = 1; CHECK_INT_LIMIT(intervalLeft, unitFactor) *msecondsPtr = intervalLeft; if (smallestUnit == Qt::Weeks) { *fractionalSmallestUnit = qreal(intervalLeft); } } return true; } /*! Sets a part of the time span in the given format. setPart allows you to adapt the current time span interval unit-by-unit based on any time format. Where setFromTimeUnit resets the complete time interval, setPart only sets a specific part in a chosen format. \example If you have a time span representing 3 weeks, 2 days, 4 hours, 31 minutes and 12 seconds, you can change the number of hours to 2 by just calling span.setPart(Qt::Hours, 2, Qt::Weeks | Qt::Days | Qt::Hours | Qt::Minutes | Qt::Seconds); Note that just like with any other function, you can not use the Months and Years units without using a reference date. */ void QTimeSpan::setPart(Qt::TimeSpanUnit unit, int interval, Qt::TimeSpanFormat format) { if (!format.testFlag(unit)) { qWarning() << "Can not set a unit that is not part of the format. Ignoring."; return; } QTimeSpanPrivate::TimePartHash partsHash(format); bool result = partsHash.fill(*this); if (!result) { qWarning() << "Retreiving parts failed, cannot set parts. Ignoring."; return; } d->addUnit(this, unit, interval - *(partsHash.value(unit) ) ); } /*! Returns Qt::TimeSpanUnit representing the order of magnitude of the time span. That is, the largest unit that can be used to display the time span that will result in a non-zero value. If the QTimeSpan does not have a valid reference date, the largest possible time unit that will be returned is Qt::Weeks. Otherwise, the largest possible time unit is Qt::Years. \returns Unit representing the order of magnitude of the time span. */ Qt::TimeSpanUnit QTimeSpan::magnitude() { //abs(qint64) doesnt't compile properly with mingw32? qint64 mag = d->interval; if (mag < 0) mag = -mag; if (mag < 1000) return Qt::Milliseconds; if (mag < (1000LL * 60LL)) return Qt::Seconds; if (mag < (1000LL * 60LL * 60LL)) return Qt::Minutes; if (mag < (1000LL * 60LL * 60LL * 24LL)) return Qt::Hours; if (mag < (1000LL * 60LL * 60LL * 24LL * 7LL)) return Qt::Days; //those the simple cases. The rest is dependent on if there is a reference date if (hasValidReference()) { //simple test. If bigger than 366 (not 365!) then we are certain of dealing with years if (mag > (1000LL * 60LL * 60LL * 24LL * 366LL)) return Qt::Years; //we need a more complicated test int years = 0; int months = 0; parts(0, 0, 0, 0, 0, 0, &months, &years); if (years > 0) return Qt::Years; if (months > 0) return Qt::Months; } return Qt::Weeks; } /*! \returns true if there is a valid reference date set, false otherwise. */ bool QTimeSpan::hasValidReference() const { return d->reference.isValid(); } /*! \returns the reference date. Note that the reference date may be invalid. */ QDateTime QTimeSpan::referenceDate() const { return d->reference; } /*! Sets the reference date. If there currently is a reference date, the referenced date will not be affected. That means that the length of the time span will change. If there currently is no reference date set, the interval will not be affected and this function will have the same effect as moveReferenceDate. /sa moveReferenceDate setReferencedDate moveReferencedDate */ void QTimeSpan::setReferenceDate(const QDateTime &referenceDate) { if (d->reference.isValid() && referenceDate.isValid()) { *this = referencedDate() - referenceDate; } else { d->reference = referenceDate; } } /*! Moves the time span to align the time spans reference date with the new reference date. Note that the length of the time span will not be modified, so the referenced date will shift as well. If no reference date was set before, it is set now and the referenced date will become valid. /sa setReferenceDate setReferencedDate moveReferencedDate */ void QTimeSpan::moveReferenceDate(const QDateTime &referenceDate) { d->reference = referenceDate; } /*! Sets the referenced date. If there currently is a reference date, that reference date will not be affected. This implies that the length of the time span changes. If there currently is no reference date set, the interval will not be affected and this function will have the same effect as moveReferencedDate. /sa setReferenceDate moveReferenceDate moveReferencedDate */ void QTimeSpan::setReferencedDate(const QDateTime &referencedDate) { if (d->reference.isValid()) { *this = referencedDate - d->reference; } else { d->reference = referencedDate.addMSecs(-(d->interval)); } } /*! Moves the time span to align the time spans referenced date with the new referenced date. Note that the length of the time span will not be modified, so the reference date will shift as well. If no reference date was set before, it is set now. /sa setReferenceDate setReferencedDate moveReferencedDate */ void QTimeSpan::moveReferencedDate(const QDateTime &referencedDate) { d->reference = referencedDate.addMSecs(-(d->interval)); } /*! Returns the referenced date and time. The referenced QDateTime is the "other end" of the QTimeSpan from the reference date. An invalid QDateTime will be returned if no valid reference date has been set. */ QDateTime QTimeSpan::referencedDate() const { if (!(d->reference.isValid())) return QDateTime(); QDateTime dt(d->reference); dt = dt.addMSecs(d->interval); return dt; } // Comparison operators /*! Returns true if the two compared QTimeSpans have both the same reference date and the same length. Note that two QTimeSpan objects that span the same period, but where one is positive and the other is negative are not considdered equal. If you need to compare those, compare the normalized versions. \sa matchesLength */ bool QTimeSpan::operator==(const QTimeSpan &other) const { return ((d->interval == other.d->interval) && (d->reference == other.d->reference)); } /*! Returns true if the interval of this QTimeSpan is shorter than the interval of the other QTimeSpan. */ bool QTimeSpan::operator<(const QTimeSpan &other) const { return d->interval < other.d->interval; } /*! Returns true if the interval of this QTimeSpan is shorter or equal than the interval of the other QTimeSpan. */ bool QTimeSpan::operator<=(const QTimeSpan &other) const { return d->interval <= other.d->interval; } /*! Returns true if the interval of this QTimeSpan is equal to the interval of the other QTimeSpan. That is, if they have the same length. The default value of normalize is false. If normalize is true, the absolute values of the interval lengths are compared instead of the real values. \code QTimeSpan s1 = 2 * QTimeSpan::Day; QTimeSpan s2 = -2 * QTimeSpan::Day; qDebug() << s1.matchesLength(s2); //returns false qDebug() << s1.matchesLength(s2, true); //returns true \endcode */ bool QTimeSpan::matchesLength(const QTimeSpan &other, bool normalize) const { if (!normalize) { return d->interval == other.d->interval; } else { return qAbs(d->interval) == qAbs(other.d->interval); } } // Arithmatic operators /*! Adds the interval of the other QTimeSpan to the interval of this QTimeSpan. The reference date of the other QTimeSpan is ignored. */ QTimeSpan & QTimeSpan::operator+=(const QTimeSpan &other) { d->interval += other.d->interval; return *this; } /*! Adds the number of milliseconds to the interval of this QTimeSpan. The reference date of the QTimeSpan is not affected. */ QTimeSpan & QTimeSpan::operator+=(qint64 msecs) { d->interval += msecs; return *this; } /*! Substracts the interval of the other QTimeSpan from the interval of this QTimeSpan. The reference date of the other QTimeSpan is ignored while the reference date of this QTimeSpan is not affected. */ QTimeSpan & QTimeSpan::operator-=(const QTimeSpan &other) { d->interval -= (other.d->interval); return *this; } /*! Substracts the number of milliseconds from the interval of this QTimeSpan. The reference date of the QTimeSpan is not affected. */ QTimeSpan & QTimeSpan::operator-=(qint64 msecs) { d->interval -= msecs; return *this; } /*! Multiplies the interval described by this QTimeSpan by the given factor. The reference date of the QTimeSpan is not affected. */ QTimeSpan & QTimeSpan::operator*=(qreal factor) { d->interval *= factor; return *this; } /*! Multiplies the interval described by this QTimeSpan by the given factor. The reference date of the QTimeSpan is not affected. */ QTimeSpan & QTimeSpan::operator*=(int factor) { d->interval *= factor; return *this; } /*! Divides the interval described by this QTimeSpan by the given factor. The reference date of the QTimeSpan is not affected. */ QTimeSpan & QTimeSpan::operator/=(qreal factor) { d->interval /= factor; return *this; } /*! Divides the interval described by this QTimeSpan by the given factor. The reference date of the QTimeSpan is not affected. */ QTimeSpan & QTimeSpan::operator/=(int factor) { d->interval /= factor; return *this; } /*! \brief Modifies this QTimeSpan to be the union of two QTimeSpans. The union of two QTimeSpans is defined as the minimum QTimeSpan that encloses both QTimeSpans. The QTimeSpans need not be overlapping. \warning Only works if both QTimeSpans have a valid reference date. \sa operator&=(const QTimeSpan& other) \sa operator|(const QTimeSpan& other) \sa united(const QTimeSpan& other) */ QTimeSpan& QTimeSpan::operator|=(const QTimeSpan& other) // Union { Q_ASSERT_X((hasValidReference() && other.hasValidReference()), "assignment-or operator", "Both participating time spans need a valid reference date"); //do we need to check for self-assignment? QDateTime start = qMin(startDate(), other.startDate()); QDateTime end = qMax(endDate(), other.endDate()); *this = end - start; return *this; } /*! \brief Modifies this QTimeSpan to be the intersection of two QTimeSpans. The intersection of two QTimeSpans is defined as the maximum QTimeSpan that both QTimeSpans have in common. If the QTimeSpans don't overlap, a null QTimeSpan will be returned. The returned QTimeSpan will be positive. \warning Only works if both QTimeSpans have a valid reference date. \sa operator&=(const QTimeSpan& other) \sa operator&(const QTimeSpan& other) \sa overlapped(const QTimeSpan& other) */ QTimeSpan& QTimeSpan::operator&=(const QTimeSpan &other) // Intersection { Q_ASSERT_X((hasValidReference() && other.hasValidReference()), "assignment-or operator", "Both participating time spans need a valid reference date"); //do we need to check for self-assignment? const QTimeSpan* first = this; const QTimeSpan* last = &other; if (other.startDate() < startDate()) { first = &other; last = this; } //check if there is overlap at all. If not, reset the interval to 0 if (!(first->endDate() > last->startDate()) ) { d->interval = 0; return *this; } *this = qMin(first->endDate(), last->endDate()) - last->startDate(); return *this; } /*! Returns true if this QTimeSpan overlaps with the other QTimeSpan. If one of the QTimeSpans does not have a valid reference date, this function returns false. */ bool QTimeSpan::overlaps(const QTimeSpan &other) const { if (!hasValidReference() || !other.hasValidReference()) return false; const QTimeSpan* first = this; const QTimeSpan* last = &other; if (other.startDate() < startDate()) { first = &other; last = this; } return (first->endDate() > last->startDate()); } /*! \brief Returns a new QTimeSpan that represents the intersection of the two QTimeSpans. The intersection of two QTimeSpans is defined as the maximum QTimeSpan that both QTimeSpans have in common. If the QTimeSpans don't overlap, a null QTimeSpan will be returned. Any valid returned QTimeSpan will be positive. \warning Only works if both QTimeSpans have a valid reference date. Will assert otherwise. \sa operator&=(const QTimeSpan& other) \sa operator&(const QTimeSpan& other) */ QTimeSpan QTimeSpan::overlapped(const QTimeSpan &other) const { Q_ASSERT_X((hasValidReference() && other.hasValidReference()), "assignment-or operator", "Both participating time spans need a valid reference date"); const QTimeSpan* first = this; const QTimeSpan* last = &other; if (other.startDate() < startDate()) { first = &other; last = this; } //check if there is overlap at all. If not, reset the interval to 0 if (!(first->endDate() >= last->startDate()) ) { return QTimeSpan(); } return qMin(first->endDate(), last->endDate()) - last->startDate(); } /*! \brief Returns a new QTimeSpan that represents the union of two QTimeSpans. The union of two QTimeSpans is defined as the minimum QTimeSpan that encloses both QTimeSpans. The QTimeSpans need not be overlapping. \warning Only works if both QTimeSpans have a valid reference date. Will assert otherwise. \sa operator|=(const QTimeSpan& other) \sa operator|(const QTimeSpan& other) */ QTimeSpan QTimeSpan::united(const QTimeSpan &other) const { Q_ASSERT_X((hasValidReference() && other.hasValidReference()), "assignment-or operator", "Both participating time spans need a valid reference date"); QDateTime start = qMin(startDate(), other.startDate()); QDateTime end = qMax(endDate(), other.endDate()); return ( end - start ); } /*! Determines if the given dateTime lies within the time span. The begin and end times are taken to be contained in the time span in this function. If the time span does not have a valid reference date, this function returns false. */ bool QTimeSpan::contains(const QDateTime &dateTime) const { if (!hasValidReference()) return false; return ((startDate() <= dateTime) && (endDate()) >= dateTime); } /*! Determines if the given date lies within the time span. The begin and end times are taken to be contained in the time span in this function. Just like in the QTimeSpan constructors that take a QDate, the time will assumed to be 00:00:00.000. If the time span does not have a valid reference date, this function returns false. */ bool QTimeSpan::contains(const QDate &date) const { QDateTime dt(date); return contains(dt); } /*! Determines if the given time lies within the time span. The begin and end times are taken to be contained in the time span in this function. Just like in the QTimeSpan constructors that take a QTime, the date will be set to today. If the time span does not have a valid reference date, this function returns false. */ bool QTimeSpan::contains(const QTime &time) const { QDateTime dt(QDate::currentDate()); dt.setTime(time); return contains(dt); } /*! Determines if the other QTimeSpan lies within this time span. Another time span is contained if its start time is the same or later then the start time of this time span, and its end time is the same or earlier than the end time of this time span. If either time span does not have a valid reference date, this function returns false. */ bool QTimeSpan::contains(const QTimeSpan &other) const { if (!(hasValidReference() && other.hasValidReference())) return false; return ((startDate() <= other.startDate()) && (endDate()) >= other.endDate()); } /*! Returns a new QTimeSpan representing the same time span as this QTimeSpan, but that is guaranteed to be positive; that is, to have the reference date before or equal to the referenced date. \sa normalize abs */ QTimeSpan QTimeSpan::normalized() const { QTimeSpan ts(*this); ts.normalize(); return ts; } /*! Modifies this QTimeSpan to represent the same time span, but that is guaranteed to be positive; that is, to have the reference date before or equal to the referenced date. If there is no valid reference date, the interval will just be made positive. \sa normalized abs */ void QTimeSpan::normalize() { if (d->interval < 0) { if (hasValidReference()) { d->reference = referencedDate(); } d->interval = qAbs(d->interval); } } /*! Returns a copy of this QTimeSpan that is guaranteed to be positive; that is, to have the reference date before or equal to the referenced date. The reference date is not modified. That implies that if there is a reference date, and the QTimeSpan is negative, the returned QTimeSpan will describe the time span \i after the reference date instead of the time span \i before. If there is no valid reference date, the interval will just be made positive. \sa normalize normalized */ QTimeSpan QTimeSpan::abs() const { QTimeSpan result(*this); result.d->interval = qAbs(result.d->interval); return result; } /*! Returns true if the interval is negative. \sa isNormal */ bool QTimeSpan::isNegative() const { return d->interval < 0; } /*! \fn QTimeSpan::isNormal() const Returns true if the interval is normal, that is: not negative. \sa isNegative */ /*! Returns the first date of the spanned time period. If there is no valid reference date, an invalid QDateTime will be returned. */ QDateTime QTimeSpan::startDate() const { if (isNegative()) return referencedDate(); return referenceDate(); } /*! Returns the last date of the spanned time period. If there is no valid reference date, an invalid QDateTime will be returned. */ QDateTime QTimeSpan::endDate() const { if (isNegative()) return referenceDate(); return referencedDate(); } /*! Returns the duration of the QTimeSpan expressed in milliseconds. This value may be negative. */ qint64 QTimeSpan::toMSecs() const { return d->interval; } /*! Returns the duration of the QTimeSpan expressed in the given TimeSpanUnit. This value may be negative. */ qreal QTimeSpan::toTimeUnit(Qt::TimeSpanUnit unit) const { qreal interval = qreal(d->interval); switch (unit){ //fall through is intentional case Qt::Weeks: interval /= 7.0; case Qt::Days: interval /= 24.0; case Qt::Hours: interval /= 60.0; case Qt::Minutes: interval /= 60.0; case Qt::Seconds: interval /= 1000.0; case Qt::Milliseconds: break; default: Q_ASSERT_X(hasValidReference(), "toTimeUnit", "Can not convert to time units that depend on the reference date (month and year)."); qreal result(0.0); int intResult(0); bool succes(false); if (unit == Qt::Months) { succes = parts(0, 0, 0, 0, 0, 0, &intResult, 0, &result); } else if (unit == Qt::Years) { succes = parts(0, 0, 0, 0, 0, 0, 0, &intResult, &result); } if (!succes) return 0.0; return result; } return interval; } /*! Sets the length of this QTimeSpan from the given number of milliseconds. The reference date is not affected. */ void QTimeSpan::setFromMSecs(qint64 msecs) { d->interval = msecs; } /*! Sets the length of this QTimeSpan from the given number of TimeSpanUnits. The reference date is not affected. */ void QTimeSpan::setFromTimeUnit(Qt::TimeSpanUnit unit, qreal interval) { switch (unit){ case Qt::Weeks: //fall through of cases is intentional! interval *= 7.0; case Qt::Days: interval *= 24.0; case Qt::Hours: interval *= 60.0; case Qt::Minutes: interval *= 60.0; case Qt::Seconds: interval *= 1000.0; case Qt::Milliseconds: break; case Qt::Months: setFromMonths(interval); return; case Qt::Years: setFromYears(interval); return; default: Q_ASSERT_X(false, "setFromTimeUnit", "Can not set a QTimeSpan duration from unknown TimeSpanUnit."); } d->interval = qint64(interval); } /*! Sets the interval of the time span as a number of months. \warning This function can only be used if a valid reference date has been set! The setFromMonths method deals with fractional months in the following way: first, the whole number of months is extracted and added to the reference date. The fractional part of the number of months is then multiplied with the number of days in the month in which that date falls. If the number of months is negative and adding a whole month yields a date exactly on a month boundary, the number of days in the month before is used instead. That number is used as the number of days and is added to the interval. \code QTimeSpan ts(QDate(2010,01,01)); ts.setFromMonths(1.5); // ts's referenced date is now februari 14, 0:00:00 ts.setFromMonths(2.5); // ts's referenced date is now march 15, 0:00:00 QTimeSpan ts2(QDate(2008,01,01)); //2008 is a leap year! ts2.setFromMonths(1.5); // ts2's referenced date is now februari 14, 12:00:00 QTimeSpan ts3(QDate(2008,03,01)); //2008 is a leap year ts3.setFromMonths(-0.5); // ts3's referenced date is now februari 14: 12:00:00 // \endcode */ void QTimeSpan::setFromMonths(qreal months) { Q_ASSERT_X(hasValidReference(), "setFromMonths", "Can not set interval from time unit month if there is no reference date."); int fullMonths = (int)months; qreal fractionalMonth = months - fullMonths; QDateTime endDate = d->reference; endDate = endDate.addMonths(fullMonths); int days = d->s_daysPerMonth[endDate.date().month()-1]; QDateTime measureDate(endDate); if (fractionalMonth < 0) { measureDate = measureDate.addMSecs(-1); } if (QDate::isLeapYear(measureDate.date().year()) && measureDate.date().month() == 2) { ++days; //februari has an extra day this year... } QTimeSpan tmp = endDate - d->reference; qreal fractionalDays = fractionalMonth * days; d->interval = tmp.toMSecs() + qint64(fractionalDays * 24.0 * 60.0 * 60.0 * 1000.0); } /*! Sets the interval of the time span as a number of years. \warning This function can only be used if a valid reference date has been set! The setFromYears method deals with fractional years in the following way: first, the whole number of years is extracted and added to the reference date. The fractional part of the number of years is then multiplied with the number of days in the year in which that date falls. That number is used as the number of days and is added to the interval. If the number of years is negative and adding the whole years yields a date exactly on a year boundary, the number of days in the year before is used instead. */ void QTimeSpan::setFromYears(qreal years) { Q_ASSERT_X(hasValidReference(), "setFromYears", "Can not set interval from time unit year if there is no reference date."); int fullYears = (int)years; qreal fractionalYear = years - fullYears; QDateTime endDate = d->reference; endDate = endDate.addYears(fullYears); qreal days = 365.0; QDateTime measureDate(endDate); if (fractionalYear < 0) { measureDate = measureDate.addMSecs(-1); } if (QDate::isLeapYear(measureDate.date().year())) { days += 1.0; //februari has an extra day this year... } QTimeSpan tmp = endDate - d->reference; qreal fractionalDays = fractionalYear * days; d->interval = tmp.toMSecs() + qint64(fractionalDays * 24.0 * 60.0 * 60.0 * 1000.0); } #ifndef QT_NO_DATASTREAM /*! Streaming operator. This operator allows you to stream a QTimeSpan into a QDataStream. /sa operator>>(QDataStream &stream, QTimeSpan &span) */ QDataStream & operator<<(QDataStream &stream, const QTimeSpan & span) { stream << span.d->reference << span.d->interval; return stream; } /*! Streaming operator. This operator allows you to stream a QTimeSpan out of a QDataStream. /sa operator>>(QDataStream &stream, QTimeSpan &span) */ QDataStream & operator>>(QDataStream &stream, QTimeSpan &span) { stream >> span.d->reference >> span.d->interval; return stream; } #endif /*! Adds another QTimeSpan to this QTimeSpan. The values of the intervals of the QTimeSpans are added up with normal arithmatic. Negative values will work as expected. If the left argument has a reference date, that reference will be kept. If only the right argument has a reference date, then that reference date will be used as the new reference date. The above can have suprising consequences: \code // s1 and s2 are two QTimeSpan objects QTimeSpan s12 = s1 + s2; QTimeSpan s21 = s2 + s1; if (s12 == s21) { //may or may not happen, depending on the reference dates of s1 and s2. } \endcode */ QTimeSpan operator+(const QTimeSpan &left, const QTimeSpan &right) { QTimeSpan result(left); result += right; // only keep the right reference date if the left argument does not have one if (!left.hasValidReference() && right.hasValidReference()) { result.setReferenceDate(right.referenceDate()); } return result; } /*! Substracts another QTimeSpan from this QTimeSpan. The value of the interval of the right QTimeSpan is substracted from the left QTimeSpan with normal arithmatic. Negative values will work as expected. If the left argument has a reference date, that reference will be kept. If only the right argument has a reference date, then that reference date will be used as the new reference date. \sa operator+ */ QTimeSpan operator-(const QTimeSpan &left, const QTimeSpan &right) { QTimeSpan result(left); result -= right; // only keep the right reference date if the left argument does not have one if (!left.hasValidReference() && right.hasValidReference()) { result.setReferenceDate(right.referenceDate()); } return result; } /*! Multiply a QTimeSpan by a scalar factor. \returns a new QTimeSpan object that has the same reference date as the left QTimeSpan, but with an interval length that is multiplied by the right argument. */ QTimeSpan operator*(const QTimeSpan &left, qreal right) { QTimeSpan result(left); result*=right; return result; } /*! Multiply a QTimeSpan by a scalar factor. \returns a new QTimeSpan object that has the same reference date as the left QTimeSpan, but with an interval length that is multiplied by the right argument. */ QTimeSpan operator*(const QTimeSpan &left, int right) { QTimeSpan result(left); result*=right; return result; } /*! Devide a QTimeSpan by a scalar factor. \returns a new QTimeSpan object that has the same reference date as the left QTimeSpan, but with an interval length that is devided by the right argument. */ QTimeSpan operator/(const QTimeSpan &left, qreal right) { QTimeSpan result(left); result/=right; return result; } /*! Devide a QTimeSpan by a scalar factor. \returns a new QTimeSpan object that has the same reference date as the left QTimeSpan, but with an interval length that is devided by the right argument. */ QTimeSpan operator/(const QTimeSpan &left, int right) { QTimeSpan result(left); result/=right; return result; } /*! Devides two QTimeSpans. The devision works on the interval lengths of the two QTimeSpan objects as you would expect from normal artithmatic. */ qreal operator/(const QTimeSpan &left, const QTimeSpan &right) { return (qreal(left.toMSecs()) / qreal(right.toMSecs())); } /*! Returns a QTimeSpan object with the same reference date as the right hand argument, but with a negated interval. Note that unlike with the normalize() method, this function will result in a QTimeSpan that describes a different period if the QTimeSpan has a reference date because the reference date is not modified. */ QTimeSpan operator-(const QTimeSpan &right) // Unary negation { QTimeSpan result(right); result.setFromMSecs(-result.toMSecs()); return result; } /*! \returns the union of the two QTimeSpans. \sa united */ QTimeSpan operator|(const QTimeSpan &left, const QTimeSpan &right) // Union { QTimeSpan result(left); result|=right; return result; } /*! \returns the intersection of the two QTimeSpans. \sa intersected */ QTimeSpan operator&(const QTimeSpan &left, const QTimeSpan &right) // Intersection { QTimeSpan result(left); result&=right; return result; } // Operators that use QTimeSpan and other date/time classes /*! Creates a new QTimeSpan object that describes the period between the two QDateTime objects. The right hand object will be used as the reference date, so that substracting a date in the past from a date representing now will yield a positive QTimeSpan. \note that while substracting two dates will result in a QTimeSpan describing the time between those dates, there is no pendant operation for adding two dates. Substractions involving an invalid QDateTime, will result in a time span with an interval length 0. If the right-hand QDateTime is valid, it will still be used as the reference date. */ QTimeSpan operator-(const QDateTime &left, const QDateTime &right) { QTimeSpan result(right); #if QT_VERSION >= 0x040700 if (left.isValid() && right.isValid()) { result = QTimeSpan(right, right.msecsTo(left)); } #else if (left.isValid() && right.isValid()) { QDateTime tmpLeft = left.toUTC(); QDateTime tmpRight = right.toUTC(); //don't use QDateTime::secsTo, you end up in rounding issues as well as overflow problems! qint64 interval = 1000LL * 60LL * 60LL * 24LL * qint64(tmpRight.date().daysTo(tmpLeft.date())); interval += tmpRight.time().msecsTo(tmpLeft.time()); result.setFromMSecs(interval); } #endif return result; } /*! Creates a new QTimeSpan object that describes the period between the two QDate objects. The right hand object will be used as the reference date, so that substracting a date in the past from a date representing now will yield a positive QTimeSpan. \note that while substracting two dates will result in a QTimeSpan describing the time between those dates, there is no pendant operation for adding two dates. */ QTimeSpan operator-(const QDate &left, const QDate &right) { QTimeSpan result = QDateTime(left) - QDateTime(right); return result; } /*! Creates a new QTimeSpan object that describes the period between the two QTime objects. The right hand time will be used as the reference time, so that substracting a time in the past from a time representing now will yield a positive QTimeSpan. \note that that both times will be assumed to be on the current date. \note that while substracting two times will result in a QTimeSpan describing the time between those, there is no pendant operation for adding two times. */ QTimeSpan operator-(const QTime &left, const QTime &right) { return QDateTime(QDate::currentDate(), left) - QDateTime(QDate::currentDate(), right); } /*! \returns the date described by the left-hand date, shifted by the interval described in the QTimeSpan. The reference date of the QTimeSpan, if set, is ignored. No rounding takes place. If a QTimeSpan describes 1 day, 23 hours and 59 minutes, adding that QTimeSpan to a QDate respresenting April 1 will still yield April 2. */ QDate operator+(const QDate &left, const QTimeSpan &right) { QDateTime dt(left); return (dt + right).date(); } /*! \returns the date and time described by the left-hand QDateTime, shifted by the interval described in the QTimeSpan. The reference date of the QTimeSpan, if set, is ignored. */ QDateTime operator+(const QDateTime &left, const QTimeSpan &right) { QDateTime result(left); result = result.addMSecs(right.toMSecs()); return result; } /*! \returns the time described by the left-hand QTime, shifted by the interval described in the QTimeSpan. The reference date of the QTimeSpan, if set, is ignored. \note that since QTimeSpan works with dates and times, the time returned will never be bigger than 23:59:59.999. The time will wrap to the next date. Use QDateTime objects if you need to keep track of that. */ QTime operator+(const QTime &left, const QTimeSpan &right) { QDateTime dt(QDate::currentDate(), left); dt = dt.addMSecs(right.toMSecs()); return dt.time(); } /*! \returns the date described by the left-hand date, shifted by the negated interval described in the QTimeSpan. The reference date of the QTimeSpan, if set, is ignored. No rounding takes place. If a QTimeSpan describes 1 day, 23 hours and 59 minutes, adding that QTimeSpan to a QDate respresenting April 1 will still yield April 2. */ QDate operator-(const QDate &left, const QTimeSpan &right) { QDateTime dt(left); return (dt - right).date(); } /*! \returns the date and time described by the left-hand QDateTime, shifted by the negated interval described in the QTimeSpan. The reference date of the QTimeSpan, if set, is ignored. */ QDateTime operator-(const QDateTime &left, const QTimeSpan &right) { QDateTime result(left); result = result.addMSecs( -(right.toMSecs()) ); return result; } /*! \returns the time described by the left-hand QTime, shifted by the negated interval described in the QTimeSpan. The reference date of the QTimeSpan, if set, is ignored. \note that since QTimeSpan works with dates and times, the time returned will never be bigger than 23:59:59.999. The time will wrap to the next date. Use QDateTimes if you need to keep track of that. */ QTime operator-(const QTime &left, const QTimeSpan &right) { QDateTime dt(QDate::currentDate(), left); dt = dt.addMSecs( -(right.toMSecs()) ); return dt.time(); } #if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DATESTRING) /*! Operator to stream QTimeSpan objects to a debug stream. */ QDebug operator<<(QDebug debug, const QTimeSpan &ts) { debug << "QTimeSpan(Reference Date =" << ts.referenceDate() << "msecs =" << ts.toMSecs() << ")"; return debug; } #endif //String conversions #ifndef QT_NO_DATESTRING /*! \returns an approximate representation of the time span length When representing the lenght of a time span, it is often not nessecairy to be completely accurate. For instance, when dispaying the age of a person, it is often enough to just state the number of years, or possibly the number of years and the number of months. Similary, when displaying how long a certain operation the user of your application started will run, it is useless to display the number of seconds left if the operation will run for hours more. toApproximateString() provides functionality to display the length of the QTimeSpan in such an approximate way. It will format the time using one or two neighbouring time units. The first time unit that will be used is the unit that represents the biggest portion of time in the span. The second time unit will be the time unit directly under that. The second unit will only be used if it is not 0, and if the first number is smaller than the indicated suppresSecondUnitLimit. The suppressSecondUnitLimit argument can be used to suppres, for instance, the number of seconds when the operation will run for more than five minutes more. The idea is that for an approximate representation of the time length, it is no longer relevant to display the second unit if the first respresents a time span that is perhaps an order of magnitude larger already. If you set suppressSecondUnitLimit to a negative number, the second unit will always be displayed, unless no valid unit for it could be found. */ QString QTimeSpan::toApproximateString(int suppresSecondUnitLimit, Qt::TimeSpanFormat format) { if (format==Qt::NoUnit) return QString(); //retreive the time unit to use as the primairy unit int primairy = -1; int secondairy = -1; Qt::TimeSpanUnit primairyUnit = magnitude(); while (!format.testFlag(primairyUnit ) && primairyUnit > Qt::NoUnit) { primairyUnit = Qt::TimeSpanUnit(primairyUnit / 2); } Qt::TimeSpanUnit secondairyUnit = Qt::NoUnit; if (primairyUnit > 1) { secondairyUnit = Qt::TimeSpanUnit(primairyUnit / 2); } else { primairy = 0; } while (!format.testFlag(secondairyUnit) && secondairyUnit > Qt::NoUnit) { secondairyUnit = Qt::TimeSpanUnit(secondairyUnit / 2); } //build up hash with pointers to ints for the units that are set in format, and 0's for those that are not. if (primairy < 0) { QTimeSpanPrivate::TimePartHash partsHash(format); bool result = partsHash.fill(*this); if (!result) { qDebug() << "false result from parts function"; return QString(); } primairy = *(partsHash.value(primairyUnit)); if (secondairyUnit > 0) { secondairy = *(partsHash.value(secondairyUnit)); } else { secondairy = 0; } } if ((primairy > 0 && secondairy > 0 && primairy < suppresSecondUnitLimit) || (suppresSecondUnitLimit < 0 && secondairyUnit > Qt::NoUnit) ) { //we will display with two units return d->unitString(primairyUnit, primairy) + QLatin1String(", ") + d->unitString(secondairyUnit, secondairy); } //we will display with only the primairy unit return d->unitString(primairyUnit, primairy); } /*! \returns a string representation of the duration of this time span in the requested format This function returns a representation of only the length of this time span. If you need the reference or referenced dates, access those using one of the provided methods and output them directly. The format parameter determines the format of the result string. The duration will be expressed in the units you use in the format. y The number of years M The number of months w The number of weeks d The number of days h The number of hours m The number of minutes s The number of seconds z The number of milliseconds Use multiple letters to force leading zeros. \note You can not use years or months if the time span does not have a valid reference date. Characters in the string that don't represent a time unit, are used as literal strings in the output. Everything between single quotes will always be used as a literal string. This makes it possible to use the characters used for the time span format also as literal output. To use a single quote in the output, put two consecutive single quotes within a single quote literal string block. To just put a single quote in a the output, you need four consequtive single quotes. */ QString QTimeSpan::toString(const QString &format) const { Qt::TimeSpanFormat tsFormat = Qt::NoUnit; QList tokenList = d->parseFormatString(format, tsFormat); QTimeSpanPrivate::TimePartHash partsHash(tsFormat); bool result = partsHash.fill(*this); if (!result) return QString(); QString formattedString; foreach(QTimeSpanPrivate::TimeFormatToken token, tokenList) { if (token.type == 0) { formattedString.append(token.string); } else { Qt::TimeSpanUnit unit(token.type); formattedString.append (QString("%1").arg(*partsHash.value( unit ), token.length, 10, QChar('0') ) ); } } return formattedString; } /*! Returns a time span represented by the string using the format given, or an empty time span if the string cannot be parsed. The optional reference argument will be used as the reference date for the string. \note You can only use months or years if you also pass a valid reference. */ QTimeSpan QTimeSpan::fromString(const QString &string, const QString &format, const QDateTime &reference) { /* There are two possible ways of parsing a string. On the one hand, you could use the lengths of string literals to determine the positions in the string where you expect the different parts of the string. On the other hand, you could use the actual contents of the literals as delimiters to figure out what parts of the string refer to what unit of time. In that case, the length of the time units would only matter if they are not surrounded by a string literal. Both seem useful. Perhaps we need two different modes for this? The code here implements the first option. The overloaded version below implements a more flexible regexp based approach. */ //stage one: parse the format string QTimeSpan span(reference); Qt::TimeSpanFormat tsFormat = Qt::NoUnit; QList tokenList = span.d->parseFormatString(format, tsFormat); //prepare the temporaries QTimeSpanPrivate::TimePartHash partsHash(tsFormat); QString input(string); //extract the values from the input string into our temporary structure foreach(const QTimeSpanPrivate::TimeFormatToken token, tokenList) { if (token.type == Qt::NoUnit) { input = input.remove(0, token.length); } else { QString part = input.left(token.length); input = input.remove(0, token.length); bool success(false); part = part.trimmed(); int value = part.toInt(&success, 10); if (!success) { return QTimeSpan(); } *(partsHash.value(token.type)) = value; } } //construct the time span from the temporary data //we must set the number of years and months first; for the rest order is not important if (partsHash.value(Qt::Years)) { span.d->addUnit(&span, Qt::Years, *(partsHash.value(Qt::Years))); delete partsHash.value(Qt::Years); partsHash.insert(Qt::Years, 0); } if (partsHash.value(Qt::Months)) { span.d->addUnit(&span, Qt::Months, *(partsHash.value(Qt::Months))); delete partsHash.value(Qt::Months); partsHash.insert(Qt::Months, 0); } //add the rest of the units QHashIterator it(partsHash); while (it.hasNext()) { it.next(); if (it.value()) { span.d->addUnit(&span, it.key(), *(it.value())); qDebug() << "Added unit" << it.key() << "with value" << *(it.value()) << "new value" << span.d->interval; } } return span; } /*! Returns a time span represented by the string using the patern given, or an empty time span if the string cannot be parsed. Each pair of capturing parenthesis can extract a time unit. The order in which the units appear is given by the list of arguments unit1 to unit8. Captures for which the corresponding type is set to Qt::NoUnit will be ignored. The reference argument will be used as the reference date for the string. \note You can only use months or years if you also pass a valid reference. */ QTimeSpan QTimeSpan::fromString(const QString &string, const QRegExp &pattern, const QDateTime &reference, Qt::TimeSpanUnit unit1, Qt::TimeSpanUnit unit2, Qt::TimeSpanUnit unit3, Qt::TimeSpanUnit unit4, Qt::TimeSpanUnit unit5, Qt::TimeSpanUnit unit6, Qt::TimeSpanUnit unit7, Qt::TimeSpanUnit unit8) { if (pattern.indexIn(string) < 0) return QTimeSpan(); QTimeSpanPrivate::TimePartHash partsHash(Qt::NoUnit); QList unitList; unitList << unit1 << unit2 << unit3 << unit4 << unit5 << unit6 << unit7 << unit8; for (int i(0); i < qMin(pattern.captureCount(), 8 ); ++i) { if (unitList.at(i) > Qt::NoUnit) { partsHash.addUnit(unitList.at(i)); QString capture = pattern.cap(i + 1); bool ok(false); int value = capture.toInt(&ok, 10); if (!ok) return QTimeSpan(); *(partsHash.value(unitList.at(i))) = value; } } //create the time span to return QTimeSpan span(reference); //construct the time span from the temporary data //we must set the number of years and months first; for the rest order is not important if (partsHash.value(Qt::Years)) { span.d->addUnit(&span, Qt::Years, *(partsHash.value(Qt::Years))); delete partsHash.value(Qt::Years); partsHash.insert(Qt::Years, 0); } if (partsHash.value(Qt::Months)) { span.d->addUnit(&span, Qt::Months, *(partsHash.value(Qt::Months))); delete partsHash.value(Qt::Months); partsHash.insert(Qt::Months, 0); } //add the rest of the units QHashIterator it(partsHash); while (it.hasNext()) { it.next(); if (it.value()) { span.d->addUnit(&span, it.key(), *(it.value())); } } return span; } #endif QT_END_NAMESPACE