New class QgsInterval for storing durations between datetimes

Move the QgsExpression::Interval class out to its own QgsInterval
class, extend with new methods and add tests

Add a typedef to keep API compatibility for 2.16
This commit is contained in:
Nyall Dawson 2016-05-10 12:27:54 +10:00
parent 3340d8ea70
commit ceba5264f7
12 changed files with 676 additions and 160 deletions

View File

@ -57,6 +57,7 @@
%Include qgsgml.sip
%Include qgsgmlschema.sip
%Include qgshistogram.sip
%Include qgsinterval.sip
%Include qgsjsonutils.sip
%Include qgsmaptopixelgeometrysimplifier.sip
%Include qgstransaction.sip

View File

@ -671,33 +671,33 @@ class QgsExpression
virtual QString dump() const;
};
//TODO QGIS 3.0 - remove
//! @deprecated use QgsInterval instead
class Interval
{
{
public:
Interval( int seconds = 0 );
//! interval length in years
double years();
double years() const;
//! interval length in months
double months();
double months() const;
//! interval length in weeks
double weeks();
double weeks() const;
//! interval length in days
double days();
double days() const;
//! interval length in hours
double hours();
double hours() const;
//! interval length in minutus
double minutes();
double minutes() const;
//! interval length in seconds
double seconds();
double seconds() const;
//! getter interval validity
bool isValid();
bool isValid() const;
//! setter interval validity
void setValid( bool valid );
//! compare two intervals
bool operator==( QgsExpression::Interval other ) const;
//! return an invalid interval
static QgsExpression::Interval invalidInterVal();
//! convert a string to an interval
static QgsExpression::Interval fromString( const QString& string );
};
@ -749,7 +749,12 @@ class QgsExpression
bool compare( double diff );
int computeInt( int x, int y );
double computeDouble( double x, double y );
QDateTime computeDateTimeFromInterval( const QDateTime& d, QgsExpression::Interval *i );
/** Computes the result date time calculation from a start datetime and an interval
* @param d start datetime
* @param i interval to add or subtract (depending on mOp)
*/
QDateTime computeDateTimeFromInterval( const QDateTime& d, QgsInterval *i );
};
class NodeInOperator : QgsExpression::Node

136
python/core/qgsinterval.sip Normal file
View File

@ -0,0 +1,136 @@
/** \ingroup core
* \class QgsInterval
* \brief A representation of the interval between two datetime values.
* \note Added in version 2.16
*/
class QgsInterval
{
%TypeHeaderCode
#include <qgsinterval.h>
%End
public:
// YEAR const value taken from postgres query
// SELECT EXTRACT(EPOCH FROM interval '1 year')
//! Seconds per year (average)
static const int YEARS;
//! Seconds per month, based on 30 day month
static const int MONTHS;
//! Seconds per week
static const int WEEKS;
//! Seconds per day
static const int DAY;
//! Seconds per hour
static const int HOUR;
//! Seconds per minute
static const int MINUTE;
/** Default constructor for QgsInterval. Creates an invalid interval.
*/
QgsInterval();
/** Constructor for QgsInterval.
* @param seconds duration of interval in seconds
*/
QgsInterval( double seconds );
/** Returns the interval duration in years (based on an average year length)
* @see setYears()
*/
double years() const;
/** Sets the interval duration in years.
* @param years duration in years (based on average year length)
* @see years()
*/
void setYears( double years );
/** Returns the interval duration in months (based on a 30 day month).
* @see setMonths()
*/
double months() const;
/** Sets the interval duration in months.
* @param months duration in months (based on a 30 day month)
* @see months()
*/
void setMonths( double months );
/** Returns the interval duration in weeks.
* @see setWeeks()
*/
double weeks() const;
/** Sets the interval duration in weeks.
* @param weeks duration in weeks
* @see weeks()
*/
void setWeeks( double weeks );
/** Returns the interval duration in days.
* @see setDays()
*/
double days() const;
/** Sets the interval duration in days.
* @param days duration in days
* @see days()
*/
void setDays( double days );
/** Returns the interval duration in hours.
* @see setHours()
*/
double hours() const;
/** Sets the interval duration in hours.
* @param hours duration in hours
* @see hours()
*/
void setHours( double hours );
/** Returns the interval duration in minutes.
* @see setMinutes()
*/
double minutes() const;
/** Sets the interval duration in minutes.
* @param minutes duration in minutes
* @see minutes()
*/
void setMinutes( double minutes );
/** Returns the interval duration in seconds.
* @see setSeconds()
*/
double seconds() const;
/** Sets the interval duration in seconds.
* @param seconds duration in seconds
* @see seconds()
*/
void setSeconds( double seconds );
/** Returns true if the interval is valid.
* @see setValid()
*/
bool isValid() const;
/** Sets whether the interval is valid.
* @param valid set to true to set the interval as valid.
* @see isValid()
*/
void setValid( bool valid );
bool operator==( const QgsInterval& other ) const;
/** Converts a string to an interval
* @param string string to parse
* @returns interval, or invalid interval if string could not be parsed
*/
static QgsInterval fromString( const QString& string );
};

