Skip to content

Commit 953e66a

Browse files
committed
Merge pull request #11003 from jreback/tz
ENH: add Series.astype with the new tz dtype
2 parents 0b858f0 + d859b04 commit 953e66a

File tree

4 files changed

+87
-15
lines changed

4 files changed

+87
-15
lines changed

doc/source/timeseries.rst

+45-11
Original file line numberDiff line numberDiff line change
@@ -1753,22 +1753,56 @@ TZ aware Dtypes
17531753

17541754
.. versionadded:: 0.17.0
17551755

1756-
``Series/DatetimeIndex`` with a timezone naive value are represented with a dtype of ``datetime64[ns]``.
1756+
``Series/DatetimeIndex`` with a timezone **naive** value are represented with a dtype of ``datetime64[ns]``.
17571757

17581758
.. ipython:: python
17591759
1760-
dr = pd.date_range('20130101',periods=3)
1761-
dr
1762-
s = Series(dr)
1763-
s
1760+
dr_naive = pd.date_range('20130101',periods=3)
1761+
dr_naive
1762+
s_naive = Series(dr_naive)
1763+
s_naive
17641764
1765-
``Series/DatetimeIndex`` with a timezone aware value are represented with a dtype of ``datetime64[ns, tz]``.
1765+
``Series/DatetimeIndex`` with a timezone **aware** value are represented with a dtype of ``datetime64[ns, tz]``.
17661766

17671767
.. ipython:: python
17681768
1769-
dr = pd.date_range('20130101',periods=3,tz='US/Eastern')
1770-
dr
1771-
s = Series(dr)
1772-
s
1769+
dr_aware = pd.date_range('20130101',periods=3,tz='US/Eastern')
1770+
dr_aware
1771+
s_aware = Series(dr_aware)
1772+
s_aware
1773+
1774+
Both of these ``Series`` can be manipulated via the ``.dt`` accessor, see :ref:`here <basics.dt_accessors>`.
1775+
See the :ref:`docs <timeseries.dtypes>` for more details.
1776+
1777+
Further more you can ``.astype(...)`` timezone aware (and naive).
1778+
1779+
.. ipython:: python
1780+
1781+
# make this naive
1782+
s_aware.astype('datetime64[ns]')
1783+
1784+
# convert
1785+
s_aware.astype('datetime64[ns, CET]')
1786+
s_naive.astype('datetime64[ns, CET]')
1787+
1788+
.. note::
1789+
1790+
Using the ``.values`` accessor on a ``Series``, returns an numpy array of the data.
1791+
These values are converted to UTC, as numpy does not currently support timezones (even though it is *printing* in the local timezone!).
1792+
1793+
.. ipython:: python
1794+
1795+
s_naive.values
1796+
s_aware.values
1797+
1798+
Further note that once converted to a numpy array these would lose the tz tenor.
1799+
1800+
.. ipython:: python
1801+
1802+
Series(s_aware.values)
1803+
1804+
However, these can be easily converted
1805+
1806+
.. ipython:: python
17731807
1774-
Both of these ``Series`` can be manipulated via the ``.dt`` accessor, see the :ref:`docs <basics.dt_accessors>` as well.
1808+
Series(s_aware).dt.tz_localize('UTC').dt.tz_convert('US/Eastern')

doc/source/whatsnew/v0.17.0.txt

+7-4
Original file line numberDiff line numberDiff line change
@@ -447,9 +447,9 @@ Datetime with TZ
447447

448448
We are adding an implementation that natively supports datetime with timezones. A ``Series`` or a ``DataFrame`` column previously
449449
*could* be assigned a datetime with timezones, and would work as an ``object`` dtype. This had performance issues with a large
450-
number rows. (:issue:`8260`, :issue:`10763`)
450+
number rows. See the :ref:`docs <timeseries.timezone_series>` for more details. (:issue:`8260`, :issue:`10763`).
451451

