Skip to content

Commit e9e259a

Browse files
jbrockmendelPingviinituutti
authored andcommitted
BUG: fix to_datetime failing to raise on mixed tznaive/tzaware datetimes (pandas-dev#24663)
1 parent f55db89 commit e9e259a

File tree

5 files changed

+28
-17
lines changed

5 files changed

+28
-17
lines changed

doc/source/whatsnew/v0.24.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,7 @@ Datetimelike
15511551
- Bug in :class:`PeriodIndex` where comparisons against an array-like object with length 1 failed to raise ``ValueError`` (:issue:`23078`)
15521552
- Bug in :meth:`DatetimeIndex.astype`, :meth:`PeriodIndex.astype` and :meth:`TimedeltaIndex.astype` ignoring the sign of the ``dtype`` for unsigned integer dtypes (:issue:`24405`).
15531553
- Fixed bug in :meth:`Series.max` with ``datetime64[ns]``-dtype failing to return ``NaT`` when nulls are present and ``skipna=False`` is passed (:issue:`24265`)
1554+
- Bug in :func:`to_datetime` where arrays of ``datetime`` objects containing both timezone-aware and timezone-naive ``datetimes`` would fail to raise ``ValueError`` (:issue:`24569`)
15541555

15551556
Timedelta
15561557
^^^^^^^^^

pandas/_libs/tslibs/conversion.pyx

+5
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ def datetime_to_datetime64(values: object[:]):
167167
int64_t[:] iresult
168168
npy_datetimestruct dts
169169
_TSObject _ts
170+
bint found_naive = False
170171

171172
result = np.empty(n, dtype='M8[ns]')
172173
iresult = result.view('i8')
@@ -176,6 +177,9 @@ def datetime_to_datetime64(values: object[:]):
176177
iresult[i] = NPY_NAT
177178
elif PyDateTime_Check(val):
178179
if val.tzinfo is not None:
180+
if found_naive:
181+
raise ValueError('Cannot mix tz-aware with '
182+
'tz-naive values')
179183
if inferred_tz is not None:
180184
if not tz_compare(val.tzinfo, inferred_tz):
181185
raise ValueError('Array must be all same time zone')
@@ -186,6 +190,7 @@ def datetime_to_datetime64(values: object[:]):
186190
iresult[i] = _ts.value
187191
check_dts_bounds(&_ts.dts)
188192
else:
193+
found_naive = True
189194
if inferred_tz is not None:
190195
raise ValueError('Cannot mix tz-aware with '
191196
'tz-naive values')

pandas/tests/arrays/test_array.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,7 @@ def test_array_inference(data, expected):
179179
[pd.Timestamp("2000", tz="CET"), pd.Timestamp("2000", tz="UTC")],
180180
# Mix of tz-aware and tz-naive
181181
[pd.Timestamp("2000", tz="CET"), pd.Timestamp("2000")],
182-
# GH-24569
183-
pytest.param(
184-
np.array([pd.Timestamp('2000'), pd.Timestamp('2000', tz='CET')]),
185-
marks=pytest.mark.xfail(reason="bug in DTA._from_sequence")
186-
),
182+
np.array([pd.Timestamp('2000'), pd.Timestamp('2000', tz='CET')]),
187183
])
188184
def test_array_inference_fails(data):
189185
result = pd.array(data)

pandas/tests/arrays/test_datetimes.py

+18
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@
1616

1717

1818
class TestDatetimeArrayConstructor(object):
19+
@pytest.mark.parametrize('meth', [DatetimeArray._from_sequence,
20+
sequence_to_dt64ns,
21+
pd.to_datetime,
22+
pd.DatetimeIndex])
23+
def test_mixing_naive_tzaware_raises(self, meth):
24+
# GH#24569
25+
arr = np.array([pd.Timestamp('2000'), pd.Timestamp('2000', tz='CET')])
26+
27+
msg = ('Cannot mix tz-aware with tz-naive values|'
28+
'Tz-aware datetime.datetime cannot be converted '
29+
'to datetime64 unless utc=True')
30+
31+
for obj in [arr, arr[::-1]]:
32+
# check that we raise regardless of whether naive is found
33+
# before aware or vice-versa
34+
with pytest.raises(ValueError, match=msg):
35+
meth(obj)
36+
1937
def test_from_pandas_array(self):
2038
arr = pd.array(np.arange(5, dtype=np.int64)) * 3600 * 10**9
2139

pandas/tests/indexes/datetimes/test_construction.py

+3-12
Original file line numberDiff line numberDiff line change
@@ -306,24 +306,15 @@ def test_construction_dti_with_mixed_timezones(self):
306306
tm.assert_index_equal(result, exp, exact=True)
307307
assert isinstance(result, DatetimeIndex)
308308

309-
# different tz coerces tz-naive to tz-awareIndex(dtype=object)
310-
result = DatetimeIndex([Timestamp('2011-01-01 10:00'),
311-
Timestamp('2011-01-02 10:00',
312-
tz='US/Eastern')], name='idx')
313-
exp = DatetimeIndex([Timestamp('2011-01-01 05:00'),
314-
Timestamp('2011-01-02 10:00')],
315-
tz='US/Eastern', name='idx')
316-
tm.assert_index_equal(result, exp, exact=True)
317-
assert isinstance(result, DatetimeIndex)
318-
319309
# tz mismatch affecting to tz-aware raises TypeError/ValueError
320310

321311
with pytest.raises(ValueError):
322312
DatetimeIndex([Timestamp('2011-01-01 10:00', tz='Asia/Tokyo'),
323313
Timestamp('2011-01-02 10:00', tz='US/Eastern')],
324314
name='idx')
325315

326-
with pytest.raises(TypeError, match='data is already tz-aware'):
316+
msg = 'cannot be converted to datetime64'
317+
with pytest.raises(ValueError, match=msg):
327318
DatetimeIndex([Timestamp('2011-01-01 10:00'),
328319
Timestamp('2011-01-02 10:00', tz='US/Eastern')],
329320
tz='Asia/Tokyo', name='idx')
@@ -333,7 +324,7 @@ def test_construction_dti_with_mixed_timezones(self):
333324
Timestamp('2011-01-02 10:00', tz='US/Eastern')],
334325
tz='US/Eastern', name='idx')
335326

336-
with pytest.raises(TypeError, match='data is already tz-aware'):
327+
with pytest.raises(ValueError, match=msg):
337328
# passing tz should results in DatetimeIndex, then mismatch raises
338329
# TypeError
339330
Index([pd.NaT, Timestamp('2011-01-01 10:00'),

0 commit comments

Comments
 (0)