diff --git a/doc/source/whatsnew/v0.17.0.txt b/doc/source/whatsnew/v0.17.0.txt index cc044bc35a707..785f35083a24e 100644 --- a/doc/source/whatsnew/v0.17.0.txt +++ b/doc/source/whatsnew/v0.17.0.txt @@ -26,6 +26,26 @@ New features Other enhancements ^^^^^^^^^^^^^^^^^^ +- Add helper functions to check for OS running on + + from pandas.compat import is_platform_windows + if is_platform_window(): + pass + +- Use dateutil.tz.gettz() after upstream recommandations + + python-dateutil provides two implementations of gettz(). + "dateutil.tz.gettz()" tries to load zone information from system provided data and fals back to + an included tarball, where as "dateutil.zoneinfo.gettz() loads directly from the tarball. Using the later on systems + which aren't providing included zone informations (e.g. Fedora or Gentoo) breaks (#9059, #8639, #9663 and #10121) + As stated by upstream in https://github.com/dateutil/dateutil/issues/11#issuecomment-70769019 only the former should be + used by library consumers. + + For compatibility in pandas following code should be used + + from pandas.tslib import _dateutil_gettz as gettz + tz = gettz('Europe/Brussels') + .. _whatsnew_0170.api: Backwards incompatible API changes @@ -66,5 +86,4 @@ Bug Fixes - Bug in ``Timestamp``'s' ``microsecond``, ``quarter``, ``dayofyear``, ``week`` and ``daysinmonth`` properties return ``np.int`` type, not built-in ``int``. (:issue:`10050`) - Bug in ``NaT`` raises ``AttributeError`` when accessing to ``daysinmonth``, ``dayofweek`` properties. (:issue:`10096`) - - +- Bug in dateutil.tz.gettz() vs. dateutil.zoneinfo.gettz() usage which creates problems on systems solely rely on systems timezone data (:issue:`9123`, :issue:`9059`, :issue:`8639`, :issue:`9663`, :issue:`10121`) diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index 6be0facf2bffc..2a273629544cb 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -26,6 +26,7 @@ Other items: * OrderedDefaultDict +* platform checker """ # pylint disable=W0611 import functools @@ -754,3 +755,16 @@ def __missing__(self, key): def __reduce__(self): # optional, for pickle support args = self.default_factory if self.default_factory else tuple() return type(self), args, None, None, list(self.items()) + + +# https://github.com/pydata/pandas/pull/9123 +def is_platform_windows(): + return sys.platform == 'win32' or sys.platform == 'cygwin' + + +def is_platform_linux(): + return sys.platform == 'linux2' + + +def is_platform_mac(): + return sys.platform == 'darwin' diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index 22f8aee1e0a4e..925cfa875196c 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -5398,7 +5398,8 @@ def test_getitem_setitem_datetime_tz_pytz(self): def test_getitem_setitem_datetime_tz_dateutil(self): tm._skip_if_no_dateutil(); from dateutil.tz import tzutc - from dateutil.zoneinfo import gettz + from pandas.tslib import _dateutil_gettz as gettz + tz = lambda x: tzutc() if x == 'UTC' else gettz(x) # handle special case for utc in dateutil from pandas import date_range diff --git a/pandas/tseries/tests/test_daterange.py b/pandas/tseries/tests/test_daterange.py index 841d81c15b4e9..69b1d84670d45 100644 --- a/pandas/tseries/tests/test_daterange.py +++ b/pandas/tseries/tests/test_daterange.py @@ -441,7 +441,7 @@ def test_month_range_union_tz_pytz(self): def test_month_range_union_tz_dateutil(self): _skip_if_windows_python_3() tm._skip_if_no_dateutil() - from dateutil.zoneinfo import gettz as timezone + from pandas.tslib import _dateutil_gettz as timezone tz = timezone('US/Eastern') early_start = datetime(2011, 1, 1) diff --git a/pandas/tseries/tests/test_period.py b/pandas/tseries/tests/test_period.py index 70c706fc66398..0218af63ca7d6 100644 --- a/pandas/tseries/tests/test_period.py +++ b/pandas/tseries/tests/test_period.py @@ -101,15 +101,15 @@ def test_timestamp_tz_arg(self): pytz.timezone('Europe/Brussels').normalize(p).tzinfo) def test_timestamp_tz_arg_dateutil(self): - import dateutil + from pandas.tslib import _dateutil_gettz as gettz from pandas.tslib import maybe_get_tz p = Period('1/1/2005', freq='M').to_timestamp(tz=maybe_get_tz('dateutil/Europe/Brussels')) - self.assertEqual(p.tz, dateutil.zoneinfo.gettz('Europe/Brussels')) + self.assertEqual(p.tz, gettz('Europe/Brussels')) def test_timestamp_tz_arg_dateutil_from_string(self): - import dateutil + from pandas.tslib import _dateutil_gettz as gettz p = Period('1/1/2005', freq='M').to_timestamp(tz='dateutil/Europe/Brussels') - self.assertEqual(p.tz, dateutil.zoneinfo.gettz('Europe/Brussels')) + self.assertEqual(p.tz, gettz('Europe/Brussels')) def test_timestamp_nat_tz(self): t = Period('NaT', freq='M').to_timestamp() diff --git a/pandas/tseries/tests/test_timeseries.py b/pandas/tseries/tests/test_timeseries.py index 0c4961d80a5f4..6c20b02324688 100644 --- a/pandas/tseries/tests/test_timeseries.py +++ b/pandas/tseries/tests/test_timeseries.py @@ -417,9 +417,9 @@ def test_timestamp_to_datetime_explicit_pytz(self): def test_timestamp_to_datetime_explicit_dateutil(self): _skip_if_windows_python_3() tm._skip_if_no_dateutil() - import dateutil + from pandas.tslib import _dateutil_gettz as gettz rng = date_range('20090415', '20090519', - tz=dateutil.zoneinfo.gettz('US/Eastern')) + tz=gettz('US/Eastern')) stamp = rng[0] dtval = stamp.to_pydatetime() @@ -1807,7 +1807,7 @@ def test_append_concat_tz_explicit_pytz(self): def test_append_concat_tz_dateutil(self): # GH 2938 tm._skip_if_no_dateutil() - from dateutil.zoneinfo import gettz as timezone + from pandas.tslib import _dateutil_gettz as timezone rng = date_range('5/8/2012 1:45', periods=10, freq='5T', tz='dateutil/US/Eastern') diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index 66f14bfb0346a..2b45718d1f9ea 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -41,7 +41,11 @@ from datetime import time as datetime_time # dateutil compat from dateutil.tz import (tzoffset, tzlocal as _dateutil_tzlocal, tzfile as _dateutil_tzfile, tzutc as _dateutil_tzutc) -from dateutil.zoneinfo import gettz as _dateutil_gettz +from pandas.compat import is_platform_windows +if is_platform_windows(): + from dateutil.zoneinfo import gettz as _dateutil_gettz +else: + from dateutil.tz import gettz as _dateutil_gettz from pytz.tzinfo import BaseTzInfo as _pytz_BaseTzInfo from pandas.compat import parse_date, string_types, PY3, iteritems