55# SPDX-License-Identifier: GPL-2.0-or-later
66
77import re
8+ from datetime import timedelta
89
910from dateutil .parser import parse
1011from 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
146160def 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