Skip to content

Commit a59ebdd

Browse files
mgmarinoJulianWgs
authored andcommitted
BUG: Fix parsing of ISO8601 durations (pandas-dev#37159)
1 parent 4eb8878 commit a59ebdd

File tree

3 files changed

+22
-18
lines changed

3 files changed

+22
-18
lines changed

doc/source/whatsnew/v1.2.0.rst

+1-2
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,7 @@ Datetimelike
348348
Timedelta
349349
^^^^^^^^^
350350
- Bug in :class:`TimedeltaIndex`, :class:`Series`, and :class:`DataFrame` floor-division with ``timedelta64`` dtypes and ``NaT`` in the denominator (:issue:`35529`)
351-
-
352-
-
351+
- Bug in parsing of ISO 8601 durations in :class:`Timedelta`, :meth:`pd.to_datetime` (:issue:`37159`, fixes :issue:`29773` and :issue:`36204`)
353352

354353
Timezones
355354
^^^^^^^^^

pandas/_libs/tslibs/timedeltas.pyx

+13-15
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ cdef inline int64_t parse_iso_format_string(str ts) except? -1:
604604

605605
for c in ts:
606606
# number (ascii codes)
607-
if ord(c) >= 48 and ord(c) <= 57:
607+
if 48 <= ord(c) <= 57:
608608

609609
have_value = 1
610610
if have_dot:
@@ -620,27 +620,28 @@ cdef inline int64_t parse_iso_format_string(str ts) except? -1:
620620
if not len(unit):
621621
number.append(c)
622622
else:
623-
# if in days, pop trailing T
624-
if unit[-1] == 'T':
625-
unit.pop()
626-
elif 'H' in unit or 'M' in unit:
627-
if len(number) > 2:
628-
raise ValueError(err_msg)
629623
r = timedelta_from_spec(number, '0', unit)
630624
result += timedelta_as_neg(r, neg)
631625

632626
neg = 0
633627
unit, number = [], [c]
634628
else:
635-
if c == 'P':
636-
pass # ignore leading character
629+
if c == 'P' or c == 'T':
630+
pass # ignore marking characters P and T
637631
elif c == '-':
638632
if neg or have_value:
639633
raise ValueError(err_msg)
640634
else:
641635
neg = 1
642-
elif c in ['D', 'T', 'H', 'M']:
636+
elif c in ['W', 'D', 'H', 'M']:
643637
unit.append(c)
638+
if c in ['H', 'M'] and len(number) > 2:
639+
raise ValueError(err_msg)
640+
r = timedelta_from_spec(number, '0', unit)
641+
result += timedelta_as_neg(r, neg)
642+
643+
neg = 0
644+
unit, number = [], []
644645
elif c == '.':
645646
# append any seconds
646647
if len(number):
@@ -661,11 +662,8 @@ cdef inline int64_t parse_iso_format_string(str ts) except? -1:
661662
r = timedelta_from_spec(number, '0', dec_unit)
662663
result += timedelta_as_neg(r, neg)
663664
else: # seconds
664-
if len(number) <= 2:
665-
r = timedelta_from_spec(number, '0', 'S')
666-
result += timedelta_as_neg(r, neg)
667-
else:
668-
raise ValueError(err_msg)
665+
r = timedelta_from_spec(number, '0', 'S')
666+
result += timedelta_as_neg(r, neg)
669667
else:
670668
raise ValueError(err_msg)
671669

pandas/tests/scalar/timedelta/test_constructors.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,14 @@ def test_overflow_on_construction():
228228
("P0DT0H0M0.001S", Timedelta(milliseconds=1)),
229229
("P0DT0H1M0S", Timedelta(minutes=1)),
230230
("P1DT25H61M61S", Timedelta(days=1, hours=25, minutes=61, seconds=61)),
231+
("PT1S", Timedelta(seconds=1)),
232+
("PT0S", Timedelta(seconds=0)),
233+
("P1WT0S", Timedelta(days=7, seconds=0)),
234+
("P1D", Timedelta(days=1)),
235+
("P1DT1H", Timedelta(days=1, hours=1)),
236+
("P1W", Timedelta(days=7)),
237+
("PT300S", Timedelta(seconds=300)),
238+
("P1DT0H0M00000000000S", Timedelta(days=1)),
231239
],
232240
)
233241
def test_iso_constructor(fmt, exp):
@@ -241,7 +249,6 @@ def test_iso_constructor(fmt, exp):
241249
"PDTHMS",
242250
"P0DT999H999M999S",
243251
"P1DT0H0M0.0000000000000S",
244-
"P1DT0H0M00000000000S",
245252
"P1DT0H0M0.S",
246253
],
247254
)

0 commit comments

Comments
 (0)