mirror of
https://github.com/django/django.git
synced 2025-06-05 11:39:13 +00:00
Fixed #28076 -- Added support for PostgreSQL's interval format to parse_duration().
This commit is contained in:
parent
684c0a35f6
commit
493f7e9e1e
@ -50,6 +50,20 @@ iso8601_duration_re = re.compile(
|
|||||||
r'$'
|
r'$'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Support PostgreSQL's day-time interval format, e.g. "3 days 04:05:06". The
|
||||||
|
# year-month and mixed intervals cannot be converted to a timedelta and thus
|
||||||
|
# aren't accepted.
|
||||||
|
postgres_interval_re = re.compile(
|
||||||
|
r'^'
|
||||||
|
r'(?:(?P<days>-?\d+) (days? ?))?'
|
||||||
|
r'(?:(?P<sign>[-+])?'
|
||||||
|
r'(?P<hours>\d+):'
|
||||||
|
r'(?P<minutes>\d\d):'
|
||||||
|
r'(?P<seconds>\d\d)'
|
||||||
|
r'(?:\.(?P<microseconds>\d{1,6}))?'
|
||||||
|
r')?$'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_date(value):
|
def parse_date(value):
|
||||||
"""Parse a string and return a datetime.date.
|
"""Parse a string and return a datetime.date.
|
||||||
@ -114,17 +128,19 @@ def parse_duration(value):
|
|||||||
|
|
||||||
The preferred format for durations in Django is '%d %H:%M:%S.%f'.
|
The preferred format for durations in Django is '%d %H:%M:%S.%f'.
|
||||||
|
|
||||||
Also supports ISO 8601 representation.
|
Also supports ISO 8601 representation and PostgreSQL's day-time interval
|
||||||
|
format.
|
||||||
"""
|
"""
|
||||||
match = standard_duration_re.match(value)
|
match = standard_duration_re.match(value)
|
||||||
if not match:
|
if not match:
|
||||||
match = iso8601_duration_re.match(value)
|
match = iso8601_duration_re.match(value) or postgres_interval_re.match(value)
|
||||||
if match:
|
if match:
|
||||||
kw = match.groupdict()
|
kw = match.groupdict()
|
||||||
|
days = datetime.timedelta(float(kw.pop('days', 0) or 0))
|
||||||
sign = -1 if kw.pop('sign', '+') == '-' else 1
|
sign = -1 if kw.pop('sign', '+') == '-' else 1
|
||||||
if kw.get('microseconds'):
|
if kw.get('microseconds'):
|
||||||
kw['microseconds'] = kw['microseconds'].ljust(6, '0')
|
kw['microseconds'] = kw['microseconds'].ljust(6, '0')
|
||||||
if kw.get('seconds') and kw.get('microseconds') and kw['seconds'].startswith('-'):
|
if kw.get('seconds') and kw.get('microseconds') and kw['seconds'].startswith('-'):
|
||||||
kw['microseconds'] = '-' + kw['microseconds']
|
kw['microseconds'] = '-' + kw['microseconds']
|
||||||
kw = {k: float(v) for k, v in kw.items() if v is not None}
|
kw = {k: float(v) for k, v in kw.items() if v is not None}
|
||||||
return sign * datetime.timedelta(**kw)
|
return days + sign * datetime.timedelta(**kw)
|
||||||
|
@ -143,7 +143,12 @@ The functions defined in this module share the following properties:
|
|||||||
Parses a string and returns a :class:`datetime.timedelta`.
|
Parses a string and returns a :class:`datetime.timedelta`.
|
||||||
|
|
||||||
Expects data in the format ``"DD HH:MM:SS.uuuuuu"`` or as specified by ISO
|
Expects data in the format ``"DD HH:MM:SS.uuuuuu"`` or as specified by ISO
|
||||||
8601 (e.g. ``P4DT1H15M20S`` which is equivalent to ``4 1:15:20``).
|
8601 (e.g. ``P4DT1H15M20S`` which is equivalent to ``4 1:15:20``) or
|
||||||
|
PostgreSQL's day-time interval format (e.g. ``3 days 04:05:06``).
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0
|
||||||
|
|
||||||
|
Support for PostgreSQL's interval format was added.
|
||||||
|
|
||||||
``django.utils.decorators``
|
``django.utils.decorators``
|
||||||
===========================
|
===========================
|
||||||
|
@ -65,6 +65,21 @@ class DurationParseTests(unittest.TestCase):
|
|||||||
with self.subTest(delta=delta):
|
with self.subTest(delta=delta):
|
||||||
self.assertEqual(parse_duration(format(delta)), delta)
|
self.assertEqual(parse_duration(format(delta)), delta)
|
||||||
|
|
||||||
|
def test_parse_postgresql_format(self):
|
||||||
|
test_values = (
|
||||||
|
('1 day', timedelta(1)),
|
||||||
|
('1 day 0:00:01', timedelta(days=1, seconds=1)),
|
||||||
|
('1 day -0:00:01', timedelta(days=1, seconds=-1)),
|
||||||
|
('-1 day -0:00:01', timedelta(days=-1, seconds=-1)),
|
||||||
|
('-1 day +0:00:01', timedelta(days=-1, seconds=1)),
|
||||||
|
('4 days 0:15:30.1', timedelta(days=4, minutes=15, seconds=30, milliseconds=100)),
|
||||||
|
('4 days 0:15:30.0001', timedelta(days=4, minutes=15, seconds=30, microseconds=100)),
|
||||||
|
('-4 days -15:00:30', timedelta(days=-4, hours=-15, seconds=-30)),
|
||||||
|
)
|
||||||
|
for source, expected in test_values:
|
||||||
|
with self.subTest(source=source):
|
||||||
|
self.assertEqual(parse_duration(source), expected)
|
||||||
|
|
||||||
def test_seconds(self):
|
def test_seconds(self):
|
||||||
self.assertEqual(parse_duration('30'), timedelta(seconds=30))
|
self.assertEqual(parse_duration('30'), timedelta(seconds=30))
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user