Skip to content

Commit b1cfd65

Browse files
Remove timezone path dependency on *nix
- Use dateutil.tz.gettz rather than loading tzfiles directly - this should support Windows: https://labix.org/python-dateutil#head-b79630fbbda87af6d6d121737510fd7ea5aeea97 - Don't load the default timezone on module import (we shouldn't do additional I/O here) - Remove custom tzfile binary-search implementation, this was fixed upstream in dateutil: dateutil/dateutil@1205f3c Fixes pandas-dev#43
1 parent dec4f3d commit b1cfd65

File tree

5 files changed

+20
-62
lines changed

5 files changed

+20
-62
lines changed

arctic/date/_mktz.py

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,24 @@
33
import dateutil
44
import tzlocal
55

6-
DEFAULT_TIME_ZONE_NAME = tzlocal.get_localzone().zone # 'Europe/London'
7-
TIME_ZONE_DATA_SOURCE = u'/usr/share/zoneinfo/'
8-
96

107
class TimezoneError(Exception):
118
pass
129

1310

14-
class tzfile(dateutil.tz.tzfile):
15-
16-
def _find_ttinfo(self, dtm, laststd=0):
17-
"""Faster version of parent class's _find_ttinfo() as this uses bisect rather than a linear search."""
18-
if dtm is None:
19-
# This will happen, for example, when a datetime.time object gets utcoffset() called.
20-
raise ValueError('tzinfo object can not calculate offset for date %s' % dtm)
21-
ts = ((dtm.toordinal() - dateutil.tz.EPOCHORDINAL) * 86400
22-
+ dtm.hour * 3600
23-
+ dtm.minute * 60
24-
+ dtm.second)
25-
idx = bisect.bisect_right(self._trans_list, ts)
26-
if len(self._trans_list) == 0 or idx == len(self._trans_list):
27-
return self._ttinfo_std
28-
if idx == 0:
29-
return self._ttinfo_before
30-
if laststd:
31-
while idx > 0:
32-
tti = self._trans_idx[idx - 1]
33-
if not tti.isdst:
34-
return tti
35-
idx -= 1
36-
else:
37-
return self._ttinfo_std
38-
else:
39-
return self._trans_idx[idx - 1]
40-
41-
4211
def mktz(zone=None):
4312
"""
44-
Return a new timezone based on the zone using the python-dateutil
45-
package. This convenience method is useful for resolving the timezone
46-
names as dateutil.tz.tzfile requires the full path.
13+
Return a new timezone (tzinfo object) based on the zone using the python-dateutil
14+
package.
4715
4816
The concise name 'mktz' is for convenient when using it on the
4917
console.
5018
5119
Parameters
5220
----------
5321
zone : `String`
54-
The zone for the timezone. This defaults to 'local'.
22+
The zone for the timezone. This defaults to local, returning:
23+
tzlocal.get_localzone()
5524
5625
Returns
5726
-------
@@ -61,14 +30,9 @@ def mktz(zone=None):
6130
- - - - - -
6231
TimezoneError : Raised if a user inputs a bad timezone name.
6332
"""
64-
6533
if zone is None:
66-
zone = DEFAULT_TIME_ZONE_NAME
67-
_path = os.path.join(TIME_ZONE_DATA_SOURCE, zone)
68-
try:
69-
tz = tzfile(_path)
70-
except (ValueError, IOError) as err:
71-
raise TimezoneError('Timezone "%s" can not be read, error: "%s"' % (zone, err))
72-
# Stash the zone name as an attribute (as pytz does)
73-
tz.zone = zone if not zone.startswith(TIME_ZONE_DATA_SOURCE) else zone[len(TIME_ZONE_DATA_SOURCE):]
34+
zone = tzlocal.get_localzone().zone
35+
tz = dateutil.tz.gettz(zone)
36+
if not tz:
37+
raise TimezoneError('Timezone "%s" can not be read' % (zone))
7438
return tz

tests/integration/tickstore/test_ts_read.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from datetime import datetime as dt
2-
from mock import patch, call
2+
from mock import patch, call, Mock
33
import numpy as np
44
from numpy.testing.utils import assert_array_equal
55
from pandas.util.testing import assert_frame_equal
@@ -287,7 +287,7 @@ def test_date_range_default_timezone(tickstore_lib, tz_name):
287287
},
288288
]
289289

290-
with patch('arctic.date._mktz.DEFAULT_TIME_ZONE_NAME', tz_name):
290+
with patch('tzlocal.get_localzone', return_value=Mock(zone=tz_name)):
291291
tickstore_lib._chunk_size = 1
292292
tickstore_lib.write('SYM', DUMMY_DATA)
293293
df = tickstore_lib.read('SYM', date_range=DateRange(20130101, 20130701), columns=None)

