Skip to content

Commit

Permalink
Add timetz attribute to DatetimeIndex (pandas-dev#22132)
Browse files Browse the repository at this point in the history
  • Loading branch information
jquinon authored and jreback committed Aug 9, 2018
1 parent ec58e4e commit 475e391
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 4 deletions.
2 changes: 2 additions & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ These can be accessed like ``Series.dt.<property>``.

Series.dt.date
Series.dt.time
Series.dt.timetz
Series.dt.year
Series.dt.month
Series.dt.day
Expand Down Expand Up @@ -1739,6 +1740,7 @@ Time/Date Components
DatetimeIndex.nanosecond
DatetimeIndex.date
DatetimeIndex.time
DatetimeIndex.timetz
DatetimeIndex.dayofyear
DatetimeIndex.weekofyear
DatetimeIndex.week
Expand Down
1 change: 1 addition & 0 deletions doc/source/timeseries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,7 @@ There are several time/date properties that one can access from ``Timestamp`` or
nanosecond,"The nanoseconds of the datetime"
date,"Returns datetime.date (does not contain timezone information)"
time,"Returns datetime.time (does not contain timezone information)"
timetz,"Returns datetime.time as local time with timezone information"
dayofyear,"The ordinal day of year"
weekofyear,"The week ordinal of the year"
week,"The week ordinal of the year"
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ Other Enhancements
The default compression for ``to_csv``, ``to_json``, and ``to_pickle`` methods has been updated to ``'infer'`` (:issue:`22004`).
- :func:`to_timedelta` now supports iso-formated timedelta strings (:issue:`21877`)
- :class:`Series` and :class:`DataFrame` now support :class:`Iterable` in constructor (:issue:`2193`)
- :class:`DatetimeIndex` gained :attr:`DatetimeIndex.timetz` attribute. Returns local time with timezone information. (:issue:`21358`)

.. _whatsnew_0240.api_breaking:

Expand Down
2 changes: 1 addition & 1 deletion pandas/_libs/tslib.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ cdef inline object create_time_from_ts(
int64_t value, npy_datetimestruct dts,
object tz, object freq):
""" convenience routine to construct a datetime.time from its parts """
return time(dts.hour, dts.min, dts.sec, dts.us)
return time(dts.hour, dts.min, dts.sec, dts.us, tz)


def ints_to_pydatetime(int64_t[:] arr, tz=None, freq=None, box="datetime"):
Expand Down
8 changes: 8 additions & 0 deletions pandas/core/arrays/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,14 @@ def time(self):

return tslib.ints_to_pydatetime(timestamps, box="time")

@property
def timetz(self):
"""
Returns numpy array of datetime.time also containing timezone
information. The time part of the Timestamps.
"""
return tslib.ints_to_pydatetime(self.asi8, self.tz, box="time")

@property
def date(self):
"""
Expand Down
3 changes: 2 additions & 1 deletion pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ class DatetimeIndex(DatetimeArrayMixin, DatelikeOps, TimelikeOps,
nanosecond
date
time
timetz
dayofyear
weekofyear
week
Expand Down Expand Up @@ -260,7 +261,7 @@ def _add_comparison_methods(cls):
'dayofyear', 'quarter', 'days_in_month',
'daysinmonth', 'microsecond',
'nanosecond']
_other_ops = ['date', 'time']
_other_ops = ['date', 'time', 'timetz']
_datetimelike_ops = _field_ops + _object_ops + _bool_ops + _other_ops
_datetimelike_methods = ['to_period', 'tz_localize',
'tz_convert',
Expand Down
14 changes: 14 additions & 0 deletions pandas/tests/indexes/datetimes/test_timezones.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,20 @@ def test_time_accessor(self, dtype):

tm.assert_numpy_array_equal(result, expected)

def test_timetz_accessor(self, tz_naive_fixture):
# GH21358
if tz_naive_fixture is not None:
tz = dateutil.tz.gettz(tz_naive_fixture)
else:
tz = None

expected = np.array([time(10, 20, 30, tzinfo=tz), pd.NaT])

index = DatetimeIndex(['2018-06-04 10:20:30', pd.NaT], tz=tz)
result = index.timetz

tm.assert_numpy_array_equal(result, expected)

def test_dti_drop_dont_lose_tz(self):
# GH#2621
ind = date_range("2012-12-01", periods=10, tz="utc")
Expand Down
19 changes: 18 additions & 1 deletion pandas/tests/scalar/timestamp/test_timezones.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""
Tests for Timestamp timezone-related methods
"""
from datetime import date, timedelta
from datetime import datetime, date, timedelta

from distutils.version import LooseVersion
import pytest
Expand Down Expand Up @@ -290,3 +290,20 @@ def test_timestamp_add_timedelta_push_over_dst_boundary(self, tz):
expected = Timestamp('3/11/2012 05:00', tz=tz)

assert result == expected

def test_timestamp_timetz_equivalent_with_datetime_tz(self,
tz_naive_fixture):
# GH21358
if tz_naive_fixture is not None:
tz = dateutil.tz.gettz(tz_naive_fixture)
else:
tz = None

stamp = Timestamp('2018-06-04 10:20:30', tz=tz)
_datetime = datetime(2018, 6, 4, hour=10,
minute=20, second=30, tzinfo=tz)

result = stamp.timetz()
expected = _datetime.timetz()

assert result == expected
19 changes: 18 additions & 1 deletion pandas/tests/series/test_datetime_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import calendar
import pytest

from datetime import datetime, date
from datetime import datetime, time, date

import numpy as np
import pandas as pd
Expand All @@ -16,6 +16,8 @@
PeriodIndex, DatetimeIndex, TimedeltaIndex)
import pandas.core.common as com

import dateutil

from pandas.util.testing import assert_series_equal
import pandas.util.testing as tm

Expand Down Expand Up @@ -459,3 +461,18 @@ def test_datetime_understood(self):
expected = pd.Series(pd.to_datetime([
'2011-12-26', '2011-12-27', '2011-12-28']))
tm.assert_series_equal(result, expected)

def test_dt_timetz_accessor(self, tz_naive_fixture):
# GH21358
if tz_naive_fixture is not None:
tz = dateutil.tz.gettz(tz_naive_fixture)
else:
tz = None

dtindex = pd.DatetimeIndex(['2014-04-04 23:56', '2014-07-18 21:24',
'2015-11-22 22:14'], tz=tz)
s = Series(dtindex)
expected = Series([time(23, 56, tzinfo=tz), time(21, 24, tzinfo=tz),
time(22, 14, tzinfo=tz)])
result = s.dt.timetz
tm.assert_series_equal(result, expected)

0 comments on commit 475e391

Please sign in to comment.