Skip to content

Commit b53a1a8

Browse files
jbrockmendeljreback
authored andcommitted
TST: Test cleanup, parametrization for datetime64 arithmetic tests (#23681)
1 parent b92f85c commit b53a1a8

File tree

7 files changed

+607
-569
lines changed

7 files changed

+607
-569
lines changed

pandas/conftest.py

+16
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import importlib
22
import os
33

4+
from dateutil.tz import tzutc
45
import hypothesis
56
from hypothesis import strategies as st
67
import numpy as np
78
import pytest
9+
from pytz import utc
810

911
from pandas.compat import PY3
1012
import pandas.util._test_decorators as td
@@ -243,6 +245,20 @@ def datetime_tz_utc():
243245
return timezone.utc
244246

245247

248+
utc_objs = ['utc', utc, tzutc()]
249+
if PY3:
250+
from datetime import timezone
251+
utc_objs.append(timezone.utc)
252+
253+
254+
@pytest.fixture(params=utc_objs)
255+
def utc_fixture(request):
256+
"""
257+
Fixture to provide variants of UTC timezone strings and tzinfo objects
258+
"""
259+
return request.param
260+
261+
246262
@pytest.fixture(params=['inner', 'outer', 'left', 'right'])
247263
def join_type(request):
248264
"""

pandas/core/arrays/datetimes.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def wrapper(self, other):
110110
# string that cannot be parsed to Timestamp
111111
return ops.invalid_comparison(self, other, op)
112112

113-
result = meth(self, other)
113+
result = op(self.asi8, other.view('i8'))
114114
if isna(other):
115115
result.fill(nat_result)
116116
elif lib.is_scalar(other):
@@ -208,9 +208,6 @@ def _simple_new(cls, values, freq=None, tz=None, **kwargs):
208208
return result
209209

210210
def __new__(cls, values, freq=None, tz=None, dtype=None):
211-
if tz is None and hasattr(values, 'tz'):
212-
# e.g. DatetimeIndex
213-
tz = values.tz
214211

215212
if freq is None and hasattr(values, "freq"):
216213
# i.e. DatetimeArray, DatetimeIndex
@@ -221,9 +218,16 @@ def __new__(cls, values, freq=None, tz=None, dtype=None):
221218
# if dtype has an embedded tz, capture it
222219
tz = dtl.validate_tz_from_dtype(dtype, tz)
223220

221+
if isinstance(values, ABCSeries):
222+
# extract to ndarray or DatetimeIndex
223+
values = values._values
224+
224225
if isinstance(values, DatetimeArrayMixin):
225226
# extract nanosecond unix timestamps
227+
if tz is None:
228+
tz = values.tz
226229
values = values.asi8
230+
227231
if values.dtype == 'i8':
228232
values = values.view('M8[ns]')
229233

pandas/tests/arithmetic/test_datetime64.py

+84-41
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from pandas import (
2626
Timestamp, Timedelta, Period, Series, date_range, NaT,
2727
DatetimeIndex, TimedeltaIndex)
28+
from pandas.core.arrays import DatetimeArrayMixin as DatetimeArray
2829

2930

3031
# ------------------------------------------------------------------
@@ -158,12 +159,16 @@ def test_dt64_ser_cmp_date_warning(self):
158159
assert "a TypeError will be raised" in str(m[0].message)
159160

160161
@pytest.mark.skip(reason="GH#21359")
161-
def test_dt64ser_cmp_date_invalid(self):
162+
def test_dt64ser_cmp_date_invalid(self, box_with_datetime):
162163
# GH#19800 datetime.date comparison raises to
163164
# match DatetimeIndex/Timestamp. This also matches the behavior
164165
# of stdlib datetime.datetime
165-
ser = pd.Series(pd.date_range('20010101', periods=10), name='dates')
166+
box = box_with_datetime
167+
168+
ser = pd.date_range('20010101', periods=10)
166169
date = ser.iloc[0].to_pydatetime().date()
170+
171+
ser = tm.box_expected(ser, box)
167172
assert not (ser == date).any()
168173
assert (ser != date).all()
169174
with pytest.raises(TypeError):
@@ -225,22 +230,37 @@ def test_timestamp_compare_series(self, left, right):
225230
result = right_f(pd.Timestamp("nat"), s_nat)
226231
tm.assert_series_equal(result, expected)
227232

228-
def test_timestamp_equality(self):
233+
def test_dt64arr_timestamp_equality(self, box_with_datetime):
229234
# GH#11034
235+
box = box_with_datetime
236+
xbox = box if box not in [pd.Index, DatetimeArray] else np.ndarray
237+
230238
ser = pd.Series([pd.Timestamp('2000-01-29 01:59:00'), 'NaT'])
239+
ser = tm.box_expected(ser, box)
240+
231241
result = ser != ser
232-
tm.assert_series_equal(result, pd.Series([False, True]))
242+
expected = tm.box_expected([False, True], xbox)
243+
tm.assert_equal(result, expected)
244+
233245
result = ser != ser[0]
234-
tm.assert_series_equal(result, pd.Series([False, True]))
246+
expected = tm.box_expected([False, True], xbox)
247+
tm.assert_equal(result, expected)
248+
235249
result = ser != ser[1]
236-
tm.assert_series_equal(result, pd.Series([True, True]))
250+
expected = tm.box_expected([True, True], xbox)
251+
tm.assert_equal(result, expected)
237252

238253
result = ser == ser
239-
tm.assert_series_equal(result, pd.Series([True, False]))
254+
expected = tm.box_expected([True, False], xbox)
255+
tm.assert_equal(result, expected)
256+
240257
result = ser == ser[0]
241-
tm.assert_series_equal(result, pd.Series([True, False]))
258+
expected = tm.box_expected([True, False], xbox)
259+
tm.assert_equal(result, expected)
260+
242261
result = ser == ser[1]
243-
tm.assert_series_equal(result, pd.Series([False, False]))
262+
expected = tm.box_expected([False, False], xbox)
263+
tm.assert_equal(result, expected)
244264

245265

246266
class TestDatetimeIndexComparisons(object):
@@ -629,7 +649,7 @@ def test_dti_cmp_object_dtype(self):
629649
# Arithmetic
630650

631651
class TestFrameArithmetic(object):
632-
def test_dt64arr_sub_dtscalar(self, box):
652+
def test_dt64arr_sub_timestamp(self, box):
633653
# GH#8554, GH#22163 DataFrame op should _not_ return dt64 dtype
634654
idx = pd.date_range('2013-01-01', periods=3)
635655
idx = tm.box_expected(idx, box)
@@ -643,28 +663,39 @@ def test_dt64arr_sub_dtscalar(self, box):
643663
result = idx - ts
644664
tm.assert_equal(result, expected)
645665

646-
def test_df_sub_datetime64_not_ns(self):
666+
def test_dt64arr_sub_datetime64_not_ns(self, box):
647667
# GH#7996, GH#22163 ensure non-nano datetime64 is converted to nano
648-
df = pd.DataFrame(pd.date_range('20130101', periods=3))
668+
# for DataFrame operation
669+
670+
dti = pd.date_range('20130101', periods=3)
671+
dtarr = tm.box_expected(dti, box)
672+
649673
dt64 = np.datetime64('2013-01-01')
650674
assert dt64.dtype == 'datetime64[D]'
651-
res = df - dt64
652-
expected = pd.DataFrame([pd.Timedelta(days=0), pd.Timedelta(days=1),
653-
pd.Timedelta(days=2)])
654-
tm.assert_frame_equal(res, expected)
675+
676+
expected = pd.TimedeltaIndex(['0 Days', '1 Day', '2 Days'])
677+
expected = tm.box_expected(expected, box)
678+
679+
result = dtarr - dt64
680+
tm.assert_equal(result, expected)
655681

656682

657683
class TestTimestampSeriesArithmetic(object):
658684

659-
def test_timestamp_sub_series(self):
660-
ser = pd.Series(pd.date_range('2014-03-17', periods=2, freq='D',
661-
tz='US/Eastern'))
685+
def test_dt64arr_sub_timestamp(self, box):
686+
ser = pd.date_range('2014-03-17', periods=2, freq='D',
687+
tz='US/Eastern')
662688
ts = ser[0]
663689

690+
# FIXME: transpose raises ValueError
691+
ser = tm.box_expected(ser, box, transpose=False)
692+
664693
delta_series = pd.Series([np.timedelta64(0, 'D'),
665694
np.timedelta64(1, 'D')])
666-
tm.assert_series_equal(ser - ts, delta_series)
667-
tm.assert_series_equal(ts - ser, -delta_series)
695+
expected = tm.box_expected(delta_series, box, transpose=False)
696+
697+
tm.assert_equal(ser - ts, expected)
698+
tm.assert_equal(ts - ser, -expected)
668699

669700
def test_dt64ser_sub_datetime_dtype(self):
670701
ts = Timestamp(datetime(1993, 1, 7, 13, 30, 00))
@@ -722,20 +753,23 @@ def check(get_ser, test_ser):
722753
if op_str not in ['__add__', '__radd__', '__sub__', '__rsub__']:
723754
check(dt2, td2)
724755

725-
@pytest.mark.parametrize('klass', [Series, pd.Index])
726-
def test_sub_datetime64_not_ns(self, klass):
727-
# GH#7996
756+
def test_sub_datetime64_not_ns(self, box):
757+
# GH#7996 operation with non-nano datetime64 scalar
728758
dt64 = np.datetime64('2013-01-01')
729759
assert dt64.dtype == 'datetime64[D]'
730760

731-
obj = klass(date_range('20130101', periods=3))
732-
res = obj - dt64
733-
expected = klass([Timedelta(days=0), Timedelta(days=1),
734-
Timedelta(days=2)])
735-
tm.assert_equal(res, expected)
761+
obj = date_range('20130101', periods=3)
762+
obj = tm.box_expected(obj, box)
763+
764+
expected = TimedeltaIndex([Timedelta(days=0), Timedelta(days=1),
765+
Timedelta(days=2)])
766+
expected = tm.box_expected(expected, box)
767+
768+
result = obj - dt64
769+
tm.assert_equal(result, expected)
736770

737-
res = dt64 - obj
738-
tm.assert_equal(res, -expected)
771+
result = dt64 - obj
772+
tm.assert_equal(result, -expected)
739773

740774
def test_sub_single_tz(self):
741775
# GH12290
@@ -1438,34 +1472,43 @@ def test_sub_dti_dti(self):
14381472
result = dti2 - dti1
14391473
tm.assert_index_equal(result, expected)
14401474

1441-
@pytest.mark.parametrize('freq', [None, 'D'])
1442-
def test_sub_period(self, freq, box_with_datetime):
1475+
@pytest.mark.parametrize('dti_freq', [None, 'D'])
1476+
def test_dt64arr_add_sub_period(self, dti_freq, box_with_datetime):
14431477
# GH#13078
14441478
# not supported, check TypeError
14451479
p = pd.Period('2011-01-01', freq='D')
14461480

1447-
idx = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=freq)
1481+
idx = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=dti_freq)
14481482
idx = tm.box_expected(idx, box_with_datetime)
14491483

1484+
with pytest.raises(TypeError):
1485+
idx + p
1486+
with pytest.raises(TypeError):
1487+
p + idx
14501488
with pytest.raises(TypeError):
14511489
idx - p
1452-
14531490
with pytest.raises(TypeError):
14541491
p - idx
14551492

1456-
@pytest.mark.parametrize('op', [operator.add, ops.radd,
1457-
operator.sub, ops.rsub])
14581493
@pytest.mark.parametrize('pi_freq', ['D', 'W', 'Q', 'H'])
14591494
@pytest.mark.parametrize('dti_freq', [None, 'D'])
1460-
def test_dti_sub_pi(self, dti_freq, pi_freq, op, box):
1495+
def test_dti_add_sub_pi(self, dti_freq, pi_freq,
1496+
box_with_datetime, box_with_period):
14611497
# GH#20049 subtracting PeriodIndex should raise TypeError
14621498
dti = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=dti_freq)
14631499
pi = dti.to_period(pi_freq)
14641500

1465-
dti = tm.box_expected(dti, box)
1466-
# TODO: Also box pi?
1501+
dtarr = tm.box_expected(dti, box_with_datetime)
1502+
parr = tm.box_expected(pi, box_with_period)
1503+
1504+
with pytest.raises(TypeError):
1505+
dtarr + parr
1506+
with pytest.raises(TypeError):
1507+
parr + dtarr
1508+
with pytest.raises(TypeError):
1509+
dtarr - parr
14671510
with pytest.raises(TypeError):
1468-
op(dti, pi)
1511+
parr - dtarr
14691512

14701513
# -------------------------------------------------------------------
14711514
# TODO: Most of this block is moved from series or frame tests, needs

0 commit comments

Comments
 (0)