From 89b36839667f023e97aceecbd1991359077d6970 Mon Sep 17 00:00:00 2001 From: sinhrks Date: Sun, 18 Jan 2015 15:38:31 +0900 Subject: [PATCH] BUG: Adding nano offset raises TypeError --- doc/source/whatsnew/v0.16.0.txt | 3 +++ .../0.15.2/0.15.2_x86_64_darwin_2.7.9.pickle | Bin 0 -> 14892 bytes pandas/io/tests/test_pickle.py | 16 ++++++++++++ pandas/tseries/offsets.py | 17 +++++++------ pandas/tseries/tests/test_offsets.py | 24 +++++++++++++++++- pandas/tslib.pyx | 2 ++ setup.py | 3 +++ 7 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 pandas/io/tests/data/legacy_pickle/0.15.2/0.15.2_x86_64_darwin_2.7.9.pickle diff --git a/doc/source/whatsnew/v0.16.0.txt b/doc/source/whatsnew/v0.16.0.txt index b3ac58a9fb84a..16882c572f48e 100644 --- a/doc/source/whatsnew/v0.16.0.txt +++ b/doc/source/whatsnew/v0.16.0.txt @@ -152,6 +152,9 @@ Bug Fixes +- Bug in adding ``offsets.Nano`` to other offets raises ``TypeError`` (:issue:`9284`) + + diff --git a/pandas/io/tests/data/legacy_pickle/0.15.2/0.15.2_x86_64_darwin_2.7.9.pickle b/pandas/io/tests/data/legacy_pickle/0.15.2/0.15.2_x86_64_darwin_2.7.9.pickle new file mode 100644 index 0000000000000000000000000000000000000000..1a01539700cf17c3d2c29653d6ad6efde7bfe640 GIT binary patch literal 14892 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 literal 0 HcmV?d00001 diff --git a/pandas/io/tests/test_pickle.py b/pandas/io/tests/test_pickle.py index aea7fb42b7d36..3073673575702 100644 --- a/pandas/io/tests/test_pickle.py +++ b/pandas/io/tests/test_pickle.py @@ -75,6 +75,7 @@ def read_pickles(self, version): if 'series' in data: if 'ts' in data['series']: self._validate_timeseries(data['series']['ts'], self.data['series']['ts']) + self._validate_frequency(data['series']['ts']) def test_read_pickles_0_10_1(self): self.read_pickles('0.10.1') @@ -148,6 +149,21 @@ def _validate_timeseries(self, pickled, current): self.assertEqual(pickled.index.freq.normalize, False) self.assert_numpy_array_equal(pickled > 0, current > 0) + def _validate_frequency(self, pickled): + # GH 9291 + from pandas.tseries.offsets import Day + freq = pickled.index.freq + result = freq + Day(1) + self.assertTrue(result, Day(2)) + + result = freq + pandas.Timedelta(hours=1) + self.assertTrue(isinstance(result, pandas.Timedelta)) + self.assertEqual(result, pandas.Timedelta(days=1, hours=1)) + + result = freq + pandas.Timedelta(nanoseconds=1) + self.assertTrue(isinstance(result, pandas.Timedelta)) + self.assertEqual(result, pandas.Timedelta(days=1, nanoseconds=1)) + if __name__ == '__main__': import nose diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 81daa2b451c6b..84449cd2fad98 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -9,7 +9,7 @@ from dateutil.relativedelta import relativedelta, weekday from dateutil.easter import easter import pandas.tslib as tslib -from pandas.tslib import Timestamp, OutOfBoundsDatetime +from pandas.tslib import Timestamp, OutOfBoundsDatetime, Timedelta import functools @@ -2010,7 +2010,7 @@ def f(self, other): class Tick(SingleConstructorOffset): - _inc = timedelta(microseconds=1000) + _inc = Timedelta(microseconds=1000) __gt__ = _tick_comp(operator.gt) __ge__ = _tick_comp(operator.ge) @@ -2107,36 +2107,37 @@ def _delta_to_tick(delta): class Day(Tick): - _inc = timedelta(1) + _inc = Timedelta(days=1) _prefix = 'D' class Hour(Tick): - _inc = timedelta(0, 3600) + _inc = Timedelta(hours=1) _prefix = 'H' class Minute(Tick): - _inc = timedelta(0, 60) + _inc = Timedelta(minutes=1) _prefix = 'T' class Second(Tick): - _inc = timedelta(0, 1) + _inc = Timedelta(seconds=1) _prefix = 'S' class Milli(Tick): + _inc = Timedelta(milliseconds=1) _prefix = 'L' class Micro(Tick): - _inc = timedelta(microseconds=1) + _inc = Timedelta(microseconds=1) _prefix = 'U' class Nano(Tick): - _inc = np.timedelta64(1, 'ns') + _inc = Timedelta(nanoseconds=1) _prefix = 'N' diff --git a/pandas/tseries/tests/test_offsets.py b/pandas/tseries/tests/test_offsets.py index ef4288b28e9e4..6f9e8b6819bd3 100644 --- a/pandas/tseries/tests/test_offsets.py +++ b/pandas/tseries/tests/test_offsets.py @@ -25,7 +25,7 @@ import pandas.tseries.offsets as offsets from pandas.io.pickle import read_pickle -from pandas.tslib import NaT, Timestamp +from pandas.tslib import NaT, Timestamp, Timedelta import pandas.tslib as tslib from pandas.util.testing import assertRaisesRegexp import pandas.util.testing as tm @@ -2817,6 +2817,7 @@ def test_Easter(): assertEq(-Easter(), datetime(2010, 4, 4), datetime(2009, 4, 12)) assertEq(-Easter(2), datetime(2010, 4, 4), datetime(2008, 3, 23)) + def test_Hour(): assertEq(Hour(), datetime(2010, 1, 1), datetime(2010, 1, 1, 1)) assertEq(Hour(-1), datetime(2010, 1, 1, 1), datetime(2010, 1, 1)) @@ -2904,6 +2905,10 @@ def test_Nanosecond(): assert (Nano(3) + Nano(2)) == Nano(5) assert (Nano(3) - Nano(2)) == Nano() + # GH9284 + assert Nano(1) + Nano(10) == Nano(11) + assert Nano(5) + Micro(1) == Nano(1005) + assert Micro(5) + Nano(1) == Nano(5001) def test_tick_offset(): assert not Day().isAnchored() @@ -2928,6 +2933,23 @@ def test_compare_ticks(): assert(kls(3) != kls(4)) +class TestTicks(tm.TestCase): + + def test_ticks(self): + offsets = [(Hour, Timedelta(hours=5)), + (Minute, Timedelta(hours=2, minutes=3)), + (Second, Timedelta(hours=2, seconds=3)), + (Milli, Timedelta(hours=2, milliseconds=3)), + (Micro, Timedelta(hours=2, microseconds=3)), + (Nano, Timedelta(hours=2, nanoseconds=3))] + + for kls, expected in offsets: + offset = kls(3) + result = offset + Timedelta(hours=2) + self.assertTrue(isinstance(result, Timedelta)) + self.assertEqual(result, expected) + + class TestOffsetNames(tm.TestCase): def test_get_offset_name(self): assertRaisesRegexp(ValueError, 'Bad rule.*BusinessDays', get_offset_name, BDay(2)) diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index 8217c4b31b287..c7c35564c1e5a 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -997,6 +997,8 @@ cdef class _NaT(_Timestamp): def _delta_to_nanoseconds(delta): + if hasattr(delta, 'nanos'): + return delta.nanos if hasattr(delta, 'delta'): delta = delta.delta if is_timedelta64_object(delta): diff --git a/setup.py b/setup.py index f58b9b0bb8551..e64235affaae2 100755 --- a/setup.py +++ b/setup.py @@ -590,6 +590,9 @@ def pxd(name): 'tests/data/legacy_pickle/0.12.0/*.pickle', 'tests/data/legacy_pickle/0.13.0/*.pickle', 'tests/data/legacy_pickle/0.14.0/*.pickle', + 'tests/data/legacy_pickle/0.14.1/*.pickle', + 'tests/data/legacy_pickle/0.15.0/*.pickle', + 'tests/data/legacy_pickle/0.15.2/*.pickle', 'tests/data/*.csv', 'tests/data/*.dta', 'tests/data/*.txt',