452-
The new implementation allows for having a single-timezone across all rows, and operating on it in a performant manner.
452+
The new implementation allows for having a single-timezone across all rows, with operations in a performant manner.
453453

454454
.. ipython:: python
455455

@@ -469,13 +469,15 @@ This uses a new-dtype representation as well, that is very similar in look-and-f
469469
.. ipython:: python
470470

471471
df['B'].dtype
472-
type(df['B']).dtype
472+
type(df['B'].dtype)
473473

474474
.. note::
475475

476476
There is a slightly different string repr for the underlying ``DatetimeIndex`` as a result of the dtype changes, but
477477
functionally these are the same.
478478

479+
Previous Behavior:
480+
479481
.. code-block:: python
480482

481483
In [1]: pd.date_range('20130101',periods=3,tz='US/Eastern')
@@ -486,12 +488,13 @@ This uses a new-dtype representation as well, that is very similar in look-and-f
486488
In [2]: pd.date_range('20130101',periods=3,tz='US/Eastern').dtype
487489
Out[2]: dtype('<M8[ns]')
488490

491+
New Behavior:
492+
489493
.. ipython:: python
490494

491495
pd.date_range('20130101',periods=3,tz='US/Eastern')
492496
pd.date_range('20130101',periods=3,tz='US/Eastern').dtype
493497

494-
495498
.. _whatsnew_0170.api_breaking.convert_objects:
496499

497500
Changes to convert_objects

pandas/core/internals.py

+21
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
array_equivalent, _maybe_convert_string_to_object,
1919
is_categorical, needs_i8_conversion, is_datetimelike_v_numeric,
2020
is_internal_type)
21+
from pandas.core.dtypes import DatetimeTZDtype
2122

2223
from pandas.core.index import Index, MultiIndex, _ensure_index
2324
from pandas.core.indexing import maybe_convert_indices, length_of_indexer
@@ -1868,6 +1869,26 @@ def __init__(self, values, placement,
18681869
fastpath=True, placement=placement,
18691870
**kwargs)
18701871

1872+
def _astype(self, dtype, **kwargs):
1873+
"""
1874+
these automatically copy, so copy=True has no effect
1875+
raise on an except if raise == True
1876+
"""
1877+
1878+
# if we are passed a datetime64[ns, tz]
1879+
if com.is_datetime64tz_dtype(dtype):
1880+
dtype = DatetimeTZDtype(dtype)
1881+
1882+
values = self.values
1883+
if getattr(values,'tz',None) is None:
1884+
values = DatetimeIndex(values).tz_localize('UTC')
1885+
values = values.tz_convert(dtype.tz)
1886+
return self.make_block(values)
1887+
1888+
# delegate
1889+
return super(DatetimeBlock, self)._astype(dtype=dtype, **kwargs)
1890+
1891+
18711892
def _can_hold_element(self, element):
18721893
if is_list_like(element):
18731894
element = np.array(element)

pandas/tests/test_series.py

+14
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,20 @@ def test_constructor_with_datetime_tz(self):
10721072
expected = Series(DatetimeIndex(s._values).asobject)
10731073
assert_series_equal(result, expected)
10741074

1075+
result = Series(s.values).dt.tz_localize('UTC').dt.tz_convert(s.dt.tz)
1076+
assert_series_equal(result, s)
1077+
1078+
# astype - datetime64[ns, tz]
1079+
result = Series(s.values).astype('datetime64[ns, US/Eastern]')
1080+
assert_series_equal(result, s)
1081+
1082+
result = Series(s.values).astype(s.dtype)
1083+
assert_series_equal(result, s)
1084+
1085+
result = s.astype('datetime64[ns, CET]')
1086+
expected = Series(date_range('20130101 06:00:00',periods=3,tz='CET'))
1087+
assert_series_equal(result, expected)
1088+
10751089
# short str
10761090
self.assertTrue('datetime64[ns, US/Eastern]' in str(s))
10771091

0 commit comments

Comments
 (0)