Skip to content

Commit 41b1f65

Browse files
committed
Merge pull request #8370 from jreback/tz_pickle
BUG/COMPAT: set tz on DatetimeIndex on pickle reconstruction (GH8367)
2 parents 8a16900 + cb8a7f0 commit 41b1f65

File tree

4 files changed

+39
-3
lines changed

4 files changed

+39
-3
lines changed

doc/source/v0.15.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ Internal Refactoring
534534

535535
In 0.15.0 ``Index`` has internally been refactored to no longer sub-class ``ndarray``
536536
but instead subclass ``PandasObject``, similarly to the rest of the pandas objects. This change allows very easy sub-classing and creation of new index types. This should be
537-
a transparent change with only very limited API implications (:issue:`5080`, :issue:`7439`, :issue:`7796`, :issue:`8024`)
537+
a transparent change with only very limited API implications (:issue:`5080`, :issue:`7439`, :issue:`7796`, :issue:`8024`, :issue:`8367`)
538538

539539
- you may need to unpickle pandas version < 0.15.0 pickles using ``pd.read_pickle`` rather than ``pickle.load``. See :ref:`pickle docs <io.pickle>`
540540
- when plotting with a ``PeriodIndex``. The ``matplotlib`` internal axes will now be arrays of ``Period`` rather than a ``PeriodIndex``. (this is similar to how a ``DatetimeIndex`` passes arrays of ``datetimes`` now)

pandas/core/index.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -3323,8 +3323,8 @@ def __contains__(self, key):
33233323

33243324
def __reduce__(self):
33253325
"""Necessary for making this object picklable"""
3326-
d = dict(levels = [lev.view(np.ndarray) for lev in self.levels],
3327-
labels = [label.view(np.ndarray) for label in self.labels],
3326+
d = dict(levels = [lev for lev in self.levels],
3327+
labels = [label for label in self.labels],
33283328
sortorder = self.sortorder,
33293329
names = list(self.names))
33303330
return _new_Index, (self.__class__, d), None

pandas/tests/test_index.py

+16
Original file line numberDiff line numberDiff line change
@@ -1645,6 +1645,14 @@ def test_numeric_compat(self):
16451645
lambda : pd.date_range('2000-01-01', periods=3) * np.timedelta64(1, 'D').astype('m8[ns]') ]:
16461646
self.assertRaises(TypeError, f)
16471647

1648+
def test_roundtrip_pickle_with_tz(self):
1649+
1650+
# GH 8367
1651+
# round-trip of timezone
1652+
index=date_range('20130101',periods=3,tz='US/Eastern',name='foo')
1653+
unpickled = self.round_trip_pickle(index)
1654+
self.assertTrue(index.equals(unpickled))
1655+
16481656
class TestPeriodIndex(Base, tm.TestCase):
16491657
_holder = PeriodIndex
16501658
_multiprocess_can_split_ = True
@@ -2347,6 +2355,14 @@ def test_legacy_v2_unpickle(self):
23472355
assert_almost_equal(res, exp)
23482356
assert_almost_equal(exp, exp2)
23492357

2358+
def test_roundtrip_pickle_with_tz(self):
2359+
2360+
# GH 8367
2361+
# round-trip of timezone
2362+
index=MultiIndex.from_product([[1,2],['a','b'],date_range('20130101',periods=3,tz='US/Eastern')],names=['one','two','three'])
2363+
unpickled = self.round_trip_pickle(index)
2364+
self.assertTrue(index.equal_levels(unpickled))
2365+
23502366
def test_from_tuples_index_values(self):
23512367
result = MultiIndex.from_tuples(self.index)
23522368
self.assertTrue((result.values == self.index.values).all())

pandas/tseries/index.py

+20
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,17 @@ def _ensure_datetime64(other):
105105

106106
_midnight = time(0, 0)
107107

108+
def _new_DatetimeIndex(cls, d):
109+
""" This is called upon unpickling, rather than the default which doesn't have arguments
110+
and breaks __new__ """
111+
112+
# simply set the tz
113+
# data are already in UTC
114+
tz = d.pop('tz',None)
115+
result = cls.__new__(cls, **d)
116+
result.tz = tz
117+
return result
118+
108119
class DatetimeIndex(DatetimeIndexOpsMixin, Int64Index):
109120
"""
110121
Immutable ndarray of datetime64 data, represented internally as int64, and
@@ -583,6 +594,15 @@ def _formatter_func(self):
583594
formatter = _get_format_datetime64(is_dates_only=self._is_dates_only)
584595
return lambda x: formatter(x, tz=self.tz)
585596

597+
def __reduce__(self):
598+
599+
# we use a special reudce here because we need
600+
# to simply set the .tz (and not reinterpret it)
601+
602+
d = dict(data=self._data)
603+
d.update(self._get_attributes_dict())
604+
return _new_DatetimeIndex, (self.__class__, d), None
605+
586606
def __setstate__(self, state):
587607
"""Necessary for making this object picklable"""
588608
if isinstance(state, dict):

0 commit comments

Comments
 (0)