Skip to content

Commit 503bad4

Browse files
committed
Allow parse_duration to handle timedelta too
... and parse_duration_or_timestamp
1 parent 36b3315 commit 503bad4

1 file changed

Lines changed: 30 additions & 15 deletions

File tree

structa/conversions.py

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# SPDX-License-Identifier: GPL-2.0-or-later
66

77
import re
8+
from datetime import timedelta
89

910
from dateutil.parser import parse
1011
from dateutil.relativedelta import relativedelta
@@ -84,18 +85,22 @@ def parse_bool(s, false='0', true='1'):
8485
}
8586

8687

87-
def parse_duration(s):
88+
def parse_duration(s, delta_type=relativedelta):
8889
"""
89-
Convert the string *s* to a :class:`~dateutil.relativedelta.relativedelta`.
90-
The string must consist of white-space and/or comma separated values which
91-
are a number followed by a suffix indicating duration. For example:
90+
Convert the string *s* to a :class:`~dateutil.relativedelta.relativedelta`
91+
(by default) or a :class:`~datetime.timedelta` if requested by
92+
*delta_type*. The string must consist of white-space and/or comma separated
93+
values which are a number followed by a suffix indicating duration. For
94+
example:
9295
9396
>>> parse_duration('1s')
9497
relativedelta(seconds=+1)
9598
>>> parse_duration('5 minutes, 30 seconds')
9699
relativedelta(minutes=+5, seconds=+30)
97100
>>> parse_duration('1 year')
98101
relativedelta(years=+1)
102+
>>> parse_duration('1 week, 1 day', delta_type=datetime.timedelta)
103+
timedelta(days=8)
99104
100105
Note that some suffixes like "m" can be ambiguous; using common
101106
abbreviations should avoid ambiguity:
@@ -112,6 +117,9 @@ def parse_duration(s):
112117
* *Microseconds*: microseconds, microsecond, microsec, micros, micro,
113118
useconds, usecond, usecs, usec, us, µseconds, µsecond, µsecs, µsec, µs
114119
120+
* *Milliseconds*: milliseconds, millisecond, millisec, millis, milli,
121+
mseconds, msecond, msecs, msec, ms
122+
115123
* *Seconds*: seconds, second, secs, sec, s
116124
117125
* *Minutes*: minutes, minute, mins, min, mi
@@ -122,25 +130,31 @@ def parse_duration(s):
122130
123131
* *Weeks*: weeks, week, wks, wk, w
124132
125-
* *Months*: months, month, mons, mon, mths, mth, m
133+
* *Months*: months, month, mons, mon, mths, mth, m (relativedelta only)
126134
127-
* *Years*: years, year, yrs, yr, y
135+
* *Years*: years, year, yrs, yr, y (relativedelta only)
128136
129137
If conversion fails, :exc:`ValueError` is raised.
130138
"""
131-
spans = {span: 0 for span in _SPANS}
139+
assert delta_type in (relativedelta, timedelta)
140+
spans = {}
132141
t = s
133142
for span, regex in _SPANS.items():
143+
if delta_type is timedelta and span in ('months', 'years'):
144+
continue
134145
m = regex.search(t)
135146
if m:
136-
spans[span] += int(m.group('num'))
147+
spans[span] = spans.get(span, 0) + int(m.group('num'))
137148
t = (t[:m.start(0)] + t[m.end(0):]).strip(' \t\n,')
138149
if not t:
139150
break
140151
if t:
141152
raise ValueError('invalid duration {}'.format(s))
142-
spans['microseconds'] += spans.pop('milliseconds') * 1000
143-
return relativedelta(**spans)
153+
if delta_type is relativedelta:
154+
spans['microseconds'] = (
155+
spans.get('microseconds', 0) +
156+
(spans.pop('milliseconds', 0) * 1000))
157+
return delta_type(**spans)
144158

145159

146160
def parse_timestamp(s):
@@ -151,14 +165,15 @@ def parse_timestamp(s):
151165
return parse(s)
152166

153167

154-
def parse_duration_or_timestamp(s):
168+
def parse_duration_or_timestamp(s, duration_type=relativedelta):
155169
"""
156170
Convert the string *s* to a :class:`~datetime.datetime` or a
157-
:class:`~dateutil.relativedelta.relativedelta`. Duration conversion is
158-
attempted to and, if this fails, date-time conversion is attempted. A
159-
:exc:`ValueError` is raised if both conversions fail.
171+
:class:`~dateutil.relativedelta.relativedelta` (or
172+
:class:`~datetime.timedelta` if *duration_type* so specifies). Duration
173+
conversion is attempted to and, if this fails, date-time conversion is
174+
attempted. A :exc:`ValueError` is raised if both conversions fail.
160175
"""
161176
try:
162-
return parse_duration(s)
177+
return parse_duration(s, duration_type=duration_type)
163178
except ValueError:
164179
return parse_timestamp(s)

0 commit comments

Comments
 (0)