Skip to content

BUG: is_normalized returned False for local tz #13484

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.18.2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -524,3 +524,5 @@ Bug Fixes


- Bug in ``Categorical.remove_unused_categories()`` changes ``.codes`` dtype to platform int (:issue:`13261`)

- Bug in ``DatetimeIndex.is_normalized`` returns False for normalized date_range in case of local timezones (:issue:`13459`)
36 changes: 6 additions & 30 deletions pandas/io/tests/test_pytables.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
assert_panel_equal,
assert_frame_equal,
assert_series_equal,
assert_produces_warning)
assert_produces_warning,
set_timezone)
from pandas import concat, Timestamp
from pandas import compat
from pandas.compat import range, lrange, u
Expand Down Expand Up @@ -5309,14 +5310,6 @@ def test_store_timezone(self):
# issue storing datetime.date with a timezone as it resets when read
# back in a new timezone

import platform
if platform.system() == "Windows":
raise nose.SkipTest("timezone setting not supported on windows")

import datetime
import time
import os

# original method
with ensure_clean_store(self.path) as store:

Expand All @@ -5327,34 +5320,17 @@ def test_store_timezone(self):
assert_frame_equal(result, df)

# with tz setting
orig_tz = os.environ.get('TZ')

def setTZ(tz):
if tz is None:
try:
del os.environ['TZ']
except:
pass
else:
os.environ['TZ'] = tz
time.tzset()

try:

with ensure_clean_store(self.path) as store:
with ensure_clean_store(self.path) as store:

setTZ('EST5EDT')
with set_timezone('EST5EDT'):
today = datetime.date(2013, 9, 10)
df = DataFrame([1, 2, 3], index=[today, today, today])
store['obj1'] = df

setTZ('CST6CDT')
with set_timezone('CST6CDT'):
result = store['obj1']

assert_frame_equal(result, df)

finally:
setTZ(orig_tz)
assert_frame_equal(result, df)

def test_legacy_datetimetz_object(self):
# legacy from < 0.17.0
Expand Down
22 changes: 21 additions & 1 deletion pandas/tseries/tests/test_timezones.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import pandas.util.testing as tm
from pandas.types.api import DatetimeTZDtype
from pandas.util.testing import assert_frame_equal
from pandas.util.testing import assert_frame_equal, set_timezone
from pandas.compat import lrange, zip

try:
Expand Down Expand Up @@ -1398,6 +1398,26 @@ def test_normalize_tz(self):
self.assertTrue(result.is_normalized)
self.assertFalse(rng.is_normalized)

def test_normalize_tz_local(self):
# GH 13459
from dateutil.tz import tzlocal

timezones = ['US/Pacific', 'US/Eastern', 'UTC', 'Asia/Kolkata',
'Asia/Shanghai', 'Australia/Canberra']

for timezone in timezones:
with set_timezone(timezone):
rng = date_range('1/1/2000 9:30', periods=10, freq='D',
tz=tzlocal())

result = rng.normalize()
expected = date_range('1/1/2000', periods=10, freq='D',
tz=tzlocal())
self.assert_index_equal(result, expected)

self.assertTrue(result.is_normalized)
self.assertFalse(rng.is_normalized)

def test_tzaware_offset(self):
dates = date_range('2012-11-01', periods=3, tz='US/Pacific')
offset = dates + offsets.Hour(5)
Expand Down
4 changes: 1 addition & 3 deletions pandas/tslib.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -4810,12 +4810,10 @@ def dates_normalized(ndarray[int64_t] stamps, tz=None):
elif _is_tzlocal(tz):
for i in range(n):
pandas_datetime_to_datetimestruct(stamps[i], PANDAS_FR_ns, &dts)
if (dts.min + dts.sec + dts.us) > 0:
return False
dt = datetime(dts.year, dts.month, dts.day, dts.hour, dts.min,
dts.sec, dts.us, tz)
dt = dt + tz.utcoffset(dt)
if dt.hour > 0:
if (dt.hour + dt.minute + dt.second + dt.microsecond) > 0:
return False
else:
trans, deltas, typ = _get_dst_info(tz)
Expand Down
47 changes: 47 additions & 0 deletions pandas/util/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2667,3 +2667,50 @@ def patch(ob, attr, value):
delattr(ob, attr)
else:
setattr(ob, attr, old)


@contextmanager
def set_timezone(tz):
"""Context manager for temporarily setting a timezone.

Parameters
----------
tz : str
A string representing a valid timezone.

Examples
--------

>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> tzlocal().tzname(datetime.now())
'IST'

>>> with set_timezone('US/Eastern'):
... tzlocal().tzname(datetime.now())
...
'EDT'
"""
if is_platform_windows():
import nose
raise nose.SkipTest("timezone setting not supported on windows")

import os
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a doc-string with Parameters here (it must also be a string, so you should str(tz) somewhere.

import time

def setTZ(tz):
if tz is None:
try:
del os.environ['TZ']
except:
pass
else:
os.environ['TZ'] = tz
time.tzset()

orig_tz = os.environ.get('TZ')
setTZ(tz)
try:
yield
finally:
setTZ(orig_tz)