diff --git a/doc/source/timeseries.rst b/doc/source/timeseries.rst index e9406b7f49245..76bc796beced8 100644 --- a/doc/source/timeseries.rst +++ b/doc/source/timeseries.rst @@ -493,7 +493,7 @@ The basic ``DateOffset`` takes the same arguments as .. ipython:: python - d = datetime(2008, 8, 18) + d = datetime(2008, 8, 18, 9, 0) d + relativedelta(months=4, days=5) We could have done the same thing with ``DateOffset``: @@ -568,10 +568,21 @@ particular day of the week: .. ipython:: python + d d + Week() d + Week(weekday=4) (d + Week(weekday=4)).weekday() + d - Week() + +``normalize`` option will be effective for addition and subtraction. + +.. ipython:: python + + d + Week(normalize=True) + d - Week(normalize=True) + + Another example is parameterizing ``YearEnd`` with the specific ending month: .. ipython:: python diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index d1fe287bf33be..57181b43df9f6 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -157,7 +157,7 @@ def isAnchored(self): return (self.n == 1) def copy(self): - return self.__class__(self.n, **self.kwds) + return self.__class__(self.n, normalize=self.normalize, **self.kwds) def _should_cache(self): return self.isAnchored() and self._cacheable @@ -251,34 +251,34 @@ def __sub__(self, other): if isinstance(other, datetime): raise TypeError('Cannot subtract datetime from offset.') elif type(other) == type(self): - return self.__class__(self.n - other.n, **self.kwds) + return self.__class__(self.n - other.n, normalize=self.normalize, **self.kwds) else: # pragma: no cover return NotImplemented def __rsub__(self, other): - return self.__class__(-self.n, **self.kwds) + other + return self.__class__(-self.n, normalize=self.normalize, **self.kwds) + other def __mul__(self, someInt): - return self.__class__(n=someInt * self.n, **self.kwds) + return self.__class__(n=someInt * self.n, normalize=self.normalize, **self.kwds) def __rmul__(self, someInt): return self.__mul__(someInt) def __neg__(self): - return self.__class__(-self.n, **self.kwds) + return self.__class__(-self.n, normalize=self.normalize, **self.kwds) @apply_wraps def rollback(self, dt): """Roll provided date backward to next offset only if not on offset""" if not self.onOffset(dt): - dt = dt - self.__class__(1, **self.kwds) + dt = dt - self.__class__(1, normalize=self.normalize, **self.kwds) return dt @apply_wraps def rollforward(self, dt): """Roll provided date forward to next offset only if not on offset""" if not self.onOffset(dt): - dt = dt + self.__class__(1, **self.kwds) + dt = dt + self.__class__(1, normalize=self.normalize, **self.kwds) return dt def onOffset(self, dt): diff --git a/pandas/tseries/tests/test_offsets.py b/pandas/tseries/tests/test_offsets.py index 1ef1bd184bdbc..9febec68bd458 100644 --- a/pandas/tseries/tests/test_offsets.py +++ b/pandas/tseries/tests/test_offsets.py @@ -361,6 +361,42 @@ def test_onOffset(self): date = datetime(dt.year, dt.month, dt.day) self.assert_(offset_n.onOffset(date)) + def test_add(self): + dt = datetime(2011, 1, 1, 9, 0) + + for offset in self.offset_types: + offset_s = self._get_offset(offset) + expected = self.expecteds[offset.__name__] + + result_dt = dt + offset_s + result_ts = Timestamp(dt) + offset_s + for result in [result_dt, result_ts]: + self.assertTrue(isinstance(result, Timestamp)) + self.assertEqual(result, expected) + + tm._skip_if_no_pytz() + for tz in self.timezones: + expected_localize = expected.tz_localize(tz) + result = Timestamp(dt, tz=tz) + offset_s + self.assert_(isinstance(result, Timestamp)) + self.assertEqual(result, expected_localize) + + # normalize=True + offset_s = self._get_offset(offset, normalize=True) + expected = Timestamp(expected.date()) + + result_dt = dt + offset_s + result_ts = Timestamp(dt) + offset_s + for result in [result_dt, result_ts]: + self.assertTrue(isinstance(result, Timestamp)) + self.assertEqual(result, expected) + + for tz in self.timezones: + expected_localize = expected.tz_localize(tz) + result = Timestamp(dt, tz=tz) + offset_s + self.assert_(isinstance(result, Timestamp)) + self.assertEqual(result, expected_localize) + class TestDateOffset(Base): _multiprocess_can_split_ = True diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index 70b6b308b6b37..2fd71521b24d5 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -753,7 +753,10 @@ cdef class _Timestamp(datetime): elif isinstance(other, timedelta) or hasattr(other, 'delta'): nanos = _delta_to_nanoseconds(other) - return Timestamp(self.value + nanos, tz=self.tzinfo, offset=self.offset) + result = Timestamp(self.value + nanos, tz=self.tzinfo, offset=self.offset) + if getattr(other, 'normalize', False): + result = Timestamp(normalize_date(result)) + return result result = datetime.__add__(self, other) if isinstance(result, datetime):