overte-HifiExperiments/libraries/shared/src/qtimespan.cpp
2014-03-26 16:58:47 -07:00

2290 lines
74 KiB
C++

/****************************************************************************
**
** 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 <limits>
#include <math.h>
#include <QRegExp>
#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 <qt_windows.h>
#endif
#ifndef Q_WS_WIN
#include <locale.h>
#endif
#include <time.h>
#if defined(Q_OS_WINCE)
#include "qfunctions_wince.h"
#endif
#if defined(Q_WS_MAC)
#include <private/qcore_mac_p.h>
#endif
#if defined(Q_OS_SYMBIAN)
#include <e32std.h>
#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<Qt::TimeSpanUnit, int*>
{
public:
TimePartHash(Qt::TimeSpanFormat format)
{
for (int i(Qt::Milliseconds); i <= Qt::Years; i *= 2) {
Qt::TimeSpanUnit u = static_cast<Qt::TimeSpanUnit>(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<TimeFormatToken> parseFormatString(const QString& formatString, Qt::TimeSpanFormat &format) const
{
QHash<QChar, Qt::TimeSpanUnit> 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<TimeFormatToken> 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)<length) {
if (formatString[pos+1] == QLatin1Char('\'')) {
++pos;
TimeFormatToken token = tokenList.last();
token.string.append(QChar('\''));
token.length = token.string.length();
tokenList[tokenList.length()-1] = token;
inLiteral = true; //we *are* staying in literal string mode
}
}
} else {
TimeFormatToken token = tokenList.last();
token.string.append(currentChar);
token.length = token.string.length();
tokenList[tokenList.length()-1] = token;
}
} else { //not in literal string
if (currentChar == QLatin1Char('\'')) {
inLiteral = true; //enter literal string mode
TimeFormatToken token;
token.type = Qt::NoUnit;
token.length = 0;
tokenList << token;
} else {
if (tokenHash.contains(currentChar)) {
Qt::TimeSpanUnit unit = tokenHash.value(currentChar);
TimeFormatToken token;
token.length = 0;
token.type = unit;
if (!tokenList.isEmpty()) {
if (tokenList.last().type == unit) {
token = tokenList.takeLast();
}
}
token.length+=1;
tokenList.append(token);
format |= unit;
} else {
//ignore character?
TimeFormatToken token;
token.length =0;
token.type = Qt::NoUnit;
if (!tokenList.isEmpty()) {
if (tokenList.last().type == Qt::NoUnit) {
token = tokenList.takeLast();
}
}
token.string.append(currentChar);
token.length= token.string.length();
tokenList.append(token);
}
}
}
++pos;
}
return tokenList;
}
#endif
};
const int QTimeSpanPrivate::s_daysPerMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
/*!
\brief Default constructor
Constructs a null QTimeSpan
*/
QTimeSpan::QTimeSpan()
: d(new QTimeSpanPrivate)
{
d->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<int>::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<QTimeSpanPrivate::TimeFormatToken> 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<QTimeSpanPrivate::TimeFormatToken> 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<Qt::TimeSpanUnit, int*> 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<Qt::TimeSpanUnit> 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<Qt::TimeSpanUnit, int*> it(partsHash);
while (it.hasNext()) {
it.next();
if (it.value()) {
span.d->addUnit(&span, it.key(), *(it.value()));
}
}
return span;
}
#endif
QT_END_NAMESPACE