Skip to content

BUG: Right result for DatetimeIndex + TimeDelta when timezone is set(… #13935

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
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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.19.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,7 @@ Bug Fixes
- Bug in invalid ``Timedelta`` arithmetic and comparison may raise ``ValueError`` rather than ``TypeError`` (:issue:`13624`)
- Bug in invalid datetime parsing in ``to_datetime`` and ``DatetimeIndex`` may raise ``TypeError`` rather than ``ValueError`` (:issue:`11169`, :issue:`11287`)
- Bug in ``Index`` created with tz-aware ``Timestamp`` and mismatched ``tz`` option incorrectly coerces timezone (:issue:`13692`)
- Bug in ``DatetimeIndex + TimeDelta`` gives wrong results when timezone is set (:issue:`13905`)
- Bug in ``DatetimeIndex`` with nanosecond frequency does not include timestamp specified with ``end`` (:issue:`13672`)

- Bug in ``Index`` raises ``OutOfBoundsDatetime`` if ``datetime`` exceeds ``datetime64[ns]`` bounds, rather than coercing to ``object`` dtype (:issue:`13663`)
Expand Down
6 changes: 3 additions & 3 deletions pandas/core/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ def _convert_to_array(self, values, name=None, other=None):

# a datelike
elif isinstance(values, pd.DatetimeIndex):
values = values.to_series()
values = values.to_series(True)
Copy link
Contributor

Choose a reason for hiding this comment

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

we should handle is_datetime64_dtype and is_datetime64tz_dtype separately here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it happens, isn't is?

# datetime with tz
elif (isinstance(ovalues, datetime.datetime) and
hasattr(ovalues, 'tz')):
Expand Down Expand Up @@ -543,9 +543,9 @@ def _offset(lvalues, rvalues):

# with tz, convert to UTC
if self.is_datetime64tz_lhs:
lvalues = lvalues.tz_localize(None)
lvalues = lvalues.tz_convert('UTC')
Copy link
Member

Choose a reason for hiding this comment

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

I think this localization can be removed, as adding timedelta is unrelated to the timezone.

if self.is_datetime64tz_rhs:
rvalues = rvalues.tz_localize(None)
rvalues = rvalues.tz_convert('UTC')

lvalues = lvalues.view(np.int64)
rvalues = rvalues.view(np.int64)
Expand Down
9 changes: 8 additions & 1 deletion pandas/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,14 @@ def __new__(cls, data=None, dtype=None, copy=False, name=None,
if (issubclass(data.dtype.type, np.datetime64) or
is_datetimetz(data)):
from pandas.tseries.index import DatetimeIndex
result = DatetimeIndex(data, copy=copy, name=name, **kwargs)
if 'tz' in kwargs:
Copy link
Contributor

Choose a reason for hiding this comment

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

this is way too complicated

Copy link
Member

Choose a reason for hiding this comment

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

we can use DatetimeIndex._simple_new(data, name=name, **kwargs). Needs to handle copy in advance.

tz = kwargs['tz']
del kwargs['tz']
result = DatetimeIndex(data, copy=copy, tz='UTC',
name=name, **kwargs).tz_convert(tz)
else:
result = DatetimeIndex(data, copy=copy,
name=name, **kwargs)
if dtype is not None and _o_dtype == dtype:
return Index(result.to_pydatetime(), dtype=_o_dtype)
else:
Expand Down
11 changes: 11 additions & 0 deletions pandas/tests/series/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,17 @@ def test_timedelta64_operations_with_DateOffset(self):
td - op(5)
op(5) - td

def test_timedelta64_operations_with_DatetimeIndex_tz(self):
# GH 13905
dti = pd.DatetimeIndex(['2016-06-28 05:30', '2016-06-28 05:31'],
dtype='datetime64[ns, America/Chicago]')
td = Series(['00:00:05', '00:00:05'], dtype='timedelta64[ns]')

exp = Series(['2016-06-28 05:30:05-05:00',
'2016-06-28 05:31:05-05:00'],
dtype='datetime64[ns, America/Chicago]')
assert_series_equal(dti + td, exp)

def test_timedelta64_operations_with_timedeltas(self):

# td operate with td
Expand Down
4 changes: 2 additions & 2 deletions pandas/types/cast.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,8 +820,8 @@ def _possibly_cast_to_datetime(value, dtype, errors='raise'):
# input has to be UTC at this point, so just
# localize
value = to_datetime(
value,
errors=errors).tz_localize(dtype.tz)
value, utc=True,
errors=errors).tz_convert(dtype.tz)
elif is_timedelta64:
value = to_timedelta(value, errors=errors)._values
except (AttributeError, ValueError, TypeError):
Expand Down