Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsinterval.cpp 3 : : --------------- 4 : : Date : May 2016 5 : : Copyright : (C) 2016 by Nyall Dawson 6 : : Email : nyall dot dawson at gmail dot com 7 : : *************************************************************************** 8 : : * * 9 : : * This program is free software; you can redistribute it and/or modify * 10 : : * it under the terms of the GNU General Public License as published by * 11 : : * the Free Software Foundation; either version 2 of the License, or * 12 : : * (at your option) any later version. * 13 : : * * 14 : : ***************************************************************************/ 15 : : 16 : : #include "qgsinterval.h" 17 : : #include <QString> 18 : : #include <QStringList> 19 : : #include <QMap> 20 : : #include <QObject> 21 : : #include <QDebug> 22 : : #include <QDateTime> 23 : : 24 : : /*************************************************************************** 25 : : * This class is considered CRITICAL and any change MUST be accompanied with 26 : : * full unit tests in test_qgsinterval.py. 27 : : * See details in QEP #17 28 : : ****************************************************************************/ 29 : : 30 : 0 : QgsInterval::QgsInterval( double seconds ) 31 : 0 : : mSeconds( seconds ) 32 : 0 : , mValid( true ) 33 : 0 : , mOriginalDuration( seconds ) 34 : 0 : , mOriginalUnit( QgsUnitTypes::TemporalSeconds ) 35 : : { 36 : 0 : } 37 : : 38 : 0 : QgsInterval::QgsInterval( double duration, QgsUnitTypes::TemporalUnit unit ) 39 : 0 : : mSeconds( duration * QgsUnitTypes::fromUnitToUnitFactor( unit, QgsUnitTypes::TemporalSeconds ) ) 40 : 0 : , mValid( true ) 41 : 0 : , mOriginalDuration( duration ) 42 : 0 : , mOriginalUnit( unit ) 43 : : { 44 : 0 : } 45 : : 46 : 0 : QgsInterval::QgsInterval( double years, double months, double weeks, double days, double hours, double minutes, double seconds ) 47 : 0 : : mSeconds( years * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalYears, QgsUnitTypes::TemporalSeconds ) 48 : 0 : + months * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalMonths, QgsUnitTypes::TemporalSeconds ) 49 : 0 : + weeks * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalWeeks, QgsUnitTypes::TemporalSeconds ) 50 : 0 : + days * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalDays, QgsUnitTypes::TemporalSeconds ) 51 : 0 : + hours * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalHours, QgsUnitTypes::TemporalSeconds ) 52 : 0 : + minutes * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalMinutes, QgsUnitTypes::TemporalSeconds ) 53 : 0 : + seconds ) 54 : 0 : , mValid( true ) 55 : : { 56 : 0 : if ( years && !months && !weeks && !days && !hours && !minutes && !seconds ) 57 : : { 58 : 0 : mOriginalDuration = years; 59 : 0 : mOriginalUnit = QgsUnitTypes::TemporalYears; 60 : 0 : } 61 : 0 : else if ( !years && months && !weeks && !days && !hours && !minutes && !seconds ) 62 : : { 63 : 0 : mOriginalDuration = months; 64 : 0 : mOriginalUnit = QgsUnitTypes::TemporalMonths; 65 : 0 : } 66 : 0 : else if ( !years && !months && weeks && !days && !hours && !minutes && !seconds ) 67 : : { 68 : 0 : mOriginalDuration = weeks; 69 : 0 : mOriginalUnit = QgsUnitTypes::TemporalWeeks; 70 : 0 : } 71 : 0 : else if ( !years && !months && !weeks && days && !hours && !minutes && !seconds ) 72 : : { 73 : 0 : mOriginalDuration = days; 74 : 0 : mOriginalUnit = QgsUnitTypes::TemporalDays; 75 : 0 : } 76 : 0 : else if ( !years && !months && !weeks && !days && hours && !minutes && !seconds ) 77 : : { 78 : 0 : mOriginalDuration = hours; 79 : 0 : mOriginalUnit = QgsUnitTypes::TemporalHours; 80 : 0 : } 81 : 0 : else if ( !years && !months && !weeks && !days && !hours && minutes && !seconds ) 82 : : { 83 : 0 : mOriginalDuration = minutes; 84 : 0 : mOriginalUnit = QgsUnitTypes::TemporalMinutes; 85 : 0 : } 86 : 0 : else if ( !years && !months && !weeks && !days && !hours && !minutes && seconds ) 87 : : { 88 : 0 : mOriginalDuration = seconds; 89 : 0 : mOriginalUnit = QgsUnitTypes::TemporalSeconds; 90 : 0 : } 91 : 0 : else if ( !years && !months && !weeks && !days && !hours && !minutes && !seconds ) 92 : : { 93 : 0 : mOriginalDuration = 0; 94 : 0 : mOriginalUnit = QgsUnitTypes::TemporalSeconds; 95 : 0 : } 96 : : else 97 : : { 98 : 0 : mOriginalUnit = QgsUnitTypes::TemporalUnknownUnit; 99 : : } 100 : 0 : } 101 : : 102 : 0 : double QgsInterval::years() const 103 : : { 104 : 0 : if ( mOriginalUnit == QgsUnitTypes::TemporalYears ) 105 : 0 : return mOriginalDuration; 106 : : 107 : 0 : return mSeconds / YEARS; 108 : 0 : } 109 : : 110 : 0 : void QgsInterval::setYears( double years ) 111 : : { 112 : 0 : mSeconds = years * YEARS; 113 : 0 : mValid = true; 114 : 0 : mOriginalDuration = years; 115 : 0 : mOriginalUnit = QgsUnitTypes::TemporalYears; 116 : 0 : } 117 : : 118 : 0 : double QgsInterval::months() const 119 : : { 120 : 0 : if ( mOriginalUnit == QgsUnitTypes::TemporalMonths ) 121 : 0 : return mOriginalDuration; 122 : : 123 : 0 : return mSeconds / MONTHS; 124 : 0 : } 125 : : 126 : 0 : void QgsInterval::setMonths( double months ) 127 : : { 128 : 0 : mSeconds = months * MONTHS; 129 : 0 : mValid = true; 130 : 0 : mOriginalDuration = months; 131 : 0 : mOriginalUnit = QgsUnitTypes::TemporalMonths; 132 : 0 : } 133 : : 134 : 0 : double QgsInterval::weeks() const 135 : : { 136 : 0 : if ( mOriginalUnit == QgsUnitTypes::TemporalWeeks ) 137 : 0 : return mOriginalDuration; 138 : : 139 : 0 : return mSeconds / WEEKS; 140 : 0 : } 141 : : 142 : : 143 : 0 : void QgsInterval::setWeeks( double weeks ) 144 : : { 145 : 0 : mSeconds = weeks * WEEKS; 146 : 0 : mValid = true; 147 : 0 : mOriginalDuration = weeks; 148 : 0 : mOriginalUnit = QgsUnitTypes::TemporalWeeks; 149 : 0 : } 150 : : 151 : 0 : double QgsInterval::days() const 152 : : { 153 : 0 : if ( mOriginalUnit == QgsUnitTypes::TemporalDays ) 154 : 0 : return mOriginalDuration; 155 : : 156 : 0 : return mSeconds / DAY; 157 : 0 : } 158 : : 159 : : 160 : 0 : void QgsInterval::setDays( double days ) 161 : : { 162 : 0 : mSeconds = days * DAY; 163 : 0 : mValid = true; 164 : 0 : mOriginalDuration = days; 165 : 0 : mOriginalUnit = QgsUnitTypes::TemporalDays; 166 : 0 : } 167 : : 168 : 0 : double QgsInterval::hours() const 169 : : { 170 : 0 : if ( mOriginalUnit == QgsUnitTypes::TemporalHours ) 171 : 0 : return mOriginalDuration; 172 : : 173 : 0 : return mSeconds / HOUR; 174 : 0 : } 175 : : 176 : : 177 : 0 : void QgsInterval::setHours( double hours ) 178 : : { 179 : 0 : mSeconds = hours * HOUR; 180 : 0 : mValid = true; 181 : 0 : mOriginalDuration = hours; 182 : 0 : mOriginalUnit = QgsUnitTypes::TemporalHours; 183 : 0 : } 184 : : 185 : 0 : double QgsInterval::minutes() const 186 : : { 187 : 0 : if ( mOriginalUnit == QgsUnitTypes::TemporalMinutes ) 188 : 0 : return mOriginalDuration; 189 : : 190 : 0 : return mSeconds / MINUTE; 191 : 0 : } 192 : : 193 : 0 : void QgsInterval::setMinutes( double minutes ) 194 : : { 195 : 0 : mSeconds = minutes * MINUTE; 196 : 0 : mValid = true; 197 : 0 : mOriginalDuration = minutes; 198 : 0 : mOriginalUnit = QgsUnitTypes::TemporalMinutes; 199 : 0 : } 200 : : 201 : 0 : void QgsInterval::setSeconds( double seconds ) 202 : : { 203 : 0 : mSeconds = seconds; 204 : 0 : mValid = true; 205 : 0 : mOriginalDuration = seconds; 206 : 0 : mOriginalUnit = QgsUnitTypes::TemporalSeconds; 207 : 0 : } 208 : : 209 : 0 : QgsInterval QgsInterval::fromString( const QString &string ) 210 : : { 211 : 0 : double seconds = 0; 212 : 0 : QRegExp rx( "([-+]?\\d*\\.?\\d+\\s+\\S+)", Qt::CaseInsensitive ); 213 : 0 : QStringList list; 214 : 0 : int pos = 0; 215 : : 216 : 0 : while ( ( pos = rx.indexIn( string, pos ) ) != -1 ) 217 : : { 218 : 0 : list << rx.cap( 1 ); 219 : 0 : pos += rx.matchedLength(); 220 : : } 221 : : 222 : 0 : QMap<int, QStringList> map; 223 : 0 : map.insert( 1, QStringList() << QStringLiteral( "second" ) << QStringLiteral( "seconds" ) << QObject::tr( "second|seconds", "list of words separated by | which reference years" ).split( '|' ) ); 224 : 0 : map.insert( 0 + MINUTE, QStringList() << QStringLiteral( "minute" ) << QStringLiteral( "minutes" ) << QObject::tr( "minute|minutes", "list of words separated by | which reference minutes" ).split( '|' ) ); 225 : 0 : map.insert( 0 + HOUR, QStringList() << QStringLiteral( "hour" ) << QStringLiteral( "hours" ) << QObject::tr( "hour|hours", "list of words separated by | which reference minutes hours" ).split( '|' ) ); 226 : 0 : map.insert( 0 + DAY, QStringList() << QStringLiteral( "day" ) << QStringLiteral( "days" ) << QObject::tr( "day|days", "list of words separated by | which reference days" ).split( '|' ) ); 227 : 0 : map.insert( 0 + WEEKS, QStringList() << QStringLiteral( "week" ) << QStringLiteral( "weeks" ) << QObject::tr( "week|weeks", "wordlist separated by | which reference weeks" ).split( '|' ) ); 228 : 0 : map.insert( 0 + MONTHS, QStringList() << QStringLiteral( "month" ) << QStringLiteral( "months" ) << QObject::tr( "month|months", "list of words separated by | which reference months" ).split( '|' ) ); 229 : 0 : map.insert( 0 + YEARS, QStringList() << QStringLiteral( "year" ) << QStringLiteral( "years" ) << QObject::tr( "year|years", "list of words separated by | which reference years" ).split( '|' ) ); 230 : : 231 : 0 : const auto constList = list; 232 : 0 : for ( const QString &match : constList ) 233 : : { 234 : 0 : QStringList split = match.split( QRegExp( "\\s+" ) ); 235 : : bool ok; 236 : 0 : double value = split.at( 0 ).toDouble( &ok ); 237 : 0 : if ( !ok ) 238 : : { 239 : 0 : continue; 240 : : } 241 : : 242 : 0 : bool matched = false; 243 : 0 : QMap<int, QStringList>::const_iterator it = map.constBegin(); 244 : 0 : for ( ; it != map.constEnd(); ++it ) 245 : : { 246 : 0 : int duration = it.key(); 247 : 0 : const auto constValue = it.value(); 248 : 0 : for ( const QString &name : constValue ) 249 : : { 250 : 0 : if ( match.contains( name, Qt::CaseInsensitive ) ) 251 : : { 252 : 0 : matched = true; 253 : 0 : break; 254 : : } 255 : : } 256 : : 257 : 0 : if ( matched ) 258 : : { 259 : 0 : seconds += value * duration; 260 : 0 : break; 261 : : } 262 : 0 : } 263 : 0 : } 264 : : 265 : : // If we can't parse the string at all then we just return invalid 266 : 0 : if ( seconds == 0 ) 267 : 0 : return QgsInterval(); 268 : : 269 : 0 : return QgsInterval( seconds ); 270 : 0 : } 271 : : 272 : 0 : QDebug operator<<( QDebug dbg, const QgsInterval &interval ) 273 : : { 274 : 0 : if ( !interval.isValid() ) 275 : 0 : dbg.nospace() << "QgsInterval()"; 276 : : else 277 : 0 : dbg.nospace() << "QgsInterval(" << interval.seconds() << ")"; 278 : 0 : return dbg.maybeSpace(); 279 : : } 280 : : 281 : 0 : QgsInterval operator-( const QDateTime &dt1, const QDateTime &dt2 ) 282 : : { 283 : 0 : qint64 mSeconds = dt2.msecsTo( dt1 ); 284 : 0 : return QgsInterval( mSeconds / 1000.0 ); 285 : : } 286 : : 287 : 0 : QDateTime operator+( const QDateTime &start, const QgsInterval &interval ) 288 : : { 289 : 0 : return start.addMSecs( static_cast<qint64>( interval.seconds() * 1000.0 ) ); 290 : : } 291 : : 292 : 0 : QgsInterval operator-( QDate date1, QDate date2 ) 293 : : { 294 : 0 : qint64 seconds = static_cast< qint64 >( date2.daysTo( date1 ) ) * 24 * 60 * 60; 295 : 0 : return QgsInterval( seconds ); 296 : : } 297 : : 298 : 0 : QgsInterval operator-( QTime time1, QTime time2 ) 299 : : { 300 : 0 : qint64 mSeconds = time2.msecsTo( time1 ); 301 : 0 : return QgsInterval( mSeconds / 1000.0 ); 302 : : }