From 8f6ac5bcf301be2efaf32736b2a6258b4253e708 Mon Sep 17 00:00:00 2001 From: sinhrks Date: Fri, 8 Aug 2014 10:57:03 +0900 Subject: [PATCH] ENH/BUG: Period and PeriodIndex ops supports timedelta-like --- doc/source/timeseries.rst | 44 ++++++- doc/source/v0.15.0.txt | 15 +++ pandas/tests/test_base.py | 126 +++++++++++++++++-- pandas/tseries/period.py | 58 ++++++++- pandas/tseries/tests/test_period.py | 187 +++++++++++++++++++++++++++- 5 files changed, 417 insertions(+), 13 deletions(-) diff --git a/doc/source/timeseries.rst b/doc/source/timeseries.rst index 7a912361d0e14..c69cd12673463 100644 --- a/doc/source/timeseries.rst +++ b/doc/source/timeseries.rst @@ -4,7 +4,7 @@ .. ipython:: python :suppress: - from datetime import datetime + from datetime import datetime, timedelta import numpy as np np.random.seed(123456) from pandas import * @@ -1098,6 +1098,36 @@ frequency. p - 3 +If ``Period`` freq is daily or higher (``D``, ``H``, ``T``, ``S``, ``L``, ``U``, ``N``), ``offsets`` and ``timedelta``-like can be added if the result can have same freq. Otherise, ``ValueError`` will be raised. + +.. ipython:: python + + p = Period('2014-07-01 09:00', freq='H') + p + Hour(2) + p + timedelta(minutes=120) + p + np.timedelta64(7200, 's') + +.. code-block:: python + + In [1]: p + Minute(5) + Traceback + ... + ValueError: Input has different freq from Period(freq=H) + +If ``Period`` has other freqs, only the same ``offsets`` can be added. Otherwise, ``ValueError`` will be raised. + +.. ipython:: python + + p = Period('2014-07', freq='M') + p + MonthEnd(3) + +.. code-block:: python + + In [1]: p + MonthBegin(3) + Traceback + ... + ValueError: Input has different freq from Period(freq=M) + Taking the difference of ``Period`` instances with the same frequency will return the number of frequency units between them: @@ -1129,6 +1159,18 @@ objects: ps = Series(randn(len(prng)), prng) ps +``PeriodIndex`` supports addition and subtraction as the same rule as ``Period``. + +.. ipython:: python + + idx = period_range('2014-07-01 09:00', periods=5, freq='H') + idx + idx + Hour(2) + + idx = period_range('2014-07', periods=5, freq='M') + idx + idx + MonthEnd(3) + PeriodIndex Partial String Indexing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/source/v0.15.0.txt b/doc/source/v0.15.0.txt index a9266c24df8ee..61a012ab5b503 100644 --- a/doc/source/v0.15.0.txt +++ b/doc/source/v0.15.0.txt @@ -271,10 +271,21 @@ Enhancements +- ``Period`` and ``PeriodIndex`` supports addition/subtraction with ``timedelta``-likes (:issue:`7966`) + If ``Period`` freq is ``D``, ``H``, ``T``, ``S``, ``L``, ``U``, ``N``, ``timedelta``-like can be added if the result can have same freq. Otherwise, only the same ``offsets`` can be added. + .. ipython:: python + idx = pd.period_range('2014-07-01 09:00', periods=5, freq='H') + idx + idx + pd.offsets.Hour(2) + idx + timedelta(minutes=120) + idx + np.timedelta64(7200, 's') + idx = pd.period_range('2014-07', periods=5, freq='M') + idx + idx + pd.offsets.MonthEnd(3) @@ -414,6 +425,10 @@ Bug Fixes +- ``Period`` and ``PeriodIndex`` addition/subtraction with ``np.timedelta64`` results in incorrect internal representations (:issue:`7740`) + + + diff --git a/pandas/tests/test_base.py b/pandas/tests/test_base.py index 356984ea88f43..211bd03262fac 100644 --- a/pandas/tests/test_base.py +++ b/pandas/tests/test_base.py @@ -915,6 +915,8 @@ def test_resolution(self): self.assertEqual(idx.resolution, expected) def test_add_iadd(self): + tm._skip_if_not_numpy17_friendly() + # union rng1 = pd.period_range('1/1/2000', freq='D', periods=5) other1 = pd.period_range('1/6/2000', freq='D', periods=5) @@ -968,11 +970,64 @@ def test_add_iadd(self): tm.assert_index_equal(rng, expected) # offset - for delta in [pd.offsets.Hour(2), timedelta(hours=2)]: - rng = pd.period_range('2000-01-01', '2000-02-01') - with tm.assertRaisesRegexp(TypeError, 'unsupported operand type\(s\)'): + # DateOffset + rng = pd.period_range('2014', '2024', freq='A') + result = rng + pd.offsets.YearEnd(5) + expected = pd.period_range('2019', '2029', freq='A') + tm.assert_index_equal(result, expected) + rng += pd.offsets.YearEnd(5) + tm.assert_index_equal(rng, expected) + + for o in [pd.offsets.YearBegin(2), pd.offsets.MonthBegin(1), pd.offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + rng + o + + rng = pd.period_range('2014-01', '2016-12', freq='M') + result = rng + pd.offsets.MonthEnd(5) + expected = pd.period_range('2014-06', '2017-05', freq='M') + tm.assert_index_equal(result, expected) + rng += pd.offsets.MonthEnd(5) + tm.assert_index_equal(rng, expected) + + for o in [pd.offsets.YearBegin(2), pd.offsets.MonthBegin(1), pd.offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + rng = pd.period_range('2014-01', '2016-12', freq='M') + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + rng + o + + # Tick + offsets = [pd.offsets.Day(3), timedelta(days=3), np.timedelta64(3, 'D'), + pd.offsets.Hour(72), timedelta(minutes=60*24*3), np.timedelta64(72, 'h')] + for delta in offsets: + rng = pd.period_range('2014-05-01', '2014-05-15', freq='D') + result = rng + delta + expected = pd.period_range('2014-05-04', '2014-05-18', freq='D') + tm.assert_index_equal(result, expected) + rng += delta + tm.assert_index_equal(rng, expected) + + for o in [pd.offsets.YearBegin(2), pd.offsets.MonthBegin(1), pd.offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + rng = pd.period_range('2014-05-01', '2014-05-15', freq='D') + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + rng + o + + offsets = [pd.offsets.Hour(2), timedelta(hours=2), np.timedelta64(2, 'h'), + pd.offsets.Minute(120), timedelta(minutes=120), np.timedelta64(120, 'm')] + for delta in offsets: + rng = pd.period_range('2014-01-01 10:00', '2014-01-05 10:00', freq='H') + result = rng + delta + expected = pd.period_range('2014-01-01 12:00', '2014-01-05 12:00', freq='H') + tm.assert_index_equal(result, expected) + rng += delta + tm.assert_index_equal(rng, expected) + + for delta in [pd.offsets.YearBegin(2), timedelta(minutes=30), np.timedelta64(30, 's')]: + rng = pd.period_range('2014-01-01 10:00', '2014-01-05 10:00', freq='H') + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): result = rng + delta - with tm.assertRaisesRegexp(TypeError, 'unsupported operand type\(s\)'): + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): rng += delta # int @@ -984,6 +1039,8 @@ def test_add_iadd(self): tm.assert_index_equal(rng, expected) def test_sub_isub(self): + tm._skip_if_not_numpy17_friendly() + # diff rng1 = pd.period_range('1/1/2000', freq='D', periods=5) other1 = pd.period_range('1/6/2000', freq='D', periods=5) @@ -1027,10 +1084,65 @@ def test_sub_isub(self): tm.assert_index_equal(rng, expected) # offset - for delta in [pd.offsets.Hour(2), timedelta(hours=2)]: - with tm.assertRaisesRegexp(TypeError, 'unsupported operand type\(s\)'): + # DateOffset + rng = pd.period_range('2014', '2024', freq='A') + result = rng - pd.offsets.YearEnd(5) + expected = pd.period_range('2009', '2019', freq='A') + tm.assert_index_equal(result, expected) + rng -= pd.offsets.YearEnd(5) + tm.assert_index_equal(rng, expected) + + for o in [pd.offsets.YearBegin(2), pd.offsets.MonthBegin(1), pd.offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + rng = pd.period_range('2014', '2024', freq='A') + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + rng - o + + rng = pd.period_range('2014-01', '2016-12', freq='M') + result = rng - pd.offsets.MonthEnd(5) + expected = pd.period_range('2013-08', '2016-07', freq='M') + tm.assert_index_equal(result, expected) + rng -= pd.offsets.MonthEnd(5) + tm.assert_index_equal(rng, expected) + + for o in [pd.offsets.YearBegin(2), pd.offsets.MonthBegin(1), pd.offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + rng = pd.period_range('2014-01', '2016-12', freq='M') + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + rng - o + + # Tick + offsets = [pd.offsets.Day(3), timedelta(days=3), np.timedelta64(3, 'D'), + pd.offsets.Hour(72), timedelta(minutes=60*24*3), np.timedelta64(72, 'h')] + for delta in offsets: + rng = pd.period_range('2014-05-01', '2014-05-15', freq='D') + result = rng - delta + expected = pd.period_range('2014-04-28', '2014-05-12', freq='D') + tm.assert_index_equal(result, expected) + rng -= delta + tm.assert_index_equal(rng, expected) + + for o in [pd.offsets.YearBegin(2), pd.offsets.MonthBegin(1), pd.offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + rng = pd.period_range('2014-05-01', '2014-05-15', freq='D') + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + rng - o + + offsets = [pd.offsets.Hour(2), timedelta(hours=2), np.timedelta64(2, 'h'), + pd.offsets.Minute(120), timedelta(minutes=120), np.timedelta64(120, 'm')] + for delta in offsets: + rng = pd.period_range('2014-01-01 10:00', '2014-01-05 10:00', freq='H') + result = rng - delta + expected = pd.period_range('2014-01-01 08:00', '2014-01-05 08:00', freq='H') + tm.assert_index_equal(result, expected) + rng -= delta + tm.assert_index_equal(rng, expected) + + for delta in [pd.offsets.YearBegin(2), timedelta(minutes=30), np.timedelta64(30, 's')]: + rng = pd.period_range('2014-01-01 10:00', '2014-01-05 10:00', freq='H') + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): result = rng + delta - with tm.assertRaisesRegexp(TypeError, 'unsupported operand type\(s\)'): + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): rng += delta # int diff --git a/pandas/tseries/period.py b/pandas/tseries/period.py index e80fdf28c4089..f12badc080f12 100644 --- a/pandas/tseries/period.py +++ b/pandas/tseries/period.py @@ -1,7 +1,7 @@ # pylint: disable=E1101,E1103,W0232 import operator -from datetime import datetime, date +from datetime import datetime, date, timedelta import numpy as np from pandas.core.base import PandasObject @@ -10,6 +10,7 @@ from pandas.tseries.index import DatetimeIndex, Int64Index, Index from pandas.core.base import DatetimeIndexOpsMixin from pandas.tseries.tools import parse_time_string +import pandas.tseries.offsets as offsets import pandas.core.common as com from pandas.core.common import (isnull, _INT64_DTYPE, _maybe_box, @@ -169,8 +170,37 @@ def __ne__(self, other): def __hash__(self): return hash((self.ordinal, self.freq)) + def _add_delta(self, other): + if isinstance(other, (timedelta, np.timedelta64, offsets.Tick)): + offset = frequencies.to_offset(self.freq) + if isinstance(offset, offsets.Tick): + nanos = tslib._delta_to_nanoseconds(other) + offset_nanos = tslib._delta_to_nanoseconds(offset) + + if nanos % offset_nanos == 0: + if self.ordinal == tslib.iNaT: + ordinal = self.ordinal + else: + ordinal = self.ordinal + (nanos // offset_nanos) + return Period(ordinal=ordinal, freq=self.freq) + elif isinstance(other, offsets.DateOffset): + freqstr = frequencies.get_standard_freq(other) + base = frequencies.get_base_alias(freqstr) + + if base == self.freq: + if self.ordinal == tslib.iNaT: + ordinal = self.ordinal + else: + ordinal = self.ordinal + other.n + return Period(ordinal=ordinal, freq=self.freq) + + raise ValueError("Input has different freq from Period(freq={0})".format(self.freq)) + def __add__(self, other): - if com.is_integer(other): + if isinstance(other, (timedelta, np.timedelta64, + offsets.Tick, offsets.DateOffset)): + return self._add_delta(other) + elif com.is_integer(other): if self.ordinal == tslib.iNaT: ordinal = self.ordinal else: @@ -180,13 +210,17 @@ def __add__(self, other): return NotImplemented def __sub__(self, other): - if com.is_integer(other): + if isinstance(other, (timedelta, np.timedelta64, + offsets.Tick, offsets.DateOffset)): + neg_other = -other + return self + neg_other + elif com.is_integer(other): if self.ordinal == tslib.iNaT: ordinal = self.ordinal else: ordinal = self.ordinal - other return Period(ordinal=ordinal, freq=self.freq) - if isinstance(other, Period): + elif isinstance(other, Period): if other.freq != self.freq: raise ValueError("Cannot do arithmetic with " "non-conforming periods") @@ -862,6 +896,22 @@ def to_timestamp(self, freq=None, how='start'): new_data = tslib.periodarr_to_dt64arr(new_data.values, base) return DatetimeIndex(new_data, freq='infer', name=self.name) + def _add_delta(self, other): + if isinstance(other, (timedelta, np.timedelta64, offsets.Tick)): + offset = frequencies.to_offset(self.freq) + if isinstance(offset, offsets.Tick): + nanos = tslib._delta_to_nanoseconds(other) + offset_nanos = tslib._delta_to_nanoseconds(offset) + if nanos % offset_nanos == 0: + return self.shift(nanos // offset_nanos) + elif isinstance(other, offsets.DateOffset): + freqstr = frequencies.get_standard_freq(other) + base = frequencies.get_base_alias(freqstr) + + if base == self.freq: + return self.shift(other.n) + raise ValueError("Input has different freq from PeriodIndex(freq={0})".format(self.freq)) + def shift(self, n): """ Specialized shift which produces an PeriodIndex diff --git a/pandas/tseries/tests/test_period.py b/pandas/tseries/tests/test_period.py index b7abedbafa7b0..3fae251b433e6 100644 --- a/pandas/tseries/tests/test_period.py +++ b/pandas/tseries/tests/test_period.py @@ -16,6 +16,7 @@ from pandas.tseries.index import DatetimeIndex, date_range, Index from pandas.tseries.tools import to_datetime import pandas.tseries.period as period +import pandas.tseries.offsets as offsets import pandas.core.datetools as datetools import pandas as pd @@ -26,7 +27,7 @@ from pandas import Series, TimeSeries, DataFrame, _np_version_under1p9 from pandas import tslib from pandas.util.testing import(assert_series_equal, assert_almost_equal, - assertRaisesRegexp) + assertRaisesRegexp, _skip_if_not_numpy17_friendly) import pandas.util.testing as tm from pandas import compat from numpy.testing import assert_array_equal @@ -2484,6 +2485,190 @@ def test_add(self): with tm.assertRaisesRegexp(TypeError, msg): dt1 + dt2 + def test_add_offset(self): + _skip_if_not_numpy17_friendly() + + # freq is DateOffset + p = Period('2011', freq='A') + self.assertEqual(p + offsets.YearEnd(2), Period('2013', freq='A')) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + p + o + + p = Period('2011-03', freq='M') + self.assertEqual(p + offsets.MonthEnd(2), Period('2011-05', freq='M')) + self.assertEqual(p + offsets.MonthEnd(12), Period('2012-03', freq='M')) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + p + o + + # freq is Tick + p = Period('2011-04-01', freq='D') + self.assertEqual(p + offsets.Day(5), Period('2011-04-06', freq='D')) + self.assertEqual(p + offsets.Hour(24), Period('2011-04-02', freq='D')) + self.assertEqual(p + np.timedelta64(2, 'D'), Period('2011-04-03', freq='D')) + self.assertEqual(p + np.timedelta64(3600 * 24, 's'), Period('2011-04-02', freq='D')) + self.assertEqual(p + timedelta(-2), Period('2011-03-30', freq='D')) + self.assertEqual(p + timedelta(hours=48), Period('2011-04-03', freq='D')) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + p + o + + p = Period('2011-04-01 09:00', freq='H') + self.assertEqual(p + offsets.Day(2), Period('2011-04-03 09:00', freq='H')) + self.assertEqual(p + offsets.Hour(3), Period('2011-04-01 12:00', freq='H')) + self.assertEqual(p + np.timedelta64(3, 'h'), Period('2011-04-01 12:00', freq='H')) + self.assertEqual(p + np.timedelta64(3600, 's'), Period('2011-04-01 10:00', freq='H')) + self.assertEqual(p + timedelta(minutes=120), Period('2011-04-01 11:00', freq='H')) + self.assertEqual(p + timedelta(days=4, minutes=180), Period('2011-04-05 12:00', freq='H')) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + p + o + + def test_add_offset_nat(self): + _skip_if_not_numpy17_friendly() + + # freq is DateOffset + p = Period('NaT', freq='A') + for o in [offsets.YearEnd(2)]: + self.assertEqual((p + o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p + o + + p = Period('NaT', freq='M') + for o in [offsets.MonthEnd(2), offsets.MonthEnd(12)]: + self.assertEqual((p + o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + p + o + + # freq is Tick + p = Period('NaT', freq='D') + for o in [offsets.Day(5), offsets.Hour(24), np.timedelta64(2, 'D'), + np.timedelta64(3600 * 24, 's'), timedelta(-2), timedelta(hours=48)]: + self.assertEqual((p + o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + p + o + + p = Period('NaT', freq='H') + for o in [offsets.Day(2), offsets.Hour(3), np.timedelta64(3, 'h'), + np.timedelta64(3600, 's'), timedelta(minutes=120), + timedelta(days=4, minutes=180)]: + self.assertEqual((p + o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: + with tm.assertRaisesRegexp(ValueError, 'Input has different freq from Period'): + p + o + + def test_sub_offset(self): + _skip_if_not_numpy17_friendly() + + # freq is DateOffset + p = Period('2011', freq='A') + self.assertEqual(p - offsets.YearEnd(2), Period('2009', freq='A')) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p - o + + p = Period('2011-03', freq='M') + self.assertEqual(p - offsets.MonthEnd(2), Period('2011-01', freq='M')) + self.assertEqual(p - offsets.MonthEnd(12), Period('2010-03', freq='M')) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p - o + + # freq is Tick + p = Period('2011-04-01', freq='D') + self.assertEqual(p - offsets.Day(5), Period('2011-03-27', freq='D')) + self.assertEqual(p - offsets.Hour(24), Period('2011-03-31', freq='D')) + self.assertEqual(p - np.timedelta64(2, 'D'), Period('2011-03-30', freq='D')) + self.assertEqual(p - np.timedelta64(3600 * 24, 's'), Period('2011-03-31', freq='D')) + self.assertEqual(p - timedelta(-2), Period('2011-04-03', freq='D')) + self.assertEqual(p - timedelta(hours=48), Period('2011-03-30', freq='D')) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + with tm.assertRaises(ValueError): + p - o + + p = Period('2011-04-01 09:00', freq='H') + self.assertEqual(p - offsets.Day(2), Period('2011-03-30 09:00', freq='H')) + self.assertEqual(p - offsets.Hour(3), Period('2011-04-01 06:00', freq='H')) + self.assertEqual(p - np.timedelta64(3, 'h'), Period('2011-04-01 06:00', freq='H')) + self.assertEqual(p - np.timedelta64(3600, 's'), Period('2011-04-01 08:00', freq='H')) + self.assertEqual(p - timedelta(minutes=120), Period('2011-04-01 07:00', freq='H')) + self.assertEqual(p - timedelta(days=4, minutes=180), Period('2011-03-28 06:00', freq='H')) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: + with tm.assertRaises(ValueError): + p - o + + def test_sub_offset_nat(self): + _skip_if_not_numpy17_friendly() + + # freq is DateOffset + p = Period('NaT', freq='A') + for o in [offsets.YearEnd(2)]: + self.assertEqual((p - o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p - o + + p = Period('NaT', freq='M') + for o in [offsets.MonthEnd(2), offsets.MonthEnd(12)]: + self.assertEqual((p - o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(365, 'D'), timedelta(365)]: + with tm.assertRaises(ValueError): + p - o + + # freq is Tick + p = Period('NaT', freq='D') + for o in [offsets.Day(5), offsets.Hour(24), np.timedelta64(2, 'D'), + np.timedelta64(3600 * 24, 's'), timedelta(-2), timedelta(hours=48)]: + self.assertEqual((p - o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(4, 'h'), timedelta(hours=23)]: + with tm.assertRaises(ValueError): + p - o + + p = Period('NaT', freq='H') + for o in [offsets.Day(2), offsets.Hour(3), np.timedelta64(3, 'h'), + np.timedelta64(3600, 's'), timedelta(minutes=120), + timedelta(days=4, minutes=180)]: + self.assertEqual((p - o).ordinal, tslib.iNaT) + + for o in [offsets.YearBegin(2), offsets.MonthBegin(1), offsets.Minute(), + np.timedelta64(3200, 's'), timedelta(hours=23, minutes=30)]: + with tm.assertRaises(ValueError): + p - o + def test_nat_ops(self): p = Period('NaT', freq='M') self.assertEqual((p + 1).ordinal, tslib.iNaT)