View File

@ -121,6 +121,7 @@ SET(QGIS_CORE_SRCS
qgsgml.cpp
qgsgmlschema.cpp
qgshistogram.cpp
qgsinterval.cpp
qgsjsonutils.cpp
qgslabel.cpp
qgslabelattributes.cpp
@ -637,6 +638,7 @@ SET(QGIS_CORE_HDRS
qgsgeometrycache.h
qgshistogram.h
qgsindexedfeature.h
qgsinterval.h
qgsjsonutils.h
qgslayerdefinition.h
qgslabel.h

View File

@ -57,79 +57,6 @@
// from parser
extern QgsExpression::Node* parseExpression( const QString& str, QString& parserErrorMsg );
QgsExpression::Interval QgsExpression::Interval::invalidInterVal()
{
QgsExpression::Interval inter = QgsExpression::Interval();
inter.setValid( false );
return inter;
}
QgsExpression::Interval QgsExpression::Interval::fromString( const QString& string )
{
int seconds = 0;
QRegExp rx( "([-+]?\\d?\\.?\\d+\\s+\\S+)", Qt::CaseInsensitive );
QStringList list;
int pos = 0;
while (( pos = rx.indexIn( string, pos ) ) != -1 )
{
list << rx.cap( 1 );
pos += rx.matchedLength();
}
QMap<int, QStringList> map;
map.insert( 1, QStringList() << "second" << "seconds" << tr( "second|seconds", "list of words separated by | which reference years" ).split( '|' ) );
map.insert( 0 + MINUTE, QStringList() << "minute" << "minutes" << tr( "minute|minutes", "list of words separated by | which reference minutes" ).split( '|' ) );
map.insert( 0 + HOUR, QStringList() << "hour" << "hours" << tr( "hour|hours", "list of words separated by | which reference minutes hours" ).split( '|' ) );
map.insert( 0 + DAY, QStringList() << "day" << "days" << tr( "day|days", "list of words separated by | which reference days" ).split( '|' ) );
map.insert( 0 + WEEKS, QStringList() << "week" << "weeks" << tr( "week|weeks", "wordlist separated by | which reference weeks" ).split( '|' ) );
map.insert( 0 + MONTHS, QStringList() << "month" << "months" << tr( "month|months", "list of words separated by | which reference months" ).split( '|' ) );
map.insert( 0 + YEARS, QStringList() << "year" << "years" << tr( "year|years", "list of words separated by | which reference years" ).split( '|' ) );
Q_FOREACH ( const QString& match, list )
{
QStringList split = match.split( QRegExp( "\\s+" ) );
bool ok;
double value = split.at( 0 ).toDouble( &ok );
if ( !ok )
{
continue;
}
bool matched = false;
QMap<int, QStringList>::const_iterator it = map.constBegin();
for ( ; it != map.constEnd(); ++it )
{
int duration = it.key();
Q_FOREACH ( const QString& name, it.value() )
{
if ( match.contains( name, Qt::CaseInsensitive ) )
{
matched = true;
break;
}
}
if ( matched )
{
seconds += value * duration;
break;
}
}
}
// If we can't parse the string at all then we just return invalid
if ( seconds == 0 )
return QgsExpression::Interval::invalidInterVal();
return QgsExpression::Interval( seconds );
}
bool QgsExpression::Interval::operator==( QgsExpression::Interval other ) const
{
return qgsDoubleNear( mSeconds, other.mSeconds );
}
///////////////////////////////////////////////
// three-value logic
@ -213,14 +140,14 @@ inline bool isDateTimeSafe( const QVariant& v )
inline bool isIntervalSafe( const QVariant& v )
{
if ( v.canConvert<QgsExpression::Interval>() )
if ( v.canConvert<QgsInterval>() )
{
return true;
}
if ( v.type() == QVariant::String )
{
return QgsExpression::Interval::fromString( v.toString() ).isValid();
return QgsInterval::fromString( v.toString() ).isValid();
}
return false;
}
@ -335,12 +262,12 @@ static QTime getTimeValue( const QVariant& value, QgsExpression* parent )
}
}
static QgsExpression::Interval getInterval( const QVariant& value, QgsExpression* parent, bool report_error = false )
static QgsInterval getInterval( const QVariant& value, QgsExpression* parent, bool report_error = false )
{
if ( value.canConvert<QgsExpression::Interval>() )
return value.value<QgsExpression::Interval>();
if ( value.canConvert<QgsInterval>() )
return value.value<QgsInterval>();
QgsExpression::Interval inter = QgsExpression::Interval::fromString( value.toString() );
QgsInterval inter = QgsInterval::fromString( value.toString() );
if ( inter.isValid() )
{
return inter;
@ -349,7 +276,7 @@ static QgsExpression::Interval getInterval( const QVariant& value, QgsExpression
if ( report_error )
parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Interval" ).arg( value.toString() ) );
return QgsExpression::Interval::invalidInterVal();
return QgsInterval();
}
static QgsGeometry getGeometry( const QVariant& value, QgsExpression* parent )
@ -1116,7 +1043,7 @@ static QVariant fcnAge( const QVariantList& values, const QgsExpressionContext*,
QDateTime d1 = getDateTimeValue( values.at( 0 ), parent );
QDateTime d2 = getDateTimeValue( values.at( 1 ), parent );
int seconds = d2.secsTo( d1 );
return QVariant::fromValue( QgsExpression::Interval( seconds ) );
return QVariant::fromValue( QgsInterval( seconds ) );
}
static QVariant fcnDayOfWeek( const QVariantList& values, const QgsExpressionContext*, QgsExpression *parent )
@ -1136,7 +1063,7 @@ static QVariant fcnDayOfWeek( const QVariantList& values, const QgsExpressionCon
static QVariant fcnDay( const QVariantList& values, const QgsExpressionContext*, QgsExpression *parent )
{
QVariant value = values.at( 0 );
QgsExpression::Interval inter = getInterval( value, parent, false );
QgsInterval inter = getInterval( value, parent, false );
if ( inter.isValid() )
{
return QVariant( inter.days() );
@ -1151,7 +1078,7 @@ static QVariant fcnDay( const QVariantList& values, const QgsExpressionContext*,
static QVariant fcnYear( const QVariantList& values, const QgsExpressionContext*, QgsExpression *parent )
{
QVariant value = values.at( 0 );
QgsExpression::Interval inter = getInterval( value, parent, false );
QgsInterval inter = getInterval( value, parent, false );
if ( inter.isValid() )
{
return QVariant( inter.years() );
@ -1166,7 +1093,7 @@ static QVariant fcnYear( const QVariantList& values, const QgsExpressionContext*
static QVariant fcnMonth( const QVariantList& values, const QgsExpressionContext*, QgsExpression *parent )
{
QVariant value = values.at( 0 );
QgsExpression::Interval inter = getInterval( value, parent, false );
QgsInterval inter = getInterval( value, parent, false );
if ( inter.isValid() )
{
return QVariant( inter.months() );
@ -1181,7 +1108,7 @@ static QVariant fcnMonth( const QVariantList& values, const QgsExpressionContext
static QVariant fcnWeek( const QVariantList& values, const QgsExpressionContext*, QgsExpression *parent )
{
QVariant value = values.at( 0 );
QgsExpression::Interval inter = getInterval( value, parent, false );
QgsInterval inter = getInterval( value, parent, false );
if ( inter.isValid() )
{
return QVariant( inter.weeks() );
@ -1196,7 +1123,7 @@ static QVariant fcnWeek( const QVariantList& values, const QgsExpressionContext*
static QVariant fcnHour( const QVariantList& values, const QgsExpressionContext*, QgsExpression *parent )
{
QVariant value = values.at( 0 );
QgsExpression::Interval inter = getInterval( value, parent, false );
QgsInterval inter = getInterval( value, parent, false );
if ( inter.isValid() )
{
return QVariant( inter.hours() );
@ -1211,7 +1138,7 @@ static QVariant fcnHour( const QVariantList& values, const QgsExpressionContext*
static QVariant fcnMinute( const QVariantList& values, const QgsExpressionContext*, QgsExpression *parent )
{
QVariant value = values.at( 0 );
QgsExpression::Interval inter = getInterval( value, parent, false );
QgsInterval inter = getInterval( value, parent, false );
if ( inter.isValid() )
{
return QVariant( inter.minutes() );
@ -1226,7 +1153,7 @@ static QVariant fcnMinute( const QVariantList& values, const QgsExpressionContex
static QVariant fcnSeconds( const QVariantList& values, const QgsExpressionContext*, QgsExpression *parent )
{
QVariant value = values.at( 0 );
QgsExpression::Interval inter = getInterval( value, parent, false );
QgsInterval inter = getInterval( value, parent, false );
if ( inter.isValid() )
{
return QVariant( inter.seconds() );
@ -3704,7 +3631,7 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression *parent, const Q
{
QDateTime dL = getDateTimeValue( vL, parent );
ENSURE_NO_EVAL_ERROR;
QgsExpression::Interval iL = getInterval( vR, parent );
QgsInterval iL = getInterval( vR, parent );
ENSURE_NO_EVAL_ERROR;
if ( mOp == boDiv || mOp == boMul || mOp == boMod )
{
@ -3920,7 +3847,7 @@ int QgsExpression::NodeBinaryOperator::computeInt( int x, int y )
}
}
QDateTime QgsExpression::NodeBinaryOperator::computeDateTimeFromInterval( const QDateTime& d, QgsExpression::Interval *i )
QDateTime QgsExpression::NodeBinaryOperator::computeDateTimeFromInterval( const QDateTime& d, QgsInterval *i )
{
switch ( mOp )
{
@ -4685,10 +4612,10 @@ QString QgsExpression::formatPreviewString( const QVariant& value )
QgsFeature feat = value.value<QgsFeature>();
return tr( "<i>&lt;feature: %1&gt;</i>" ).arg( feat.id() );
}
else if ( value.canConvert< QgsExpression::Interval >() )
else if ( value.canConvert< QgsInterval >() )
{
//result is a feature
QgsExpression::Interval interval = value.value<QgsExpression::Interval>();
QgsInterval interval = value.value<QgsInterval>();
return tr( "<i>&lt;interval: %1 days&gt;</i>" ).arg( interval.days() );
}
else if ( value.type() == QVariant::Date )

View File

@ -26,6 +26,7 @@
#include "qgis.h"
#include "qgsunittypes.h"
#include "qgsinterval.h"
class QgsFeature;
class QgsGeometry;
@ -959,51 +960,9 @@ class CORE_EXPORT QgsExpression
bool mHasNamedNodes;
};
class CORE_EXPORT Interval
{
// YEAR const value taken from postgres query
// SELECT EXTRACT(EPOCH FROM interval '1 year')
static const int YEARS = 31557600;
static const int MONTHS = 60 * 60 * 24 * 30;
static const int WEEKS = 60 * 60 * 24 * 7;
static const int DAY = 60 * 60 * 24;
static const int HOUR = 60 * 60;
static const int MINUTE = 60;
public:
Interval( double seconds = 0 )
: mSeconds( seconds )
, mValid( true )
{ }
//! interval length in years
double years() { return mSeconds / YEARS;}
//! interval length in months
double months() { return mSeconds / MONTHS; }
//! interval length in weeks
double weeks() { return mSeconds / WEEKS;}
//! interval length in days
double days() { return mSeconds / DAY;}
//! interval length in hours
double hours() { return mSeconds / HOUR;}
//! interval length in minutus
double minutes() { return mSeconds / MINUTE;}
//! interval length in seconds
double seconds() { return mSeconds; }
//! getter interval validity
bool isValid() { return mValid; }
//! setter interval validity
void setValid( bool valid ) { mValid = valid; }
//! compare two intervals
bool operator==( QgsExpression::Interval other ) const;
//! return an invalid interval
static QgsExpression::Interval invalidInterVal();
//! convert a string to an interval
static QgsExpression::Interval fromString( const QString& string );
private:
double mSeconds;
bool mValid;
};
//TODO QGIS 3.0 - remove
//! @deprecated use QgsInterval instead
typedef QgsInterval Interval;
class CORE_EXPORT NodeUnaryOperator : public Node
{
@ -1063,7 +1022,12 @@ class CORE_EXPORT QgsExpression
bool compare( double diff );
int computeInt( int x, int y );
double computeDouble( double x, double y );
QDateTime computeDateTimeFromInterval( const QDateTime& d, QgsExpression::Interval *i );
/** Computes the result date time calculation from a start datetime and an interval
* @param d start datetime
* @param i interval to add or subtract (depending on mOp)
*/
QDateTime computeDateTimeFromInterval( const QDateTime& d, QgsInterval* i );
BinaryOperator mOp;
Node* mOpLeft;
@ -1492,7 +1456,6 @@ class CORE_EXPORT QgsExpression
Q_NOWARN_DEPRECATED_POP
Q_DECLARE_METATYPE( QgsExpression::Interval )
Q_DECLARE_METATYPE( QgsExpression::Node* )
#endif // QGSEXPRESSION_H

131
src/core/qgsinterval.cpp Normal file
View File

@ -0,0 +1,131 @@
/***************************************************************************
qgsinterval.cpp
---------------
Date : May 2016
Copyright : (C) 2016 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsinterval.h"
#include "qgis.h"
#include <QString>
#include <QStringList>
#include <QMap>
#include <QObject>
#include <QDebug>
/***************************************************************************
* This class is considered CRITICAL and any change MUST be accompanied with
* full unit tests in test_qgsinterval.py.
* See details in QEP #17
****************************************************************************/
QgsInterval::QgsInterval()
: mSeconds( 0.0 )
, mValid( false )
{
}
QgsInterval::QgsInterval( double seconds )
: mSeconds( seconds )
, mValid( true )
{ }
bool QgsInterval::operator==( const QgsInterval& other ) const
{
if ( !mValid && !other.mValid )
return true;
else if ( mValid && other.mValid )
return qgsDoubleNear( mSeconds, other.mSeconds );
else
return false;
}
QgsInterval QgsInterval::fromString( const QString& string )
{
int seconds = 0;
QRegExp rx( "([-+]?\\d?\\.?\\d+\\s+\\S+)", Qt::CaseInsensitive );
QStringList list;
int pos = 0;
while (( pos = rx.indexIn( string, pos ) ) != -1 )
{
list << rx.cap( 1 );
pos += rx.matchedLength();
}
QMap<int, QStringList> map;
map.insert( 1, QStringList() << "second" << "seconds" << QObject::tr( "second|seconds", "list of words separated by | which reference years" ).split( '|' ) );
map.insert( 0 + MINUTE, QStringList() << "minute" << "minutes" << QObject::tr( "minute|minutes", "list of words separated by | which reference minutes" ).split( '|' ) );
map.insert( 0 + HOUR, QStringList() << "hour" << "hours" << QObject::tr( "hour|hours", "list of words separated by | which reference minutes hours" ).split( '|' ) );
map.insert( 0 + DAY, QStringList() << "day" << "days" << QObject::tr( "day|days", "list of words separated by | which reference days" ).split( '|' ) );
map.insert( 0 + WEEKS, QStringList() << "week" << "weeks" << QObject::tr( "week|weeks", "wordlist separated by | which reference weeks" ).split( '|' ) );
map.insert( 0 + MONTHS, QStringList() << "month" << "months" << QObject::tr( "month|months", "list of words separated by | which reference months" ).split( '|' ) );
map.insert( 0 + YEARS, QStringList() << "year" << "years" << QObject::tr( "year|years", "list of words separated by | which reference years" ).split( '|' ) );
Q_FOREACH ( const QString& match, list )
{
QStringList split = match.split( QRegExp( "\\s+" ) );
bool ok;
double value = split.at( 0 ).toDouble( &ok );
if ( !ok )
{
continue;
}
bool matched = false;
QMap<int, QStringList>::const_iterator it = map.constBegin();
for ( ; it != map.constEnd(); ++it )
{
int duration = it.key();
Q_FOREACH ( const QString& name, it.value() )
{
if ( match.contains( name, Qt::CaseInsensitive ) )
{
matched = true;
break;
}
}
if ( matched )
{
seconds += value * duration;
break;
}
}
}
// If we can't parse the string at all then we just return invalid
if ( seconds == 0 )
return QgsInterval();
return QgsInterval( seconds );
}
QDebug operator<<( QDebug dbg, const QgsInterval& interval )
{
if ( !interval.isValid() )
dbg.nospace() << "QgsInterval()";
else
dbg.nospace() << "QgsInterval(" << interval.seconds() << ")";
return dbg.maybeSpace();
}
QgsInterval operator-( const QDateTime& dt1, const QDateTime& dt2 )
{
qint64 mSeconds = dt2.msecsTo( dt1 );
return QgsInterval( mSeconds / 1000.0 );
}
QDateTime operator+( const QDateTime& start, const QgsInterval& interval )
{
return start.addMSecs( static_cast<qint64>( interval.seconds() * 1000.0 ) );
}

188
src/core/qgsinterval.h Normal file
View File

@ -0,0 +1,188 @@
/***************************************************************************
qgsinterval.h
-------------
Date : May 2016
Copyright : (C) 2016 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSINTERVAL_H
#define QGSINTERVAL_H
/***************************************************************************
* This class is considered CRITICAL and any change MUST be accompanied with
* full unit tests in test_qgsinterval.py.
* See details in QEP #17
****************************************************************************/
#include <QVariant>
class QString;
/** \ingroup core
* \class QgsInterval
* \brief A representation of the interval between two datetime values.
* \note Added in version 2.16
*/
class CORE_EXPORT QgsInterval
{
public:
// YEAR const value taken from postgres query
// SELECT EXTRACT(EPOCH FROM interval '1 year')
//! Seconds per year (average)
static const int YEARS = 31557600;
//! Seconds per month, based on 30 day month
static const int MONTHS = 60 * 60 * 24 * 30;
//! Seconds per week
static const int WEEKS = 60 * 60 * 24 * 7;
//! Seconds per day
static const int DAY = 60 * 60 * 24;
//! Seconds per hour
static const int HOUR = 60 * 60;
//! Seconds per minute
static const int MINUTE = 60;
/** Default constructor for QgsInterval. Creates an invalid interval.
*/
QgsInterval();
/** Constructor for QgsInterval.
* @param seconds duration of interval in seconds
*/
QgsInterval( double seconds );
/** Returns the interval duration in years (based on an average year length)
* @see setYears()
*/
double years() const { return mSeconds / YEARS; }
/** Sets the interval duration in years.
* @param years duration in years (based on average year length)
* @see years()
*/
void setYears( double years ) { mSeconds = years * YEARS; mValid = true; }
/** Returns the interval duration in months (based on a 30 day month).
* @see setMonths()
*/
double months() const { return mSeconds / MONTHS; }
/** Sets the interval duration in months.
* @param months duration in months (based on a 30 day month)
* @see months()
*/
void setMonths( double months ) { mSeconds = months * MONTHS; mValid = true; }
/** Returns the interval duration in weeks.
* @see setWeeks()
*/
double weeks() const { return mSeconds / WEEKS; }
/** Sets the interval duration in weeks.
* @param weeks duration in weeks
* @see weeks()
*/
void setWeeks( double weeks ) { mSeconds = weeks * WEEKS; mValid = true; }
/** Returns the interval duration in days.
* @see setDays()
*/
double days() const { return mSeconds / DAY; }
/** Sets the interval duration in days.
* @param days duration in days
* @see days()
*/
void setDays( double days ) { mSeconds = days * DAY; mValid = true; }
/** Returns the interval duration in hours.
* @see setHours()
*/
double hours() const { return mSeconds / HOUR; }
/** Sets the interval duration in hours.
* @param hours duration in hours
* @see hours()
*/
void setHours( double hours ) { mSeconds = hours * HOUR; mValid = true; }
/** Returns the interval duration in minutes.
* @see setMinutes()
*/
double minutes() const { return mSeconds / MINUTE; }
/** Sets the interval duration in minutes.
* @param minutes duration in minutes
* @see minutes()
*/
void setMinutes( double minutes ) { mSeconds = minutes * MINUTE; mValid = true; }
/** Returns the interval duration in seconds.
* @see setSeconds()
*/
double seconds() const { return mSeconds; }
/** Sets the interval duration in seconds.
* @param seconds duration in seconds
* @see seconds()
*/
void setSeconds( double seconds ) { mSeconds = seconds; mValid = true; }
/** Returns true if the interval is valid.
* @see setValid()
*/
bool isValid() const { return mValid; }
/** Sets whether the interval is valid.
* @param valid set to true to set the interval as valid.
* @see isValid()
*/
void setValid( bool valid ) { mValid = valid; }
bool operator==( const QgsInterval& other ) const;
/** Converts a string to an interval
* @param string string to parse
* @returns interval, or invalid interval if string could not be parsed
*/
static QgsInterval fromString( const QString& string );
private:
//! Duration of interval in seconds
double mSeconds;
//! True if interval is valid
bool mValid;
};
Q_DECLARE_METATYPE( QgsInterval )
/** Returns the interval between two datetimes
* @param dt1 start datetime
* @param dt2 datetime to subtract, ie subtract dt2 from dt1
* @note added in QGIS 2.16
* @note not available in Python bindings
*/
QgsInterval CORE_EXPORT operator-( const QDateTime& dt1, const QDateTime& dt2 );
/** Adds an interval to a datetime
* @param start initial datetime
* @param interval interval to add
* @note added in QGIS 2.16
* @note not available in Python bindings
*/
QDateTime CORE_EXPORT operator+( const QDateTime& start, const QgsInterval& interval );
//! Debug string representation of interval
QDebug operator<<( QDebug dbg, const QgsInterval& interval );
\
#endif // QGSINTERVAL_H

View File

@ -28,7 +28,7 @@ email : hugo dot mercier at oslandia dot com
#include <qgsgeometry.h>
#include <qgsmaplayerregistry.h>
#include <qgsproviderregistry.h>
#include "qgsinterval.h"
#include <sqlite3.h>
#include <spatialite.h>
#include <stdio.h>
@ -816,9 +816,9 @@ void qgisFunctionWrapper( sqlite3_context* ctxt, int nArgs, sqlite3_value** args
qgsGeometryToSpatialiteBlob( ret.value<QgsGeometry>(), /*srid*/0, blob, size );
sqlite3_result_blob( ctxt, blob, size, deleteGeometryBlob );
}
else if ( ret.canConvert<QgsExpression::Interval>() )
else if ( ret.canConvert<QgsInterval>() )
{
sqlite3_result_double( ctxt, ret.value<QgsExpression::Interval>().seconds() );
sqlite3_result_double( ctxt, ret.value<QgsInterval>().seconds() );
}
break;
}

View File

@ -734,15 +734,15 @@ class TestQgsExpression: public QObject
// Datetime functions
QTest::newRow( "to date" ) << "todate('2012-06-28')" << false << QVariant( QDate( 2012, 6, 28 ) );
QTest::newRow( "to interval" ) << "tointerval('1 Year 1 Month 1 Week 1 Hour 1 Minute')" << false << QVariant::fromValue( QgsExpression::Interval( 34758060 ) );
QTest::newRow( "to interval" ) << "tointerval('1 Year 1 Month 1 Week 1 Hour 1 Minute')" << false << QVariant::fromValue( QgsInterval( 34758060 ) );
QTest::newRow( "day with date" ) << "day('2012-06-28')" << false << QVariant( 28 );
QTest::newRow( "day with interval" ) << "day(tointerval('28 days'))" << false << QVariant( 28.0 );
QTest::newRow( "month with date" ) << "month('2012-06-28')" << false << QVariant( 6 );
QTest::newRow( "month with interval" ) << "month(tointerval('2 months'))" << false << QVariant( 2.0 );
QTest::newRow( "year with date" ) << "year('2012-06-28')" << false << QVariant( 2012 );
QTest::newRow( "year with interval" ) << "year(tointerval('2 years'))" << false << QVariant( 2.0 );
QTest::newRow( "age" ) << "age('2012-06-30','2012-06-28')" << false << QVariant::fromValue( QgsExpression::Interval( 172800 ) );
QTest::newRow( "negative age" ) << "age('2012-06-28','2012-06-30')" << false << QVariant::fromValue( QgsExpression::Interval( -172800 ) );
QTest::newRow( "age" ) << "age('2012-06-30','2012-06-28')" << false << QVariant::fromValue( QgsInterval( 172800 ) );
QTest::newRow( "negative age" ) << "age('2012-06-28','2012-06-30')" << false << QVariant::fromValue( QgsInterval( -172800 ) );
QTest::newRow( "day of week date" ) << "day_of_week(todate('2015-09-21'))" << false << QVariant( 1 );
QTest::newRow( "day of week datetime" ) << "day_of_week(to_datetime('2015-09-20 13:01:43'))" << false << QVariant( 0 );
QTest::newRow( "hour datetime" ) << "hour(to_datetime('2015-09-20 13:01:43'))" << false << QVariant( 13 );
@ -909,10 +909,10 @@ class TestQgsExpression: public QObject
break;
case QVariant::UserType:
{
if ( result.userType() == qMetaTypeId<QgsExpression::Interval>() )
if ( result.userType() == qMetaTypeId<QgsInterval>() )
{
QgsExpression::Interval inter = result.value<QgsExpression::Interval>();
QgsExpression::Interval gotinter = expected.value<QgsExpression::Interval>();
QgsInterval inter = result.value<QgsInterval>();
QgsInterval gotinter = expected.value<QgsInterval>();
QCOMPARE( inter.seconds(), gotinter.seconds() );
}
else

View File

@ -41,6 +41,7 @@ ADD_PYTHON_TEST(PyQgsGeometryAvoidIntersections test_qgsgeometry_avoid_intersect
ADD_PYTHON_TEST(PyQgsGeometryGeneratorSymbolLayerV2 test_qgsgeometrygeneratorsymbollayerv2.py)
ADD_PYTHON_TEST(PyQgsGeometryTest test_qgsgeometry.py)
ADD_PYTHON_TEST(PyQgsGraduatedSymbolRendererV2 test_qgsgraduatedsymbolrendererv2.py)
ADD_PYTHON_TEST(PyQgsInterval test_qgsinterval.py)
ADD_PYTHON_TEST(PyQgsJSONUtils test_qgsjsonutils.py)
ADD_PYTHON_TEST(PyQgsMapUnitScale test_qgsmapunitscale.py)
ADD_PYTHON_TEST(PyQgsMemoryProvider test_provider_memory.py)

View File

@ -0,0 +1,162 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsInterval.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = 'Nyall Dawson'
__date__ = '10/05/2016'
__copyright__ = 'Copyright 2015, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import qgis # NOQA
from qgis.core import QgsInterval
from qgis.testing import unittest
from qgis.PyQt.QtCore import QDateTime, QDate, QTime
class TestQgsInterval(unittest.TestCase):
def testIntervalConstructor(self):
""" Test QgsInterval constructor """
# invalid interval
i = QgsInterval()
self.assertFalse(i.isValid())
i.setValid(True)
self.assertTrue(i.isValid())
i.setValid(False)
self.assertFalse(i.isValid())
# setting a duration should make interval valid
i.setSeconds(5)
self.assertTrue(i.isValid())
# constructor with duration
i = QgsInterval(56)
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 56)
def testSettersGetters(self):
# setters and getters
i = QgsInterval()
i.setSeconds(60)
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 60.0)
i = QgsInterval()
i.setMinutes(10)
self.assertTrue(i.isValid())
self.assertEqual(i.minutes(), 10.0)
i = QgsInterval()
i.setHours(5)
self.assertTrue(i.isValid())
self.assertEqual(i.hours(), 5.0)
i = QgsInterval()
i.setDays(70)
self.assertTrue(i.isValid())
self.assertEqual(i.days(), 70.0)
i = QgsInterval()
i.setWeeks(9)
self.assertTrue(i.isValid())
self.assertEqual(i.weeks(), 9.0)
i = QgsInterval()
i.setMonths(4)
self.assertTrue(i.isValid())
self.assertEqual(i.months(), 4.0)
i = QgsInterval()
i.setYears(8)
self.assertTrue(i.isValid())
self.assertEqual(i.years(), 8.0)
def testConversions(self):
i = QgsInterval()
# conversions
i.setYears(1)
self.assertEqual(round(i.months()), 12)
self.assertEqual(round(i.weeks()), 52)
self.assertEqual(round(i.days()), 365)
i.setDays(5)
self.assertEqual(i.hours(), 5 * 24)
self.assertEqual(i.minutes(), 5 * 24 * 60)
self.assertEqual(i.seconds(), 5 * 24 * 60 * 60)
def testEquality(self):
i1 = QgsInterval()
i2 = QgsInterval()
self.assertEqual(i1, i2)
i1 = QgsInterval(5)
self.assertNotEqual(i1, i2)
i1.setValid(False)
i2 = QgsInterval(5)
self.assertNotEqual(i1, i2)
i1 = QgsInterval(5)
self.assertEqual(i1, i2)
i1.setSeconds(6)
self.assertNotEqual(i1, i2)
def testFromString(self):
i = QgsInterval.fromString('1 Year 1 Month 1 Week 1 Hour 1 Minute')
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 34758060)
i = QgsInterval.fromString('1 Year, 1 Month, 1 Week, 1 Hour, 1 Minute')
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 34758060)
i = QgsInterval.fromString('1 Year; 1 Month; 1 Week; 1 Hour; 1 Minute')
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 34758060)
i = QgsInterval.fromString('1 Year. 1 Month. 1 Week. 1 Hour. 1 Minute.')
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 34758060)
i = QgsInterval.fromString('2 Years')
self.assertTrue(i.isValid())
self.assertEqual(i.years(), 2)
i = QgsInterval.fromString('30 month')
self.assertTrue(i.isValid())
self.assertEqual(i.months(), 30)
i = QgsInterval.fromString(' 40 MONTHS ')
self.assertTrue(i.isValid())
self.assertEqual(i.months(), 40)
i = QgsInterval.fromString('2.5 weeks')
self.assertTrue(i.isValid())
self.assertEqual(i.weeks(), 2.5)
i = QgsInterval.fromString('2.5 WEEK')
self.assertTrue(i.isValid())
self.assertEqual(i.weeks(), 2.5)
i = QgsInterval.fromString('1 Day')
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 24 * 60 * 60)
i = QgsInterval.fromString('2 dAys')
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 48 * 60 * 60)
i = QgsInterval.fromString('1 hours')
self.assertTrue(i.isValid())
self.assertEqual(i.hours(), 1)
i = QgsInterval.fromString('1.7 HoURS ')
self.assertTrue(i.isValid())
self.assertEqual(i.hours(), 1.7)
i = QgsInterval.fromString('2 minutes')
self.assertTrue(i.isValid())
self.assertEqual(i.minutes(), 2)
i = QgsInterval.fromString('123 MiNuTe ')
self.assertTrue(i.isValid())
self.assertEqual(i.minutes(), 123)
i = QgsInterval.fromString('5 Seconds')
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 5)
i = QgsInterval.fromString('5 second')
self.assertTrue(i.isValid())
self.assertEqual(i.seconds(), 5)
i = QgsInterval.fromString('bad')
self.assertFalse(i.isValid())
if __name__ == '__main__':
unittest.main()