mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-04 00:06:46 -05:00
Add some methods to parse ISO8601 duration strings
(eg '2021-03-23T00:00:00Z/2021-03-24T12:00:00Z/PT12H') to a list of datetimes
This commit is contained in:
parent
1a7fe6ae55
commit
1bb042bc78
@ -10,6 +10,8 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsTemporalUtils
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
@ -100,6 +102,37 @@ the number of days in the current month.
|
||||
|
||||
.. versionadded:: 3.18
|
||||
%End
|
||||
|
||||
static QList< QDateTime > calculateDateTimesUsingDuration( const QDateTime &start, const QDateTime &end, const QString &duration, bool &ok /Out/, bool &maxValuesExceeded /Out/, int maxValues = -1 );
|
||||
%Docstring
|
||||
Calculates a complete list of datetimes between ``start`` and ``end``, using the specified ISO8601 ``duration`` string (eg "PT12H").
|
||||
|
||||
:param start: start date time
|
||||
:param end: end date time
|
||||
:param duration: ISO8601 duration string
|
||||
:param maxValues: maximum number of values to return, or -1 to return all values
|
||||
|
||||
:return: - calculated list of date times
|
||||
- ok: will be set to ``True`` if ``duration`` was successfully parsed and date times could be calculated
|
||||
- maxValuesExceeded: will be set to ``True`` if the maximum number of values to return was exceeded
|
||||
|
||||
.. versionadded:: 3.20
|
||||
%End
|
||||
|
||||
static QList< QDateTime > calculateDateTimesFromISO8601( const QString &string, bool &ok /Out/, bool &maxValuesExceeded /Out/, int maxValues = -1 );
|
||||
%Docstring
|
||||
Calculates a complete list of datetimes from a ISO8601 ``string`` containing a duration (eg "2021-03-23T00:00:00Z/2021-03-24T12:00:00Z/PT12H").
|
||||
|
||||
:param string: ISO8601 compatible string
|
||||
:param maxValues: maximum number of values to return, or -1 to return all values
|
||||
|
||||
:return: - calculated list of date times
|
||||
- ok: will be set to ``True`` if ``string`` was successfully parsed and date times could be calculated
|
||||
- maxValuesExceeded: will be set to ``True`` if the maximum number of values to return was exceeded
|
||||
|
||||
.. versionadded:: 3.20
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -209,3 +209,86 @@ QDateTime QgsTemporalUtils::calculateFrameTime( const QDateTime &start, const lo
|
||||
}
|
||||
}
|
||||
|
||||
QList<QDateTime> QgsTemporalUtils::calculateDateTimesUsingDuration( const QDateTime &start, const QDateTime &end, const QString &duration, bool &ok, bool &maxValuesExceeded, int maxValues )
|
||||
{
|
||||
ok = false;
|
||||
const QgsTimeDuration timeDuration( QgsTimeDuration::fromString( duration, ok ) );
|
||||
if ( !ok )
|
||||
return {};
|
||||
|
||||
if ( timeDuration.years == 0 && timeDuration.months == 0 && timeDuration.weeks == 0 && timeDuration.days == 0
|
||||
&& timeDuration.hours == 0 && timeDuration.minutes == 0 && timeDuration.seconds == 0 )
|
||||
{
|
||||
ok = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<QDateTime> res;
|
||||
QDateTime current = start;
|
||||
maxValuesExceeded = false;
|
||||
while ( current <= end )
|
||||
{
|
||||
res << current;
|
||||
|
||||
if ( maxValues >= 0 && res.size() > maxValues )
|
||||
{
|
||||
maxValuesExceeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( timeDuration.years )
|
||||
current = current.addYears( timeDuration.years );
|
||||
if ( timeDuration.months )
|
||||
current = current.addMonths( timeDuration.months );
|
||||
if ( timeDuration.weeks || timeDuration.days )
|
||||
current = current.addDays( timeDuration.weeks * 7 + timeDuration.days );
|
||||
if ( timeDuration.hours || timeDuration.minutes || timeDuration.seconds )
|
||||
current = current.addSecs( timeDuration.hours * 60LL * 60 + timeDuration.minutes * 60 + timeDuration.seconds );
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QList<QDateTime> QgsTemporalUtils::calculateDateTimesFromISO8601( const QString &string, bool &ok, bool &maxValuesExceeded, int maxValues )
|
||||
{
|
||||
ok = false;
|
||||
maxValuesExceeded = false;
|
||||
const QStringList parts = string.split( '/' );
|
||||
if ( parts.length() != 3 )
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const QDateTime start = QDateTime::fromString( parts.at( 0 ), Qt::ISODate );
|
||||
if ( !start.isValid() )
|
||||
return {};
|
||||
const QDateTime end = QDateTime::fromString( parts.at( 1 ), Qt::ISODate );
|
||||
if ( !end.isValid() )
|
||||
return {};
|
||||
|
||||
return calculateDateTimesUsingDuration( start, end, parts.at( 2 ), ok, maxValuesExceeded, maxValues );
|
||||
}
|
||||
|
||||
//
|
||||
// QgsTimeDuration
|
||||
//
|
||||
|
||||
QgsTimeDuration QgsTimeDuration::fromString( const QString &string, bool &ok )
|
||||
{
|
||||
ok = false;
|
||||
thread_local QRegularExpression sRx( QStringLiteral( R"(P(?:([\d]+)Y)?(?:([\d]+)M)?(?:([\d]+)W)?(?:([\d]+)D)?(?:T(?:([\d]+)H)?(?:([\d]+)M)?(?:([\d\.]+)S)?)?$)" ) );
|
||||
|
||||
const QRegularExpressionMatch match = sRx.match( string );
|
||||
QgsTimeDuration duration;
|
||||
if ( match.hasMatch() )
|
||||
{
|
||||
ok = true;
|
||||
duration.years = match.capturedView( 1 ).toInt();
|
||||
duration.months = match.capturedView( 2 ).toInt();
|
||||
duration.weeks = match.capturedView( 3 ).toInt();
|
||||
duration.days = match.capturedView( 4 ).toInt();
|
||||
duration.hours = match.capturedView( 5 ).toInt();
|
||||
duration.minutes = match.capturedView( 6 ).toInt();
|
||||
duration.seconds = match.capturedView( 7 ).toDouble();
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
@ -25,6 +25,46 @@ class QgsMapSettings;
|
||||
class QgsFeedback;
|
||||
class QgsMapDecoration;
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsTimeDuration
|
||||
* \brief Contains utility methods for working with temporal layers and projects.
|
||||
*
|
||||
* Designed for storage of ISO8601 duration values.
|
||||
*
|
||||
* \note Not available in Python bindings
|
||||
* \since QGIS 3.20
|
||||
*/
|
||||
class CORE_EXPORT QgsTimeDuration
|
||||
{
|
||||
public:
|
||||
|
||||
//! Years
|
||||
int years = 0;
|
||||
//! Months
|
||||
int months = 0;
|
||||
//! Weeks
|
||||
int weeks = 0;
|
||||
//! Days
|
||||
int days = 0;
|
||||
//! Hours
|
||||
int hours = 0;
|
||||
//! Minutes
|
||||
int minutes = 0;
|
||||
//! Seconds
|
||||
double seconds = 0;
|
||||
|
||||
/**
|
||||
* Creates a QgsTimeDuration from a \a string value.
|
||||
*/
|
||||
static QgsTimeDuration fromString( const QString &string, bool &ok );
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsTemporalUtils
|
||||
@ -125,6 +165,31 @@ class CORE_EXPORT QgsTemporalUtils
|
||||
* \since QGIS 3.18
|
||||
*/
|
||||
static QDateTime calculateFrameTime( const QDateTime &start, const long long frame, const QgsInterval interval );
|
||||
|
||||
/**
|
||||
* Calculates a complete list of datetimes between \a start and \a end, using the specified ISO8601 \a duration string (eg "PT12H").
|
||||
* \param start start date time
|
||||
* \param end end date time
|
||||
* \param duration ISO8601 duration string
|
||||
* \param ok will be set to TRUE if \a duration was successfully parsed and date times could be calculated
|
||||
* \param maxValuesExceeded will be set to TRUE if the maximum number of values to return was exceeded
|
||||
* \param maxValues maximum number of values to return, or -1 to return all values
|
||||
* \returns calculated list of date times
|
||||
* \since QGIS 3.20
|
||||
*/
|
||||
static QList< QDateTime > calculateDateTimesUsingDuration( const QDateTime &start, const QDateTime &end, const QString &duration, bool &ok SIP_OUT, bool &maxValuesExceeded SIP_OUT, int maxValues = -1 );
|
||||
|
||||
/**
|
||||
* Calculates a complete list of datetimes from a ISO8601 \a string containing a duration (eg "2021-03-23T00:00:00Z/2021-03-24T12:00:00Z/PT12H").
|
||||
* \param string ISO8601 compatible string
|
||||
* \param ok will be set to TRUE if \a string was successfully parsed and date times could be calculated
|
||||
* \param maxValuesExceeded will be set to TRUE if the maximum number of values to return was exceeded
|
||||
* \param maxValues maximum number of values to return, or -1 to return all values
|
||||
* \returns calculated list of date times
|
||||
* \since QGIS 3.20
|
||||
*/
|
||||
static QList< QDateTime > calculateDateTimesFromISO8601( const QString &string, bool &ok SIP_OUT, bool &maxValuesExceeded SIP_OUT, int maxValues = -1 );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -144,6 +144,122 @@ class TestQgsTemporalUtils(unittest.TestCase):
|
||||
QgsInterval(0.2, unit))
|
||||
self.assertEqual(f, expected3[unit])
|
||||
|
||||
def testCalculateDateTimesUsingDuration(self):
|
||||
# invalid duration string
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2021, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 3, 24), QTime(12, 0, 0)), 'xT12H')
|
||||
self.assertFalse(ok)
|
||||
# null duration string
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2021, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 3, 24), QTime(12, 0, 0)), '')
|
||||
self.assertFalse(ok)
|
||||
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2021, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 3, 24), QTime(12, 0, 0)), 'P')
|
||||
self.assertFalse(ok)
|
||||
|
||||
# valid durations
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2021, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 3, 24), QTime(12, 0, 0)), 'PT12H')
|
||||
self.assertEqual(vals, [QDateTime(2021, 3, 23, 0, 0),
|
||||
QDateTime(2021, 3, 23, 12, 0),
|
||||
QDateTime(2021, 3, 24, 0, 0),
|
||||
QDateTime(2021, 3, 24, 12, 0)])
|
||||
self.assertTrue(ok)
|
||||
self.assertFalse(exceeded)
|
||||
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2021, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 3, 24), QTime(12, 0, 0)), 'PT12H', maxValues=2)
|
||||
self.assertEqual(vals, [QDateTime(2021, 3, 23, 0, 0),
|
||||
QDateTime(2021, 3, 23, 12, 0),
|
||||
QDateTime(2021, 3, 24, 0, 0)])
|
||||
self.assertTrue(ok)
|
||||
self.assertTrue(exceeded)
|
||||
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2021, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 3, 24), QTime(12, 0, 0)), 'PT10H2M5S')
|
||||
self.assertEqual(vals, [QDateTime(2021, 3, 23, 0, 0), QDateTime(2021, 3, 23, 10, 2, 5),
|
||||
QDateTime(2021, 3, 23, 20, 4, 10), QDateTime(2021, 3, 24, 6, 6, 15)])
|
||||
self.assertTrue(ok)
|
||||
self.assertFalse(exceeded)
|
||||
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2010, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 5, 24), QTime(12, 0, 0)), 'P2Y')
|
||||
self.assertEqual(vals,
|
||||
[QDateTime(2010, 3, 23, 0, 0), QDateTime(2012, 3, 23, 0, 0), QDateTime(2014, 3, 23, 0, 0),
|
||||
QDateTime(2016, 3, 23, 0, 0), QDateTime(2018, 3, 23, 0, 0), QDateTime(2020, 3, 23, 0, 0)])
|
||||
self.assertTrue(ok)
|
||||
self.assertFalse(exceeded)
|
||||
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2020, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 5, 24), QTime(12, 0, 0)), 'P2M')
|
||||
self.assertEqual(vals,
|
||||
[QDateTime(2020, 3, 23, 0, 0), QDateTime(2020, 5, 23, 0, 0), QDateTime(2020, 7, 23, 0, 0),
|
||||
QDateTime(2020, 9, 23, 0, 0), QDateTime(2020, 11, 23, 0, 0), QDateTime(2021, 1, 23, 0, 0),
|
||||
QDateTime(2021, 3, 23, 0, 0), QDateTime(2021, 5, 23, 0, 0)])
|
||||
self.assertTrue(ok)
|
||||
self.assertFalse(exceeded)
|
||||
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2021, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 5, 24), QTime(12, 0, 0)), 'P2W')
|
||||
self.assertEqual(vals, [QDateTime(2021, 3, 23, 0, 0), QDateTime(2021, 4, 6, 0, 0), QDateTime(2021, 4, 20, 0, 0),
|
||||
QDateTime(2021, 5, 4, 0, 0), QDateTime(2021, 5, 18, 0, 0)])
|
||||
self.assertTrue(ok)
|
||||
self.assertFalse(exceeded)
|
||||
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2021, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 4, 7), QTime(12, 0, 0)), 'P2D')
|
||||
self.assertEqual(vals,
|
||||
[QDateTime(2021, 3, 23, 0, 0), QDateTime(2021, 3, 25, 0, 0), QDateTime(2021, 3, 27, 0, 0),
|
||||
QDateTime(2021, 3, 29, 0, 0), QDateTime(2021, 3, 31, 0, 0), QDateTime(2021, 4, 2, 0, 0),
|
||||
QDateTime(2021, 4, 4, 0, 0), QDateTime(2021, 4, 6, 0, 0)])
|
||||
self.assertTrue(ok)
|
||||
self.assertFalse(exceeded)
|
||||
|
||||
# complex mix
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesUsingDuration(
|
||||
QDateTime(QDate(2010, 3, 23), QTime(0, 0, 0)),
|
||||
QDateTime(QDate(2021, 5, 24), QTime(12, 0, 0)), 'P2Y1M3W4DT5H10M22S')
|
||||
self.assertEqual(vals, [QDateTime(2010, 3, 23, 0, 0), QDateTime(2012, 5, 18, 5, 10, 22),
|
||||
QDateTime(2014, 7, 13, 10, 20, 44), QDateTime(2016, 9, 7, 15, 31, 6),
|
||||
QDateTime(2018, 11, 1, 20, 41, 28), QDateTime(2020, 12, 27, 1, 51, 50)])
|
||||
self.assertTrue(ok)
|
||||
self.assertFalse(exceeded)
|
||||
|
||||
def testCalculateDateTimesFromISO8601(self):
|
||||
# invalid duration string
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesFromISO8601('x')
|
||||
self.assertFalse(ok)
|
||||
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesFromISO8601(
|
||||
'a-03-23T00:00:00Z/2021-03-24T12:00:00Z/PT12H')
|
||||
self.assertFalse(ok)
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesFromISO8601(
|
||||
'2021-03-23T00:00:00Z/b-03-24T12:00:00Z/PT12H')
|
||||
self.assertFalse(ok)
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesFromISO8601(
|
||||
'2021-03-23T00:00:00Z/2021-03-24T12:00:00Z/xc')
|
||||
self.assertFalse(ok)
|
||||
|
||||
vals, ok, exceeded = QgsTemporalUtils.calculateDateTimesFromISO8601(
|
||||
'2021-03-23T00:00:00Z/2021-03-24T12:00:00Z/PT12H')
|
||||
self.assertEqual(vals, [QDateTime(2021, 3, 23, 0, 0, 0, 0, Qt.TimeSpec(1)),
|
||||
QDateTime(2021, 3, 23, 12, 0, 0, 0, Qt.TimeSpec(1)),
|
||||
QDateTime(2021, 3, 24, 0, 0, 0, 0, Qt.TimeSpec(1)),
|
||||
QDateTime(2021, 3, 24, 12, 0, 0, 0, Qt.TimeSpec(1))])
|
||||
self.assertTrue(ok)
|
||||
self.assertFalse(exceeded)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user