Skip to content

Commit e566490

Browse files
committed
Merge pull request pandas-dev#67 from manahl/multiindex-timezone-support
Add multiindex timezone support
2 parents a13ebdd + b8aa869 commit e566490

File tree

3 files changed

+38
-14
lines changed

3 files changed

+38
-14
lines changed

arctic/store/_pandas_ndarray_store.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
import ast
12
import logging
23

34
from bson.binary import Binary
45
from pandas import DataFrame, MultiIndex, Series, DatetimeIndex, Panel
6+
from pandas.tseries.index import DatetimeIndex
57
from pandas.tslib import Timestamp, get_timezone
8+
69
import numpy as np
7-
import ast
810

911
from .._compression import compress, decompress
12+
from ..date._util import to_pandas_closed_closed
1013
from ..exceptions import ArcticException
1114
from ._ndarray_store import NdarrayStore
12-
from ..date._util import to_pandas_closed_closed
15+
1316

1417
log = logging.getLogger(__name__)
1518

@@ -57,6 +60,8 @@ def _index_to_records(self, df):
5760

5861
if isinstance(index, DatetimeIndex) and index.tz is not None:
5962
metadata['index_tz'] = get_timezone(index.tz)
63+
elif isinstance(index, MultiIndex):
64+
metadata['index_tz'] = [get_timezone(i.tz) if isinstance(i, DatetimeIndex) else None for i in index.levels]
6065

6166
return index_names, ix_vals, metadata
6267

@@ -66,6 +71,10 @@ def _index_from_records(self, recarr):
6671

6772
if isinstance(rtn, DatetimeIndex) and 'index_tz' in recarr.dtype.metadata:
6873
rtn = rtn.tz_localize('UTC').tz_convert(recarr.dtype.metadata['index_tz'])
74+
elif isinstance(rtn, MultiIndex):
75+
for i, tz in enumerate(recarr.dtype.metadata.get('index_tz')):
76+
if tz is not None:
77+
rtn.set_levels(rtn.levels[i].tz_localize('UTC').tz_convert(tz), i, inplace=True)
6978

7079
return rtn
7180

tests/integration/store/test_bitemporal_store.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
2012-11-08 17:06:11.040 | 3.0
2929
2012-11-09 17:06:11.040 | 3.5""")
3030

31+
LOCAL_TZ = mktz()
32+
3133

3234
def test_new_ts_read_write(bitemporal_library):
3335
bitemporal_library.update('spam', ts1)
@@ -57,7 +59,7 @@ def test_write_ts_with_column_name_same_as_observed_dt_ok(bitemporal_library):
5759
def test_last_update(bitemporal_library):
5860
bitemporal_library.update('spam', ts1, as_of=dt(2015, 1, 1))
5961
bitemporal_library.update('spam', ts1, as_of=dt(2015, 1, 2))
60-
assert bitemporal_library.read('spam').last_updated == dt(2015, 1, 2)
62+
assert bitemporal_library.read('spam').last_updated == dt(2015, 1, 2, tzinfo=LOCAL_TZ)
6163

6264

6365
def test_existing_ts_update_and_read(bitemporal_library):
@@ -195,10 +197,9 @@ def test_read_ts_raw_all_version_ok(bitemporal_library):
195197

196198

197199
def test_bitemporal_store_saves_as_of_with_timezone(bitemporal_library):
198-
local_tz = mktz()
199200
bitemporal_library.update('spam', ts1, as_of=dt(2015, 5, 1))
200201
df = bitemporal_library.read('spam', raw=True).data
201-
assert all([x[1].replace(tzinfo=mktz('UTC')) == dt(2015, 5, 1, tzinfo=local_tz) for x in df.index])
202+
assert all([x[1] == dt(2015, 5, 1, tzinfo=LOCAL_TZ) for x in df.index])
202203

203204

204205
def test_bitemporal_store_read_as_of_timezone(bitemporal_library):
@@ -263,4 +264,4 @@ def test_multi_index_update(bitemporal_library):
263264
bitemporal_library.update('spam', ts, as_of=dt(2015, 1, 1))
264265
bitemporal_library.update('spam', ts2, as_of=dt(2015, 1, 2))
265266
assert_frame_equal(expected_ts, bitemporal_library.read('spam').data)
266-
assert bitemporal_library.read('spam').last_updated == dt(2015, 1, 2)
267+
assert bitemporal_library.read('spam').last_updated == dt(2015, 1, 2, tzinfo=LOCAL_TZ)

tests/integration/store/test_pandas_store.py

+22-8
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
from StringIO import StringIO
22
from datetime import datetime as dt, timedelta as dtd
3+
import io
4+
import itertools
5+
import string
6+
37
from dateutil.rrule import rrule, DAILY
8+
from mock import Mock, patch
49
from pandas import DataFrame, Series, DatetimeIndex, MultiIndex, read_csv, Panel, date_range, concat
10+
from pandas.tseries.offsets import DateOffset
511
from pandas.util.testing import assert_frame_equal, assert_series_equal
6-
import numpy as np
712
import pytest
8-
import io
9-
import itertools
10-
from mock import Mock, patch
11-
import string
1213

13-
from arctic.date import DateRange, mktz
1414
from arctic._compression import decompress
15+
from arctic.date import DateRange, mktz
1516
from arctic.store._pandas_ndarray_store import PandasDataFrameStore, PandasSeriesStore, PandasStore
1617
from arctic.store.version_store import register_versioned_storage
17-
from pandas.tseries.offsets import DateOffset
18+
import numpy as np
19+
from tests.util import read_str_as_pandas
1820

1921

2022
register_versioned_storage(PandasDataFrameStore)
@@ -831,9 +833,21 @@ def test_data_info_cols(library):
831833
library.write('test_data', s)
832834
md = library.get_info('test_data')
833835
assert md == {'dtype': [('level_0', '<i8'), ('level_1', 'S2'), ('0', '<i8')],
834-
'col_names': {u'index': [u'level_0', u'level_1'], u'columns': [u'0']},
836+
'col_names': {u'index': [u'level_0', u'level_1'], u'columns': [u'0'], u'index_tz': [None, None]},
835837
'type': u'pandasdf',
836838
'handler': 'PandasDataFrameStore',
837839
'rows': 3,
838840
'segment_count': 1,
839841
'size': 54}
842+
843+
844+
def test_read_write_multiindex_store_keeps_timezone(library):
845+
"""If I write a multi-index dataframe and reads it, the timezone of the index shouldn't change, right?"""
846+
hk, ny, ldn = mktz('Asia/Hong_Kong'), mktz('America/New_York'), mktz('Europe/London')
847+
row0 = [dt(2015, 1, 1, tzinfo=hk), dt(2015, 1, 1, tzinfo=ny), dt(2015, 1, 1, tzinfo=ldn), 0, 42]
848+
row1 = [dt(2015, 1, 2, tzinfo=hk), dt(2015, 1, 2, tzinfo=ny), dt(2015, 1, 2, tzinfo=ldn), 1, 43]
849+
df = DataFrame([row0, row1], columns=['dt_a', 'dt_b', 'dt_c', 'index_0', 'data'])
850+
df = df.set_index(['dt_a', 'dt_b', 'dt_c', 'index_0'])
851+
library.write('spam', df)
852+
assert list(library.read('spam').data.index[0]) == row0[:-1]
853+
assert list(library.read('spam').data.index[1]) == row1[:-1]

0 commit comments

Comments
 (0)