Skip to content

Commit f7822bd

Browse files
committed
Merge pull request #7705 from sinhrks/normalize_bug
BUG: offset normalize option may not work in addition/subtraction
2 parents 1508491 + 163ef05 commit f7822bd

File tree

4 files changed

+59
-9
lines changed

4 files changed

+59
-9
lines changed

doc/source/timeseries.rst

+12-1
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ The basic ``DateOffset`` takes the same arguments as
493493

494494
.. ipython:: python
495495
496-
d = datetime(2008, 8, 18)
496+
d = datetime(2008, 8, 18, 9, 0)
497497
d + relativedelta(months=4, days=5)
498498
499499
We could have done the same thing with ``DateOffset``:
@@ -568,10 +568,21 @@ particular day of the week:
568568

569569
.. ipython:: python
570570
571+
d
571572
d + Week()
572573
d + Week(weekday=4)
573574
(d + Week(weekday=4)).weekday()
574575
576+
d - Week()
577+
578+
``normalize`` option will be effective for addition and subtraction.
579+
580+
.. ipython:: python
581+
582+
d + Week(normalize=True)
583+
d - Week(normalize=True)
584+
585+
575586
Another example is parameterizing ``YearEnd`` with the specific ending month:
576587

577588
.. ipython:: python

pandas/tseries/offsets.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def isAnchored(self):
157157
return (self.n == 1)
158158

159159
def copy(self):
160-
return self.__class__(self.n, **self.kwds)
160+
return self.__class__(self.n, normalize=self.normalize, **self.kwds)
161161

162162
def _should_cache(self):
163163
return self.isAnchored() and self._cacheable
@@ -251,34 +251,34 @@ def __sub__(self, other):
251251
if isinstance(other, datetime):
252252
raise TypeError('Cannot subtract datetime from offset.')
253253
elif type(other) == type(self):
254-
return self.__class__(self.n - other.n, **self.kwds)
254+
return self.__class__(self.n - other.n, normalize=self.normalize, **self.kwds)
255255
else: # pragma: no cover
256256
return NotImplemented
257257

258258
def __rsub__(self, other):
259-
return self.__class__(-self.n, **self.kwds) + other
259+
return self.__class__(-self.n, normalize=self.normalize, **self.kwds) + other
260260

261261
def __mul__(self, someInt):
262-
return self.__class__(n=someInt * self.n, **self.kwds)
262+
return self.__class__(n=someInt * self.n, normalize=self.normalize, **self.kwds)
263263

264264
def __rmul__(self, someInt):
265265
return self.__mul__(someInt)
266266

267267
def __neg__(self):
268-
return self.__class__(-self.n, **self.kwds)
268+
return self.__class__(-self.n, normalize=self.normalize, **self.kwds)
269269

270270
@apply_wraps
271271
def rollback(self, dt):
272272
"""Roll provided date backward to next offset only if not on offset"""
273273
if not self.onOffset(dt):
274-
dt = dt - self.__class__(1, **self.kwds)
274+
dt = dt - self.__class__(1, normalize=self.normalize, **self.kwds)
275275
return dt
276276

277277
@apply_wraps
278278
def rollforward(self, dt):
279279
"""Roll provided date forward to next offset only if not on offset"""
280280
if not self.onOffset(dt):
281-
dt = dt + self.__class__(1, **self.kwds)
281+
dt = dt + self.__class__(1, normalize=self.normalize, **self.kwds)
282282
return dt
283283

284284
def onOffset(self, dt):

pandas/tseries/tests/test_offsets.py

+36
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,42 @@ def test_onOffset(self):
361361
date = datetime(dt.year, dt.month, dt.day)
362362
self.assert_(offset_n.onOffset(date))
363363

364+
def test_add(self):
365+
dt = datetime(2011, 1, 1, 9, 0)
366+
367+
for offset in self.offset_types:
368+
offset_s = self._get_offset(offset)
369+
expected = self.expecteds[offset.__name__]
370+
371+
result_dt = dt + offset_s
372+
result_ts = Timestamp(dt) + offset_s
373+
for result in [result_dt, result_ts]:
374+
self.assertTrue(isinstance(result, Timestamp))
375+
self.assertEqual(result, expected)
376+
377+
tm._skip_if_no_pytz()
378+
for tz in self.timezones:
379+
expected_localize = expected.tz_localize(tz)
380+
result = Timestamp(dt, tz=tz) + offset_s
381+
self.assert_(isinstance(result, Timestamp))
382+
self.assertEqual(result, expected_localize)
383+
384+
# normalize=True
385+
offset_s = self._get_offset(offset, normalize=True)
386+
expected = Timestamp(expected.date())
387+
388+
result_dt = dt + offset_s
389+
result_ts = Timestamp(dt) + offset_s
390+
for result in [result_dt, result_ts]:
391+
self.assertTrue(isinstance(result, Timestamp))
392+
self.assertEqual(result, expected)
393+
394+
for tz in self.timezones:
395+
expected_localize = expected.tz_localize(tz)
396+
result = Timestamp(dt, tz=tz) + offset_s
397+
self.assert_(isinstance(result, Timestamp))
398+
self.assertEqual(result, expected_localize)
399+
364400

365401
class TestDateOffset(Base):
366402
_multiprocess_can_split_ = True

pandas/tslib.pyx

+4-1
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,10 @@ cdef class _Timestamp(datetime):
753753

754754
elif isinstance(other, timedelta) or hasattr(other, 'delta'):
755755
nanos = _delta_to_nanoseconds(other)
756-
return Timestamp(self.value + nanos, tz=self.tzinfo, offset=self.offset)
756+
result = Timestamp(self.value + nanos, tz=self.tzinfo, offset=self.offset)
757+
if getattr(other, 'normalize', False):
758+
result = Timestamp(normalize_date(result))
759+
return result
757760

758761
result = datetime.__add__(self, other)
759762
if isinstance(result, datetime):

0 commit comments

Comments
 (0)