QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsinterval.cpp
Go to the documentation of this file.
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 
18 #include <QString>
19 #include <QStringList>
20 #include <QMap>
21 #include <QObject>
22 #include <QDebug>
23 #include <QDateTime>
24 #include <QRegularExpression>
25 
26 /***************************************************************************
27  * This class is considered CRITICAL and any change MUST be accompanied with
28  * full unit tests in test_qgsinterval.py.
29  * See details in QEP #17
30  ****************************************************************************/
31 
32 QgsInterval::QgsInterval( double seconds )
33  : mSeconds( seconds )
34  , mValid( true )
35  , mOriginalDuration( seconds )
36  , mOriginalUnit( QgsUnitTypes::TemporalSeconds )
37 {
38 }
39 
41  : mSeconds( duration * QgsUnitTypes::fromUnitToUnitFactor( unit, QgsUnitTypes::TemporalSeconds ) )
42  , mValid( true )
43  , mOriginalDuration( duration )
44  , mOriginalUnit( unit )
45 {
46 }
47 
48 QgsInterval::QgsInterval( double years, double months, double weeks, double days, double hours, double minutes, double seconds )
49  : mSeconds( years * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalYears, QgsUnitTypes::TemporalSeconds )
50  + months * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalMonths, QgsUnitTypes::TemporalSeconds )
51  + weeks * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalWeeks, QgsUnitTypes::TemporalSeconds )
52  + days * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalDays, QgsUnitTypes::TemporalSeconds )
53  + hours * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalHours, QgsUnitTypes::TemporalSeconds )
54  + minutes * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::TemporalMinutes, QgsUnitTypes::TemporalSeconds )
55  + seconds )
56  , mValid( true )
57 {
58  if ( years && !months && !weeks && !days && !hours && !minutes && !seconds )
59  {
60  mOriginalDuration = years;
61  mOriginalUnit = QgsUnitTypes::TemporalYears;
62  }
63  else if ( !years && months && !weeks && !days && !hours && !minutes && !seconds )
64  {
65  mOriginalDuration = months;
66  mOriginalUnit = QgsUnitTypes::TemporalMonths;
67  }
68  else if ( !years && !months && weeks && !days && !hours && !minutes && !seconds )
69  {
70  mOriginalDuration = weeks;
71  mOriginalUnit = QgsUnitTypes::TemporalWeeks;
72  }
73  else if ( !years && !months && !weeks && days && !hours && !minutes && !seconds )
74  {
75  mOriginalDuration = days;
76  mOriginalUnit = QgsUnitTypes::TemporalDays;
77  }
78  else if ( !years && !months && !weeks && !days && hours && !minutes && !seconds )
79  {
80  mOriginalDuration = hours;
81  mOriginalUnit = QgsUnitTypes::TemporalHours;
82  }
83  else if ( !years && !months && !weeks && !days && !hours && minutes && !seconds )
84  {
85  mOriginalDuration = minutes;
86  mOriginalUnit = QgsUnitTypes::TemporalMinutes;
87  }
88  else if ( !years && !months && !weeks && !days && !hours && !minutes && seconds )
89  {
90  mOriginalDuration = seconds;
91  mOriginalUnit = QgsUnitTypes::TemporalSeconds;
92  }
93  else if ( !years && !months && !weeks && !days && !hours && !minutes && !seconds )
94  {
95  mOriginalDuration = 0;
96  mOriginalUnit = QgsUnitTypes::TemporalSeconds;
97  }
98  else
99  {
100  mOriginalUnit = QgsUnitTypes::TemporalUnknownUnit;
101  }
102 }
103 
104 double QgsInterval::years() const
105 {
106  if ( mOriginalUnit == QgsUnitTypes::TemporalYears )
107  return mOriginalDuration;
108 
109  return mSeconds / YEARS;
110 }
111 
112 void QgsInterval::setYears( double years )
113 {
114  mSeconds = years * YEARS;
115  mValid = true;
116  mOriginalDuration = years;
117  mOriginalUnit = QgsUnitTypes::TemporalYears;
118 }
119 
120 double QgsInterval::months() const
121 {
122  if ( mOriginalUnit == QgsUnitTypes::TemporalMonths )
123  return mOriginalDuration;
124 
125  return mSeconds / MONTHS;
126 }
127 
128 void QgsInterval::setMonths( double months )
129 {
130  mSeconds = months * MONTHS;
131  mValid = true;
132  mOriginalDuration = months;
133  mOriginalUnit = QgsUnitTypes::TemporalMonths;
134 }
135 
136 double QgsInterval::weeks() const
137 {
138  if ( mOriginalUnit == QgsUnitTypes::TemporalWeeks )
139  return mOriginalDuration;
140 
141  return mSeconds / WEEKS;
142 }
143 
144 
145 void QgsInterval::setWeeks( double weeks )
146 {
147  mSeconds = weeks * WEEKS;
148  mValid = true;
149  mOriginalDuration = weeks;
150  mOriginalUnit = QgsUnitTypes::TemporalWeeks;
151 }
152 
153 double QgsInterval::days() const
154 {
155  if ( mOriginalUnit == QgsUnitTypes::TemporalDays )
156  return mOriginalDuration;
157 
158  return mSeconds / DAY;
159 }
160 
161 
162 void QgsInterval::setDays( double days )
163 {
164  mSeconds = days * DAY;
165  mValid = true;
166  mOriginalDuration = days;
167  mOriginalUnit = QgsUnitTypes::TemporalDays;
168 }
169 
170 double QgsInterval::hours() const
171 {
172  if ( mOriginalUnit == QgsUnitTypes::TemporalHours )
173  return mOriginalDuration;
174 
175  return mSeconds / HOUR;
176 }
177 
178 
179 void QgsInterval::setHours( double hours )
180 {
181  mSeconds = hours * HOUR;
182  mValid = true;
183  mOriginalDuration = hours;
184  mOriginalUnit = QgsUnitTypes::TemporalHours;
185 }
186 
187 double QgsInterval::minutes() const
188 {
189  if ( mOriginalUnit == QgsUnitTypes::TemporalMinutes )
190  return mOriginalDuration;
191 
192  return mSeconds / MINUTE;
193 }
194 
195 void QgsInterval::setMinutes( double minutes )
196 {
197  mSeconds = minutes * MINUTE;
198  mValid = true;
199  mOriginalDuration = minutes;
200  mOriginalUnit = QgsUnitTypes::TemporalMinutes;
201 }
202 
203 void QgsInterval::setSeconds( double seconds )
204 {
205  mSeconds = seconds;
206  mValid = true;
207  mOriginalDuration = seconds;
208  mOriginalUnit = QgsUnitTypes::TemporalSeconds;
209 }
210 
211 QgsInterval QgsInterval::fromString( const QString &string )
212 {
213  double seconds = 0;
214  const thread_local QRegularExpression rx( "([-+]?\\d*\\.?\\d+\\s+\\S+)", QRegularExpression::CaseInsensitiveOption );
215  const thread_local QRegularExpression rxtime( ".* \\d{1,2}(:)\\d{1,2}(:)\\d{1,2}.*", QRegularExpression::CaseInsensitiveOption );
216 
217  const QRegularExpressionMatch matchtime = rxtime.match( string );
218  QString modedString = QString( string );
219  if ( matchtime.hasMatch() ) //some part of the string contains 00:00:00 style duration
220  {
221  // Get the second occurrence of : (minutes)
222  modedString.replace( matchtime.capturedStart( 2 ), 1, " minutes " );
223  // Get the first occurrence of : (hours)
224  modedString.replace( matchtime.capturedStart( 1 ), 1, " hours " );
225  modedString.append( " seconds" );
226  }
227 
228  QStringList list;
229  int pos = 0;
230  QRegularExpressionMatch match = rx.match( modedString );
231  while ( match.hasMatch() )
232  {
233  list << match.captured( 1 );
234  pos = match.capturedStart() + match.capturedLength();
235  match = rx.match( modedString, pos );
236  }
237 
238  const thread_local QMap<int, QStringList> map{{
239  {1, QStringList() << QStringLiteral( "second" ) << QStringLiteral( "seconds" ) << QObject::tr( "second|seconds", "list of words separated by | which reference years" ).split( '|' )},
240  { 0 + MINUTE, QStringList() << QStringLiteral( "minute" ) << QStringLiteral( "minutes" ) << QObject::tr( "minute|minutes", "list of words separated by | which reference minutes" ).split( '|' ) },
241  {0 + HOUR, QStringList() << QStringLiteral( "hour" ) << QStringLiteral( "hours" ) << QObject::tr( "hour|hours", "list of words separated by | which reference minutes hours" ).split( '|' )},
242  {0 + DAY, QStringList() << QStringLiteral( "day" ) << QStringLiteral( "days" ) << QObject::tr( "day|days", "list of words separated by | which reference days" ).split( '|' )},
243  {0 + WEEKS, QStringList() << QStringLiteral( "week" ) << QStringLiteral( "weeks" ) << QObject::tr( "week|weeks", "wordlist separated by | which reference weeks" ).split( '|' )},
244  {0 + MONTHS, QStringList() << QStringLiteral( "month" ) << QStringLiteral( "months" ) << QStringLiteral( "mon" ) << QObject::tr( "month|months|mon", "list of words separated by | which reference months" ).split( '|' )},
245  {0 + YEARS, QStringList() << QStringLiteral( "year" ) << QStringLiteral( "years" ) << QObject::tr( "year|years", "list of words separated by | which reference years" ).split( '|' )},
246  }};
247 
248  const thread_local QRegularExpression splitRx( "\\s+" );
249 
250  for ( const QString &match : std::as_const( list ) )
251  {
252  const QStringList split = match.split( splitRx );
253  bool ok;
254  const double value = split.at( 0 ).toDouble( &ok );
255  if ( !ok )
256  {
257  continue;
258  }
259 
260  bool matched = false;
261  QMap<int, QStringList>::const_iterator it = map.constBegin();
262  for ( ; it != map.constEnd(); ++it )
263  {
264  const int duration = it.key();
265  const QStringList durationIdentifiers = it.value();
266  for ( const QString &identifier : durationIdentifiers )
267  {
268  if ( match.contains( identifier, Qt::CaseInsensitive ) )
269  {
270  matched = true;
271  break;
272  }
273  }
274 
275  if ( matched )
276  {
277  seconds += value * duration;
278  break;
279  }
280  }
281  }
282 
283  // If we can't parse the string at all then we just return invalid
284  if ( seconds == 0 )
285  return QgsInterval();
286 
287  return QgsInterval( seconds );
288 }
289 
290 QDebug operator<<( QDebug dbg, const QgsInterval &interval )
291 {
292  if ( !interval.isValid() )
293  dbg.nospace() << "QgsInterval()";
294  else
295  dbg.nospace() << "QgsInterval(" << interval.seconds() << ")";
296  return dbg.maybeSpace();
297 }
298 
299 QgsInterval operator-( const QDateTime &dt1, const QDateTime &dt2 )
300 {
301  const qint64 mSeconds = dt2.msecsTo( dt1 );
302  return QgsInterval( mSeconds / 1000.0 );
303 }
304 
305 QDateTime operator+( const QDateTime &start, const QgsInterval &interval )
306 {
307  return start.addMSecs( static_cast<qint64>( interval.seconds() * 1000.0 ) );
308 }
309 
310 QgsInterval operator-( QDate date1, QDate date2 )
311 {
312  const qint64 seconds = static_cast< qint64 >( date2.daysTo( date1 ) ) * 24 * 60 * 60;
313  return QgsInterval( seconds );
314 }
315 
316 QgsInterval operator-( QTime time1, QTime time2 )
317 {
318  const qint64 mSeconds = time2.msecsTo( time1 );
319  return QgsInterval( mSeconds / 1000.0 );
320 }
QgsInterval::seconds
double seconds() const
Returns the interval duration in seconds.
Definition: qgsinterval.h:236
QgsInterval::setYears
void setYears(double years)
Sets the interval duration in years.
Definition: qgsinterval.cpp:112
qgsinterval.h
QgsUnitTypes::TemporalYears
@ TemporalYears
Years.
Definition: qgsunittypes.h:158
QgsUnitTypes::TemporalDays
@ TemporalDays
Days.
Definition: qgsunittypes.h:155
QgsUnitTypes::TemporalUnknownUnit
@ TemporalUnknownUnit
Unknown time unit.
Definition: qgsunittypes.h:162
QgsInterval::months
double months() const
Returns the interval duration in months (based on a 30 day month).
Definition: qgsinterval.cpp:120
QgsUnitTypes::TemporalUnit
TemporalUnit
Temporal units.
Definition: qgsunittypes.h:149
QgsInterval::setWeeks
void setWeeks(double weeks)
Sets the interval duration in weeks.
Definition: qgsinterval.cpp:145
QgsInterval::MONTHS
static const int MONTHS
Seconds per month, based on 30 day month.
Definition: qgsinterval.h:50
QgsInterval::setHours
void setHours(double hours)
Sets the interval duration in hours.
Definition: qgsinterval.cpp:179
QgsUnitTypes::TemporalSeconds
@ TemporalSeconds
Seconds.
Definition: qgsunittypes.h:152
QgsUnitTypes::TemporalMonths
@ TemporalMonths
Months.
Definition: qgsunittypes.h:157
QgsInterval::fromString
static QgsInterval fromString(const QString &string)
Converts a string to an interval.
Definition: qgsinterval.cpp:211
QgsInterval::setMonths
void setMonths(double months)
Sets the interval duration in months.
Definition: qgsinterval.cpp:128
QgsInterval::DAY
static const int DAY
Seconds per day.
Definition: qgsinterval.h:54
QgsInterval::HOUR
static const int HOUR
Seconds per hour.
Definition: qgsinterval.h:56
QgsUnitTypes
Helper functions for various unit types.
Definition: qgsunittypes.h:38
QgsInterval::MINUTE
static const int MINUTE
Seconds per minute.
Definition: qgsinterval.h:58
QgsInterval::setDays
void setDays(double days)
Sets the interval duration in days.
Definition: qgsinterval.cpp:162
QgsInterval::WEEKS
static const int WEEKS
Seconds per week.
Definition: qgsinterval.h:52
QgsInterval::minutes
double minutes() const
Returns the interval duration in minutes.
Definition: qgsinterval.cpp:187
QgsUnitTypes::TemporalHours
@ TemporalHours
Hours.
Definition: qgsunittypes.h:154
QgsInterval::setSeconds
void setSeconds(double seconds)
Sets the interval duration in seconds.
Definition: qgsinterval.cpp:203
QgsInterval::weeks
double weeks() const
Returns the interval duration in weeks.
Definition: qgsinterval.cpp:136
QgsInterval::QgsInterval
QgsInterval()=default
Default constructor for QgsInterval.
QgsUnitTypes::TemporalMinutes
@ TemporalMinutes
Minutes.
Definition: qgsunittypes.h:153
QgsUnitTypes::TemporalWeeks
@ TemporalWeeks
Weeks.
Definition: qgsunittypes.h:156
operator-
QgsInterval operator-(const QDateTime &dt1, const QDateTime &dt2)
Returns the interval between two datetimes.
Definition: qgsinterval.cpp:299
QgsInterval
A representation of the interval between two datetime values.
Definition: qgsinterval.h:41
QgsInterval::years
double years() const
Returns the interval duration in years (based on an average year length)
Definition: qgsinterval.cpp:104
operator<<
QDebug operator<<(QDebug dbg, const QgsInterval &interval)
Debug string representation of interval.
Definition: qgsinterval.cpp:290
operator+
QDateTime operator+(const QDateTime &start, const QgsInterval &interval)
Adds an interval to a datetime.
Definition: qgsinterval.cpp:305
QgsInterval::days
double days() const
Returns the interval duration in days.
Definition: qgsinterval.cpp:153
QgsInterval::hours
double hours() const
Returns the interval duration in hours.
Definition: qgsinterval.cpp:170
QgsInterval::isValid
bool isValid() const
Returns true if the interval is valid.
Definition: qgsinterval.h:255
QgsInterval::setMinutes
void setMinutes(double minutes)
Sets the interval duration in minutes.
Definition: qgsinterval.cpp:195
QgsInterval::YEARS
static const int YEARS
Seconds per year (average)
Definition: qgsinterval.h:48