Skip to content

Commit 144223d

Browse files
jbrockmendeldavid-liu-brattle-1
authored andcommitted
disallow normalize=True with Tick classes (pandas-dev#21427)
1 parent 60408a6 commit 144223d

File tree

3 files changed

+58
-3
lines changed

3 files changed

+58
-3
lines changed

doc/source/whatsnew/v0.24.0.txt

+35
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,41 @@ Other Enhancements
2424
Backwards incompatible API changes
2525
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2626

27+
.. _whatsnew_0240.api.datetimelike.normalize
28+
29+
Tick DateOffset Normalize Restrictions
30+
--------------------------------------
31+
32+
Creating a ``Tick`` object (:class:``Day``, :class:``Hour``, :class:``Minute``,
33+
:class:``Second``, :class:``Milli``, :class:``Micro``, :class:``Nano``) with
34+
`normalize=True` is no longer supported. This prevents unexpected behavior
35+
where addition could fail to be monotone or associative. (:issue:`21427`)
36+
37+
.. ipython:: python
38+
39+
ts = pd.Timestamp('2018-06-11 18:01:14')
40+
ts
41+
tic = pd.offsets.Hour(n=2, normalize=True)
42+
tic
43+
44+
Previous Behavior:
45+
46+
.. code-block:: ipython
47+
48+
In [4]: ts + tic
49+
Out [4]: Timestamp('2018-06-11 00:00:00')
50+
51+
In [5]: ts + tic + tic + tic == ts + (tic + tic + tic)
52+
Out [5]: False
53+
54+
Current Behavior:
55+
56+
.. ipython:: python
57+
58+
tic = pd.offsets.Hour(n=2)
59+
ts + tic + tic + tic == ts + (tic + tic + tic)
60+
61+
2762
.. _whatsnew_0240.api.datetimelike:
2863

2964
Datetimelike API Changes

pandas/tests/tseries/offsets/test_offsets.py

+20-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
YearEnd, Day,
2929
QuarterEnd, BusinessMonthEnd, FY5253,
3030
Nano, Easter, FY5253Quarter,
31-
LastWeekOfMonth)
31+
LastWeekOfMonth, Tick)
3232
from pandas.core.tools.datetimes import format, ole2datetime
3333
import pandas.tseries.offsets as offsets
3434
from pandas.io.pickle import read_pickle
@@ -270,6 +270,11 @@ def test_offset_freqstr(self, offset_types):
270270

271271
def _check_offsetfunc_works(self, offset, funcname, dt, expected,
272272
normalize=False):
273+
274+
if normalize and issubclass(offset, Tick):
275+
# normalize=True disallowed for Tick subclasses GH#21427
276+
return
277+
273278
offset_s = self._get_offset(offset, normalize=normalize)
274279
func = getattr(offset_s, funcname)
275280

@@ -458,6 +463,9 @@ def test_onOffset(self, offset_types):
458463
assert offset_s.onOffset(dt)
459464

460465
# when normalize=True, onOffset checks time is 00:00:00
466+
if issubclass(offset_types, Tick):
467+
# normalize=True disallowed for Tick subclasses GH#21427
468+
return
461469
offset_n = self._get_offset(offset_types, normalize=True)
462470
assert not offset_n.onOffset(dt)
463471

@@ -485,7 +493,9 @@ def test_add(self, offset_types, tz):
485493
assert isinstance(result, Timestamp)
486494
assert result == expected_localize
487495

488-
# normalize=True
496+
# normalize=True, disallowed for Tick subclasses GH#21427
497+
if issubclass(offset_types, Tick):
498+
return
489499
offset_s = self._get_offset(offset_types, normalize=True)
490500
expected = Timestamp(expected.date())
491501

@@ -3092,6 +3102,14 @@ def test_require_integers(offset_types):
30923102
cls(n=1.5)
30933103

30943104

3105+
def test_tick_normalize_raises(tick_classes):
3106+
# check that trying to create a Tick object with normalize=True raises
3107+
# GH#21427
3108+
cls = tick_classes
3109+
with pytest.raises(ValueError):
3110+
cls(n=3, normalize=True)
3111+
3112+
30953113
def test_weeks_onoffset():
30963114
# GH#18510 Week with weekday = None, normalize = False should always
30973115
# be onOffset

pandas/tseries/offsets.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -2217,8 +2217,10 @@ class Tick(SingleConstructorOffset):
22172217
_attributes = frozenset(['n', 'normalize'])
22182218

22192219
def __init__(self, n=1, normalize=False):
2220-
# TODO: do Tick classes with normalize=True make sense?
22212220
self.n = self._validate_n(n)
2221+
if normalize:
2222+
raise ValueError("Tick offset with `normalize=True` are not "
2223+
"allowed.") # GH#21427
22222224
self.normalize = normalize
22232225

22242226
__gt__ = _tick_comp(operator.gt)

0 commit comments

Comments
 (0)