Skip to content

Commit d40fb54

Browse files
mingglijreback
authored andcommitted
ENH: ISO8601-compliant datetime string conversion in iterrows() and Series construction. (#19762)
1 parent 8f1dfa7 commit d40fb54

File tree

5 files changed

+33
-9
lines changed

5 files changed

+33
-9
lines changed

doc/source/whatsnew/v0.23.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,6 @@ Datetimelike
766766
- Bug in :func:`to_datetime` where passing an out-of-bounds datetime with ``errors='coerce'`` and ``utc=True`` would raise ``OutOfBoundsDatetime`` instead of parsing to ``NaT`` (:issue:`19612`)
767767
- Bug in :class:`DatetimeIndex` and :class:`TimedeltaIndex` addition and subtraction where name of the returned object was not always set consistently. (:issue:`19744`)
768768
- Bug in :class:`DatetimeIndex` and :class:`TimedeltaIndex` addition and subtraction where operations with numpy arrays raised ``TypeError`` (:issue:`19847`)
769-
-
770769

771770
Timedelta
772771
^^^^^^^^^
@@ -918,6 +917,7 @@ Reshaping
918917
- :func:`Series.rename` now accepts ``axis`` as a kwarg (:issue:`18589`)
919918
- Comparisons between :class:`Series` and :class:`Index` would return a ``Series`` with an incorrect name, ignoring the ``Index``'s name attribute (:issue:`19582`)
920919
- Bug in :func:`qcut` where datetime and timedelta data with ``NaT`` present raised a ``ValueError`` (:issue:`19768`)
920+
- Bug in :func:`DataFrame.iterrows`, which would infers strings not compliant to `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ to datetimes (:issue:`19671`)
921921

922922
Other
923923
^^^^^

pandas/core/dtypes/cast.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -904,16 +904,23 @@ def maybe_infer_to_datetimelike(value, convert_dates=False):
904904
def try_datetime(v):
905905
# safe coerce to datetime64
906906
try:
907-
v = tslib.array_to_datetime(v, errors='raise')
907+
# GH19671
908+
v = tslib.array_to_datetime(v,
909+
require_iso8601=True,
910+
errors='raise')
908911
except ValueError:
909912

910913
# we might have a sequence of the same-datetimes with tz's
911914
# if so coerce to a DatetimeIndex; if they are not the same,
912-
# then these stay as object dtype
915+
# then these stay as object dtype, xref GH19671
913916
try:
914-
from pandas import to_datetime
915-
return to_datetime(v)
916-
except Exception:
917+
from pandas._libs.tslibs import conversion
918+
from pandas import DatetimeIndex
919+
920+
values, tz = conversion.datetime_to_datetime64(v)
921+
return DatetimeIndex(values).tz_localize(
922+
'UTC').tz_convert(tz=tz)
923+
except (ValueError, TypeError):
917924
pass
918925

919926
except Exception:

pandas/core/internals.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2602,8 +2602,8 @@ def _maybe_coerce_values(self, values):
26022602
"""Input validation for values passed to __init__. Ensure that
26032603
we have datetime64ns, coercing if necessary.
26042604
2605-
Parametetrs
2606-
-----------
2605+
Parameters
2606+
----------
26072607
values : array-like
26082608
Must be convertible to datetime64
26092609

pandas/tests/dtypes/test_cast.py

+4
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ def test_maybe_infer_to_datetimelike(self):
301301
[NaT, 'b', 1]]))
302302
assert result.size == 6
303303

304+
# GH19671
305+
result = Series(['M1701', Timestamp('20130101')])
306+
assert result.dtype.kind == 'O'
307+
304308

305309
class TestConvert(object):
306310

pandas/tests/frame/test_api.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
from numpy.random import randn
1616
import numpy as np
1717

18-
from pandas import DataFrame, Series, date_range, timedelta_range, Categorical
18+
from pandas import (DataFrame, Series, date_range, timedelta_range,
19+
Categorical, SparseDataFrame)
1920
import pandas as pd
2021

2122
from pandas.util.testing import (assert_almost_equal,
@@ -214,6 +215,18 @@ def test_iterrows(self):
214215
exp = self.mixed_frame.loc[k]
215216
self._assert_series_equal(v, exp)
216217

218+
def test_iterrows_iso8601(self):
219+
# GH19671
220+
if self.klass == SparseDataFrame:
221+
pytest.xfail(reason='SparseBlock datetime type not implemented.')
222+
223+
s = self.klass(
224+
{'non_iso8601': ['M1701', 'M1802', 'M1903', 'M2004'],
225+
'iso8601': date_range('2000-01-01', periods=4, freq='M')})
226+
for k, v in s.iterrows():
227+
exp = s.loc[k]
228+
self._assert_series_equal(v, exp)
229+
217230
def test_itertuples(self):
218231
for i, tup in enumerate(self.frame.itertuples()):
219232
s = self.klass._constructor_sliced(tup[1:])

0 commit comments

Comments
 (0)