Skip to content

BUG: Block/DTI doesnt handle tzlocal properly #13583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v0.19.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,8 @@ Bug Fixes
- Bug in ``.resample(..)`` with a ``PeriodIndex`` not retaining its type or name with an empty ``DataFrame`` appropriately when empty (:issue:`13212`)
- Bug in ``groupby(..).resample(..)`` where passing some keywords would raise an exception (:issue:`13235`)
- Bug in ``.tz_convert`` on a tz-aware ``DateTimeIndex`` that relied on index being sorted for correct results (:issue:`13306`)
- Bug in ``.tz_localize`` with ``dateutil.tz.tzlocal`` may return incorrect result (:issue:`13583`)
- Bug in ``DatetimeTZDtype`` dtype with ``dateutil.tz.tzlocal`` cannot be regarded as valid dtype (:issue:`13583`)
- Bug in ``pd.read_hdf()`` where attempting to load an HDF file with a single dataset, that had one or more categorical columns, failed unless the key argument was set to the name of the dataset. (:issue:`13231`)
- Bug in ``.rolling()`` that allowed a negative integer window in contruction of the ``Rolling()`` object, but would later fail on aggregation (:issue:`13383`)

Expand Down
12 changes: 12 additions & 0 deletions pandas/tools/tests/test_merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,18 @@ def test_concat_tz_series_with_datetimelike(self):
result = concat([pd.Series(x), pd.Series(y)], ignore_index=True)
tm.assert_series_equal(result, pd.Series(x + y, dtype='object'))

def test_concat_tz_series_tzlocal(self):
# GH 13583
tm._skip_if_no_dateutil()
import dateutil
x = [pd.Timestamp('2011-01-01', tz=dateutil.tz.tzlocal()),
pd.Timestamp('2011-02-01', tz=dateutil.tz.tzlocal())]
y = [pd.Timestamp('2012-01-01', tz=dateutil.tz.tzlocal()),
pd.Timestamp('2012-02-01', tz=dateutil.tz.tzlocal())]
result = concat([pd.Series(x), pd.Series(y)], ignore_index=True)
tm.assert_series_equal(result, pd.Series(x + y))
self.assertEqual(result.dtype, 'datetime64[ns, tzlocal()]')

def test_concat_period_series(self):
x = Series(pd.PeriodIndex(['2015-11-01', '2015-12-01'], freq='D'))
y = Series(pd.PeriodIndex(['2015-10-01', '2016-01-01'], freq='D'))
Expand Down
40 changes: 40 additions & 0 deletions pandas/tseries/tests/test_timezones.py
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,46 @@ def test_tslib_tz_convert_dst(self):
self.assert_numpy_array_equal(idx.hour,
np.array([4, 4], dtype=np.int32))

def test_tzlocal(self):
# GH 13583
ts = Timestamp('2011-01-01', tz=dateutil.tz.tzlocal())
self.assertEqual(ts.tz, dateutil.tz.tzlocal())
self.assertTrue("tz='tzlocal()')" in repr(ts))

tz = tslib.maybe_get_tz('tzlocal()')
self.assertEqual(tz, dateutil.tz.tzlocal())

# get offset using normal datetime for test
offset = dateutil.tz.tzlocal().utcoffset(datetime(2011, 1, 1))
offset = offset.total_seconds() * 1000000000
self.assertEqual(ts.value + offset, Timestamp('2011-01-01').value)

def test_tz_localize_tzlocal(self):
# GH 13583
offset = dateutil.tz.tzlocal().utcoffset(datetime(2011, 1, 1))
offset = int(offset.total_seconds() * 1000000000)

dti = date_range(start='2001-01-01', end='2001-03-01')
dti2 = dti.tz_localize(dateutil.tz.tzlocal())
tm.assert_numpy_array_equal(dti2.asi8 + offset, dti.asi8)

dti = date_range(start='2001-01-01', end='2001-03-01',
tz=dateutil.tz.tzlocal())
dti2 = dti.tz_localize(None)
tm.assert_numpy_array_equal(dti2.asi8 - offset, dti.asi8)

def test_tz_convert_tzlocal(self):
# GH 13583
# tz_convert doesn't affect to internal
dti = date_range(start='2001-01-01', end='2001-03-01', tz='UTC')
dti2 = dti.tz_convert(dateutil.tz.tzlocal())
tm.assert_numpy_array_equal(dti2.asi8, dti.asi8)

dti = date_range(start='2001-01-01', end='2001-03-01',
tz=dateutil.tz.tzlocal())
dti2 = dti.tz_convert(None)
tm.assert_numpy_array_equal(dti2.asi8, dti.asi8)


class TestTimeZoneCacheKey(tm.TestCase):
def test_cache_keys_are_distinct_for_pytz_vs_dateutil(self):
Expand Down
7 changes: 4 additions & 3 deletions pandas/tslib.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1591,7 +1591,9 @@ cpdef inline object maybe_get_tz(object tz):
Otherwise, just return tz.
"""
if isinstance(tz, string_types):
if tz.startswith('dateutil/'):
if tz == 'tzlocal()':
tz = _dateutil_tzlocal()
elif tz.startswith('dateutil/'):
zone = tz[9:]
tz = _dateutil_gettz(zone)
# On Python 3 on Windows, the filename is not always set correctly.
Expand Down Expand Up @@ -3767,7 +3769,6 @@ def tz_convert(ndarray[int64_t] vals, object tz1, object tz2):
return np.array([], dtype=np.int64)

# Convert to UTC

if _get_zone(tz1) != 'UTC':
utc_dates = np.empty(n, dtype=np.int64)
if _is_tzlocal(tz1):
Expand Down Expand Up @@ -3822,7 +3823,7 @@ def tz_convert(ndarray[int64_t] vals, object tz1, object tz2):
dts.min, dts.sec, dts.us, tz2)
delta = int(total_seconds(_get_utcoffset(tz2, dt))) * 1000000000
result[i] = v + delta
return result
return result

# Convert UTC to other timezone
trans, deltas, typ = _get_dst_info(tz2)
Expand Down