Skip to content

Commit

Permalink
DEPR: tz_convert in the Timestamp constructor (#23621)
Browse files Browse the repository at this point in the history
  • Loading branch information
mroeschke authored and jreback committed Nov 18, 2018
1 parent 3702de2 commit e2c4f04
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 24 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ Deprecations
- Constructing a :class:`TimedeltaIndex` from data with ``datetime64``-dtyped data is deprecated, will raise ``TypeError`` in a future version (:issue:`23539`)
- The ``keep_tz=False`` option (the default) of the ``keep_tz`` keyword of
:meth:`DatetimeIndex.to_series` is deprecated (:issue:`17832`).
- Timezone converting a tz-aware ``datetime.datetime`` or :class:`Timestamp` with :class:`Timestamp` and the ``tz`` argument is now deprecated. Instead, use :meth:`Timestamp.tz_convert` (:issue:`23579`)

.. _whatsnew_0240.deprecations.datetimelike_int_ops:

Expand Down
30 changes: 16 additions & 14 deletions pandas/_libs/tslibs/timestamps.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,9 @@ class Timestamp(_Timestamp):
elif tz is not None:
raise ValueError('Can provide at most one of tz, tzinfo')

# User passed tzinfo instead of tz; avoid silently ignoring
tz, tzinfo = tzinfo, None

if is_string_object(ts_input):
# User passed a date string to parse.
# Check that the user didn't also pass a date attribute kwarg.
Expand All @@ -710,24 +713,23 @@ class Timestamp(_Timestamp):

elif ts_input is _no_input:
# User passed keyword arguments.
if tz is None:
# Handle the case where the user passes `tz` and not `tzinfo`
tz = tzinfo
return Timestamp(datetime(year, month, day, hour or 0,
minute or 0, second or 0,
microsecond or 0, tzinfo),
nanosecond=nanosecond, tz=tz)
ts_input = datetime(year, month, day, hour or 0,
minute or 0, second or 0,
microsecond or 0)
elif is_integer_object(freq):
# User passed positional arguments:
# Timestamp(year, month, day[, hour[, minute[, second[,
# microsecond[, nanosecond[, tzinfo]]]]]])
return Timestamp(datetime(ts_input, freq, tz, unit or 0,
year or 0, month or 0, day or 0,
minute), nanosecond=hour, tz=minute)

if tzinfo is not None:
# User passed tzinfo instead of tz; avoid silently ignoring
tz, tzinfo = tzinfo, None
ts_input = datetime(ts_input, freq, tz, unit or 0,
year or 0, month or 0, day or 0)
nanosecond = hour
tz = minute
freq = None

if getattr(ts_input, 'tzinfo', None) is not None and tz is not None:
warnings.warn("Passing a datetime or Timestamp with tzinfo and the"
" tz parameter will raise in the future. Use"
" tz_convert instead.", FutureWarning)

ts = convert_to_tsobject(ts_input, tz, unit, 0, 0, nanosecond or 0)

Expand Down
7 changes: 6 additions & 1 deletion pandas/core/arrays/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ def _to_m8(key, tz=None):
"""
if not isinstance(key, Timestamp):
# this also converts strings
key = Timestamp(key, tz=tz)
key = Timestamp(key)
if key.tzinfo is not None and tz is not None:
# Don't tz_localize(None) if key is already tz-aware
key = key.tz_convert(tz)
else:
key = key.tz_localize(tz)

return np.int64(conversion.pydt_to_i8(key)).view(_NS_DTYPE)

Expand Down
8 changes: 7 additions & 1 deletion pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -9336,8 +9336,14 @@ def describe_categorical_1d(data):
if is_datetime64_any_dtype(data):
tz = data.dt.tz
asint = data.dropna().values.view('i8')
top = Timestamp(top)
if top.tzinfo is not None and tz is not None:
# Don't tz_localize(None) if key is already tz-aware
top = top.tz_convert(tz)
else:
top = top.tz_localize(tz)
names += ['top', 'freq', 'first', 'last']
result += [Timestamp(top, tz=tz), freq,
result += [top, freq,
Timestamp(asint.min(), tz=tz),
Timestamp(asint.max(), tz=tz)]
else:
Expand Down
22 changes: 18 additions & 4 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,10 @@ def get_value(self, series, key):

# needed to localize naive datetimes
if self.tz is not None:
key = Timestamp(key, tz=self.tz)
if key.tzinfo is not None:
key = Timestamp(key).tz_convert(self.tz)
else:
key = Timestamp(key).tz_localize(self.tz)

return self.get_value_maybe_box(series, key)

Expand All @@ -963,7 +966,11 @@ def get_value(self, series, key):
def get_value_maybe_box(self, series, key):
# needed to localize naive datetimes
if self.tz is not None:
key = Timestamp(key, tz=self.tz)
key = Timestamp(key)
if key.tzinfo is not None:
key = key.tz_convert(self.tz)
else:
key = key.tz_localize(self.tz)
elif not isinstance(key, Timestamp):
key = Timestamp(key)
values = self._engine.get_value(com.values_from_object(series),
Expand All @@ -986,7 +993,10 @@ def get_loc(self, key, method=None, tolerance=None):

if isinstance(key, datetime):
# needed to localize naive datetimes
key = Timestamp(key, tz=self.tz)
if key.tzinfo is None:
key = Timestamp(key, tz=self.tz)
else:
key = Timestamp(key).tz_convert(self.tz)
return Index.get_loc(self, key, method, tolerance)

elif isinstance(key, timedelta):
Expand All @@ -1010,7 +1020,11 @@ def get_loc(self, key, method=None, tolerance=None):
pass

try:
stamp = Timestamp(key, tz=self.tz)
stamp = Timestamp(key)
if stamp.tzinfo is not None and self.tz is not None:
stamp = stamp.tz_convert(self.tz)
else:
stamp = stamp.tz_localize(self.tz)
return Index.get_loc(self, stamp, method, tolerance)
except KeyError:
raise KeyError(key)
Expand Down
5 changes: 4 additions & 1 deletion pandas/io/formats/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -1246,7 +1246,10 @@ def _format_datetime64(x, tz=None, nat_rep='NaT'):
return nat_rep

if tz is not None or not isinstance(x, Timestamp):
x = Timestamp(x, tz=tz)
if getattr(x, 'tzinfo', None) is not None:
x = Timestamp(x).tz_convert(tz)
else:
x = Timestamp(x).tz_localize(tz)

return str(x)

Expand Down
6 changes: 6 additions & 0 deletions pandas/tests/indexes/datetimes/test_construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,12 @@ def test_construction_from_replaced_timestamps_with_dst(self):
tz='Australia/Melbourne')
tm.assert_index_equal(result, expected)

def test_construction_with_tz_and_tz_aware_dti(self):
# GH 23579
dti = date_range('2016-01-01', periods=3, tz='US/Central')
with pytest.raises(TypeError):
DatetimeIndex(dti, tz='Asia/Tokyo')


class TestTimeSeries(object):

Expand Down
16 changes: 13 additions & 3 deletions pandas/tests/scalar/timestamp/test_timestamp.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,10 @@ def test_constructor(self):
assert conversion.pydt_to_i8(result) == expected_tz

# should convert to UTC
result = Timestamp(result, tz='UTC')
if tz is not None:
result = Timestamp(result).tz_convert('UTC')
else:
result = Timestamp(result, tz='UTC')
expected_utc = expected - offset * 3600 * 1000000000
assert result.value == expected_utc
assert conversion.pydt_to_i8(result) == expected_utc
Expand Down Expand Up @@ -295,7 +298,7 @@ def test_constructor_with_stringoffset(self):
assert conversion.pydt_to_i8(result) == expected_tz

# should convert to UTC
result = Timestamp(result, tz='UTC')
result = Timestamp(result).tz_convert('UTC')
expected_utc = expected
assert result.value == expected_utc
assert conversion.pydt_to_i8(result) == expected_utc
Expand Down Expand Up @@ -558,7 +561,7 @@ def test_construct_timestamp_near_dst(self, offset):
# GH 20854
expected = Timestamp('2016-10-30 03:00:00{}'.format(offset),
tz='Europe/Helsinki')
result = Timestamp(expected, tz='Europe/Helsinki')
result = Timestamp(expected).tz_convert('Europe/Helsinki')
assert result == expected

@pytest.mark.parametrize('arg', [
Expand All @@ -580,6 +583,13 @@ def test_constructor_invalid_frequency(self):
with pytest.raises(ValueError, match="Invalid frequency:"):
Timestamp('2012-01-01', freq=[])

@pytest.mark.parametrize('box', [datetime, Timestamp])
def test_depreciate_tz_and_tzinfo_in_datetime_input(self, box):
# GH 23579
kwargs = {'year': 2018, 'month': 1, 'day': 1, 'tzinfo': utc}
with tm.assert_produces_warning(FutureWarning):
Timestamp(box(**kwargs), tz='US/Pacific')


class TestTimestamp(object):

Expand Down

0 comments on commit e2c4f04

Please sign in to comment.