tests/unit/date/test_datetime_to_ms_roundtrip.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from datetime import datetime as dt
44
import pytz
55
from arctic.date import mktz, datetime_to_ms, ms_to_datetime
6-
from arctic.date._mktz import DEFAULT_TIME_ZONE_NAME
76
import sys
87

98

@@ -64,20 +63,20 @@ def test_datetime_roundtrip_local_no_tz():
6463

6564

6665
def test_datetime_roundtrip_local_tz():
67-
pdt = datetime.datetime(2012, 6, 12, 12, 12, 12, 123000, tzinfo=mktz(DEFAULT_TIME_ZONE_NAME))
66+
pdt = datetime.datetime(2012, 6, 12, 12, 12, 12, 123000, tzinfo=mktz())
6867
pdt2 = ms_to_datetime(datetime_to_ms(pdt))
6968
assert pdt2 == pdt
7069

71-
pdt = datetime.datetime(2012, 1, 12, 12, 12, 12, 123000, tzinfo=mktz(DEFAULT_TIME_ZONE_NAME))
70+
pdt = datetime.datetime(2012, 1, 12, 12, 12, 12, 123000, tzinfo=mktz())
7271
pdt2 = ms_to_datetime(datetime_to_ms(pdt))
7372
assert pdt2 == pdt
7473

7574

7675
def test_datetime_roundtrip_est_tz():
7776
pdt = datetime.datetime(2012, 6, 12, 12, 12, 12, 123000, tzinfo=mktz('EST'))
7877
pdt2 = ms_to_datetime(datetime_to_ms(pdt))
79-
assert pdt2.replace(tzinfo=mktz(DEFAULT_TIME_ZONE_NAME)) == pdt
78+
assert pdt2.replace(tzinfo=mktz()) == pdt
8079

8180
pdt = datetime.datetime(2012, 1, 12, 12, 12, 12, 123000, tzinfo=mktz('EST'))
8281
pdt2 = ms_to_datetime(datetime_to_ms(pdt))
83-
assert pdt2.replace(tzinfo=mktz(DEFAULT_TIME_ZONE_NAME)) == pdt
82+
assert pdt2.replace(tzinfo=mktz()) == pdt

tests/unit/date/test_mktz.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from datetime import datetime as dt
22
from mock import patch
33
from pytest import raises
4+
import tzlocal
45

56
from arctic.date import mktz, TimezoneError
6-
from arctic.date._mktz import DEFAULT_TIME_ZONE_NAME, tzfile, TIME_ZONE_DATA_SOURCE
7+
8+
DEFAULT_TIME_ZONE_NAME = tzlocal.get_localzone().zone # 'Europe/London'
79

810

911
def test_mktz():
@@ -27,19 +29,13 @@ def test_mktz_noarg():
2729

2830
def test_mktz_zone():
2931
tz = mktz('UTC')
30-
assert tz.zone == "UTC"
32+
assert 'UTC' in repr(tz)
3133
tz = mktz('/usr/share/zoneinfo/UTC')
32-
assert tz.zone == "UTC"
34+
assert 'UTC' in repr(tz)
3335

3436

3537
def test_mktz_fails_if_invalid_timezone():
3638
with patch('os.path.exists') as file_exists:
3739
file_exists.return_value = False
3840
with raises(TimezoneError):
3941
mktz('junk')
40-
41-
42-
def test_tzfile_raises():
43-
t = tzfile(TIME_ZONE_DATA_SOURCE + DEFAULT_TIME_ZONE_NAME)
44-
with raises(ValueError):
45-
t._find_ttinfo(None)

tests/unit/date/test_util.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
from datetime import datetime as dt
55
from arctic.date import datetime_to_ms, ms_to_datetime, mktz, to_pandas_closed_closed, DateRange, OPEN_OPEN, CLOSED_CLOSED
6-
from arctic.date._mktz import DEFAULT_TIME_ZONE_NAME
76
from arctic.date._util import to_dt
87

98

@@ -24,7 +23,7 @@ def test_datetime_to_ms_and_back(pdt):
2423

2524

2625
def test_datetime_to_ms_and_back_microseconds():
27-
pdt = dt(2012, 8, 1, 12, 34, 56, 999999, tzinfo=mktz(DEFAULT_TIME_ZONE_NAME))
26+
pdt = dt(2012, 8, 1, 12, 34, 56, 999999, tzinfo=mktz())
2827
i = datetime_to_ms(pdt)
2928
pdt2 = ms_to_datetime(i)
3029

0 commit comments

Comments
 (0)