From ac2f8367098afcafa2666e26498d4eef0aa6b49e Mon Sep 17 00:00:00 2001 From: David Krych Date: Wed, 27 Jun 2018 15:08:42 -0400 Subject: [PATCH 01/11] Minimal modifications that pass new tests and fix #21469 + #21546. --- pandas/core/internals.py | 8 +++++--- pandas/core/series.py | 8 ++++++-- pandas/tests/frame/test_indexing.py | 14 ++++++++++++++ pandas/tests/series/indexing/test_boolean.py | 14 ++++++++++++++ pandas/tests/series/test_combine_concat.py | 13 +++++++++++++ 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/pandas/core/internals.py b/pandas/core/internals.py index fe508dc1bb0bc..f79c2ba40b1fb 100644 --- a/pandas/core/internals.py +++ b/pandas/core/internals.py @@ -1476,7 +1476,7 @@ def where(self, other, cond, align=True, errors='raise', if transpose: values = values.T - other = getattr(other, 'values', other) + other = getattr(other, '_values', getattr(other, 'values', other)) cond = getattr(cond, 'values', cond) # If the default broadcasting would go in the wrong direction, then @@ -1498,6 +1498,8 @@ def func(cond, values, other): values, other) try: + if isinstance(self, DatetimeTZBlock): + cond = cond.T return self._try_coerce_result(expressions.where( cond, values, other)) except Exception as detail: @@ -2888,8 +2890,8 @@ def _try_coerce_args(self, values, other): elif isinstance(other, self._holder): if other.tz != self.values.tz: raise ValueError("incompatible or non tz-aware value") - other = other.asi8 - other_mask = isna(other) + other_mask = _block_shape(isna(other), ndim=self.ndim) + other = _block_shape(other.asi8, ndim=self.ndim) elif isinstance(other, (np.datetime64, datetime, date)): other = tslib.Timestamp(other) tz = getattr(other, 'tz', None) diff --git a/pandas/core/series.py b/pandas/core/series.py index cdb901d18767c..74bd1d6b45f2e 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -22,6 +22,7 @@ is_float_dtype, is_extension_type, is_extension_array_dtype, + is_datetimelike, is_datetime64tz_dtype, is_timedelta64_dtype, is_object_dtype, @@ -78,6 +79,7 @@ from pandas._libs import index as libindex, tslib as libts, lib, iNaT from pandas.core.config import get_option from pandas.core.strings import StringMethods +from pandas.core.tools.datetimes import to_datetime import pandas.plotting._core as gfx @@ -2303,10 +2305,12 @@ def combine_first(self, other): new_index = self.index.union(other.index) this = self.reindex(new_index, copy=False) other = other.reindex(new_index, copy=False) + if is_datetimelike(this) and not is_datetimelike(other): + other = to_datetime(other) + # TODO: do we need name? name = ops.get_op_result_name(self, other) # noqa - rs_vals = com._where_compat(isna(this), other._values, this._values) - return self._constructor(rs_vals, index=new_index).__finalize__(self) + return this.where(notna(this), other) def update(self, other): """ diff --git a/pandas/tests/frame/test_indexing.py b/pandas/tests/frame/test_indexing.py index be37e696ea0a3..ba03fe5736ce3 100644 --- a/pandas/tests/frame/test_indexing.py +++ b/pandas/tests/frame/test_indexing.py @@ -2926,6 +2926,20 @@ def test_where_callable(self): tm.assert_frame_equal(result, (df + 2).where((df + 2) > 8, (df + 2) + 10)) + def test_where_tz_values(self): + dts1 = date_range('20150101', '20150105', tz='America/New_York') + df1 = DataFrame({'date': dts1}) + dts2 = date_range('20150103', '20150107', tz='America/New_York') + df2 = DataFrame({'date': dts2}) + mask = DataFrame(True, index=df1.index, columns=df2.columns) + mask.iloc[3:] = False + result = df1.where(mask, df2) + dts3 = DatetimeIndex(['20150101', '20150102', '20150103', + '20150106', '20150107'], + tz='America/New_York') + exp = DataFrame({'date': dts3}) + assert_frame_equal(exp, result) + def test_mask(self): df = DataFrame(np.random.randn(5, 3)) cond = df > 0 diff --git a/pandas/tests/series/indexing/test_boolean.py b/pandas/tests/series/indexing/test_boolean.py index 2aef0df5349cb..eb62e883b55e9 100644 --- a/pandas/tests/series/indexing/test_boolean.py +++ b/pandas/tests/series/indexing/test_boolean.py @@ -1,6 +1,7 @@ # coding=utf-8 # pylint: disable-msg=E1101,W0612 +import datetime import pytest import pandas as pd @@ -551,6 +552,19 @@ def test_where_datetime_conversion(): assert_series_equal(rs, expected) +def test_where_dt_tz_values(): + dts1 = pd.date_range('20150101', '20150105', tz='America/New_York') + df1 = pd.DataFrame({'date': dts1}) + dts2 = pd.date_range('20150103', '20150107', tz='America/New_York') + df2 = pd.DataFrame({'date': dts2}) + result = df1.date.where(df1.date < df1.date[3], df2.date) + exp_vals = pd.DatetimeIndex(['20150101', '20150102', '20150103', + '20150106', '20150107'], + tz='America/New_York') + exp = Series(exp_vals, name='date') + assert_series_equal(exp, result) + + def test_mask(): # compare with tested results in test_where s = Series(np.random.randn(5)) diff --git a/pandas/tests/series/test_combine_concat.py b/pandas/tests/series/test_combine_concat.py index f35cce6ac9d71..d3e4720a756f1 100644 --- a/pandas/tests/series/test_combine_concat.py +++ b/pandas/tests/series/test_combine_concat.py @@ -170,6 +170,19 @@ def get_result_type(dtype, dtype2): ]).dtype assert result.kind == expected + def test_combine_first_dt_tz_values(self): + dts1 = pd.date_range('20150101', '20150105', tz='America/New_York') + df1 = pd.DataFrame({'date': dts1}) + dts2 = pd.date_range('20160514', '20160518', tz='America/New_York') + df2 = pd.DataFrame({'date': dts2}, index=range(3, 8)) + result = df1.date.combine_first(df2.date) + exp_vals = pd.DatetimeIndex(['20150101', '20150102', '20150103', + '20150104', '20150105', '20160516', + '20160517', '20160518'], + tz='America/New_York') + exp = pd.Series(exp_vals, name='date') + assert_series_equal(exp, result) + def test_concat_empty_series_dtypes(self): # booleans From 770a22e18cf58c98805807fb973c0bcb58e5906e Mon Sep 17 00:00:00 2001 From: David Krych Date: Wed, 27 Jun 2018 15:35:19 -0400 Subject: [PATCH 02/11] Remove unused import. --- pandas/tests/series/indexing/test_boolean.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tests/series/indexing/test_boolean.py b/pandas/tests/series/indexing/test_boolean.py index eb62e883b55e9..f6c232d055552 100644 --- a/pandas/tests/series/indexing/test_boolean.py +++ b/pandas/tests/series/indexing/test_boolean.py @@ -1,7 +1,6 @@ # coding=utf-8 # pylint: disable-msg=E1101,W0612 -import datetime import pytest import pandas as pd From 04a05387e52ff2261c74563b0de0ce88f196e174 Mon Sep 17 00:00:00 2001 From: David Krych Date: Wed, 27 Jun 2018 15:55:12 -0400 Subject: [PATCH 03/11] Remove explicit DatetimeTZBlock check. --- pandas/core/internals.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/internals.py b/pandas/core/internals.py index f79c2ba40b1fb..e44c0f1b8e69d 100644 --- a/pandas/core/internals.py +++ b/pandas/core/internals.py @@ -1484,6 +1484,8 @@ def where(self, other, cond, align=True, errors='raise', if getattr(other, 'ndim', 0) >= 1: if values.ndim - 1 == other.ndim and axis == 1: other = other.reshape(tuple(other.shape + (1, ))) + elif transpose and values.ndim == self.ndim - 1: + cond = cond.T if not hasattr(cond, 'shape'): raise ValueError("where must have a condition that is ndarray " @@ -1498,8 +1500,6 @@ def func(cond, values, other): values, other) try: - if isinstance(self, DatetimeTZBlock): - cond = cond.T return self._try_coerce_result(expressions.where( cond, values, other)) except Exception as detail: From 6e6c6191d38abd17c3c5600140a694915260b82b Mon Sep 17 00:00:00 2001 From: David Krych Date: Wed, 27 Jun 2018 17:48:37 -0400 Subject: [PATCH 04/11] Update test_coercion; xfailing case now fixed. --- pandas/tests/indexing/test_coercion.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pandas/tests/indexing/test_coercion.py b/pandas/tests/indexing/test_coercion.py index de756375db8cb..e7daefffe5f6f 100644 --- a/pandas/tests/indexing/test_coercion.py +++ b/pandas/tests/indexing/test_coercion.py @@ -580,12 +580,11 @@ def test_where_series_datetime64(self, fill_val, exp_dtype): values = pd.Series(pd.date_range(fill_val, periods=4)) if fill_val.tz: exp = pd.Series([pd.Timestamp('2011-01-01'), - pd.Timestamp('2012-01-02 05:00'), + pd.Timestamp('2012-01-02 00:00', tz='US/Eastern'), pd.Timestamp('2011-01-03'), - pd.Timestamp('2012-01-04 05:00')]) - self._assert_where_conversion(obj, cond, values, exp, - 'datetime64[ns]') - pytest.xfail("ToDo: do not coerce to UTC, must be object") + pd.Timestamp('2012-01-04 00:00', + tz='US/Eastern')]) + self._assert_where_conversion(obj, cond, values, exp, exp_dtype) exp = pd.Series([pd.Timestamp('2011-01-01'), values[1], pd.Timestamp('2011-01-03'), values[3]]) From 8c022d96e1a7f8308b246c2b8159cdb51b66c79f Mon Sep 17 00:00:00 2001 From: David Krych Date: Wed, 27 Jun 2018 19:48:35 -0400 Subject: [PATCH 05/11] Remove orphaned private _where_compat method. --- pandas/core/common.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pandas/core/common.py b/pandas/core/common.py index 1de8269c9a0c6..ec516d9d80023 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -410,19 +410,6 @@ def _apply_if_callable(maybe_callable, obj, **kwargs): return maybe_callable -def _where_compat(mask, arr1, arr2): - if arr1.dtype == _NS_DTYPE and arr2.dtype == _NS_DTYPE: - new_vals = np.where(mask, arr1.view('i8'), arr2.view('i8')) - return new_vals.view(_NS_DTYPE) - - if arr1.dtype == _NS_DTYPE: - arr1 = tslib.ints_to_pydatetime(arr1.view('i8')) - if arr2.dtype == _NS_DTYPE: - arr2 = tslib.ints_to_pydatetime(arr2.view('i8')) - - return np.where(mask, arr1, arr2) - - def _dict_compat(d): """ Helper function to convert datetimelike-keyed dicts to Timestamp-keyed dict From 96a59f6703e24113c80a8f8069fbfa732c1cac60 Mon Sep 17 00:00:00 2001 From: David Krych Date: Wed, 27 Jun 2018 21:09:48 -0400 Subject: [PATCH 06/11] Remove newly-unused import --- pandas/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/common.py b/pandas/core/common.py index ec516d9d80023..7aa092c2354da 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -14,7 +14,7 @@ from pandas.compat import long, zip, iteritems, PY36, OrderedDict from pandas.core.config import get_option from pandas.core.dtypes.generic import ABCSeries, ABCIndex -from pandas.core.dtypes.common import _NS_DTYPE, is_integer +from pandas.core.dtypes.common import is_integer from pandas.core.dtypes.inference import _iterable_not_string from pandas.core.dtypes.missing import isna, isnull, notnull # noqa from pandas.core.dtypes.cast import construct_1d_object_array_from_listlike From 71a4fd4a72316bdf687203869df2f95f42d3d595 Mon Sep 17 00:00:00 2001 From: David Krych Date: Fri, 29 Jun 2018 09:51:32 -0400 Subject: [PATCH 07/11] Remove no-longer-relevant comment and no-op code line. --- pandas/core/series.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 74bd1d6b45f2e..eb333d6b451ee 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2308,8 +2308,6 @@ def combine_first(self, other): if is_datetimelike(this) and not is_datetimelike(other): other = to_datetime(other) - # TODO: do we need name? - name = ops.get_op_result_name(self, other) # noqa return this.where(notna(this), other) def update(self, other): From 7c1464387488294f5f262658a5d9b8d605e255a0 Mon Sep 17 00:00:00 2001 From: David Krych Date: Fri, 29 Jun 2018 14:04:13 -0400 Subject: [PATCH 08/11] Clean up unit tests. --- pandas/tests/frame/test_indexing.py | 23 ++++++++++---------- pandas/tests/series/indexing/test_boolean.py | 20 ++++++++--------- pandas/tests/series/test_combine_concat.py | 21 +++++++++--------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/pandas/tests/frame/test_indexing.py b/pandas/tests/frame/test_indexing.py index 184a9106dcf63..24d6624a26e0b 100644 --- a/pandas/tests/frame/test_indexing.py +++ b/pandas/tests/frame/test_indexing.py @@ -36,6 +36,7 @@ import pandas.util.testing as tm from pandas.tests.frame.common import TestData +from pandas.conftest import tz_naive_fixture class TestDataFrameIndexing(TestData): @@ -2936,18 +2937,18 @@ def test_where_callable(self): tm.assert_frame_equal(result, (df + 2).where((df + 2) > 8, (df + 2) + 10)) - def test_where_tz_values(self): - dts1 = date_range('20150101', '20150105', tz='America/New_York') - df1 = DataFrame({'date': dts1}) - dts2 = date_range('20150103', '20150107', tz='America/New_York') - df2 = DataFrame({'date': dts2}) - mask = DataFrame(True, index=df1.index, columns=df2.columns) - mask.iloc[3:] = False + def test_where_tz_values(self, tz_naive_fixture): + df1 = DataFrame(DatetimeIndex(['20150101', '20150102', '20150103'], + tz=tz_naive_fixture), + columns=['date']) + df2 = DataFrame(DatetimeIndex(['20150103', '20150104', '20150105'], + tz=tz_naive_fixture), + columns=['date']) + mask = DataFrame([True, True, False], columns=['date']) + exp = DataFrame(DatetimeIndex(['20150101', '20150102', '20150105'], + tz=tz_naive_fixture), + columns=['date']) result = df1.where(mask, df2) - dts3 = DatetimeIndex(['20150101', '20150102', '20150103', - '20150106', '20150107'], - tz='America/New_York') - exp = DataFrame({'date': dts3}) assert_frame_equal(exp, result) def test_mask(self): diff --git a/pandas/tests/series/indexing/test_boolean.py b/pandas/tests/series/indexing/test_boolean.py index f6c232d055552..19b5d11de91fb 100644 --- a/pandas/tests/series/indexing/test_boolean.py +++ b/pandas/tests/series/indexing/test_boolean.py @@ -15,6 +15,7 @@ from pandas.util.testing import (assert_series_equal) import pandas.util.testing as tm +from pandas.conftest import tz_naive_fixture def test_getitem_boolean(test_data): @@ -551,16 +552,15 @@ def test_where_datetime_conversion(): assert_series_equal(rs, expected) -def test_where_dt_tz_values(): - dts1 = pd.date_range('20150101', '20150105', tz='America/New_York') - df1 = pd.DataFrame({'date': dts1}) - dts2 = pd.date_range('20150103', '20150107', tz='America/New_York') - df2 = pd.DataFrame({'date': dts2}) - result = df1.date.where(df1.date < df1.date[3], df2.date) - exp_vals = pd.DatetimeIndex(['20150101', '20150102', '20150103', - '20150106', '20150107'], - tz='America/New_York') - exp = Series(exp_vals, name='date') +def test_where_dt_tz_values(tz_naive_fixture): + ser1 = pd.Series(pd.DatetimeIndex(['20150101', '20150102', '20150103'], + tz=tz_naive_fixture)) + ser2 = pd.Series(pd.DatetimeIndex(['20160514', '20160515', '20160516'], + tz=tz_naive_fixture)) + mask = pd.Series([True, True, False]) + result = ser1.where(mask, ser2) + exp = pd.Series(pd.DatetimeIndex(['20150101', '20150102', '20160516'], + tz=tz_naive_fixture)) assert_series_equal(exp, result) diff --git a/pandas/tests/series/test_combine_concat.py b/pandas/tests/series/test_combine_concat.py index d3e4720a756f1..c1891430683da 100644 --- a/pandas/tests/series/test_combine_concat.py +++ b/pandas/tests/series/test_combine_concat.py @@ -170,17 +170,18 @@ def get_result_type(dtype, dtype2): ]).dtype assert result.kind == expected - def test_combine_first_dt_tz_values(self): - dts1 = pd.date_range('20150101', '20150105', tz='America/New_York') - df1 = pd.DataFrame({'date': dts1}) - dts2 = pd.date_range('20160514', '20160518', tz='America/New_York') - df2 = pd.DataFrame({'date': dts2}, index=range(3, 8)) - result = df1.date.combine_first(df2.date) + def test_combine_first_dt_tz_values(self, tz_naive_fixture): + ser1 = pd.Series(pd.DatetimeIndex(['20150101', '20150102', '20150103'], + tz=tz_naive_fixture), + name='ser1') + ser2 = pd.Series(pd.DatetimeIndex(['20160514', '20160515', '20160516'], + tz=tz_naive_fixture), + index=[2, 3, 4], name='ser2') + result = ser1.combine_first(ser2) exp_vals = pd.DatetimeIndex(['20150101', '20150102', '20150103', - '20150104', '20150105', '20160516', - '20160517', '20160518'], - tz='America/New_York') - exp = pd.Series(exp_vals, name='date') + '20160515', '20160516'], + tz=tz_naive_fixture) + exp = pd.Series(exp_vals, name='ser1') assert_series_equal(exp, result) def test_concat_empty_series_dtypes(self): From 3d072c5b882c0189220353099330476e256c7529 Mon Sep 17 00:00:00 2001 From: David Krych Date: Fri, 29 Jun 2018 16:23:31 -0400 Subject: [PATCH 09/11] Fix unneeded imports. --- pandas/tests/frame/test_indexing.py | 1 - pandas/tests/series/indexing/test_boolean.py | 1 - 2 files changed, 2 deletions(-) diff --git a/pandas/tests/frame/test_indexing.py b/pandas/tests/frame/test_indexing.py index 24d6624a26e0b..90f3bede482c6 100644 --- a/pandas/tests/frame/test_indexing.py +++ b/pandas/tests/frame/test_indexing.py @@ -36,7 +36,6 @@ import pandas.util.testing as tm from pandas.tests.frame.common import TestData -from pandas.conftest import tz_naive_fixture class TestDataFrameIndexing(TestData): diff --git a/pandas/tests/series/indexing/test_boolean.py b/pandas/tests/series/indexing/test_boolean.py index 19b5d11de91fb..5cfb9b1ff4292 100644 --- a/pandas/tests/series/indexing/test_boolean.py +++ b/pandas/tests/series/indexing/test_boolean.py @@ -15,7 +15,6 @@ from pandas.util.testing import (assert_series_equal) import pandas.util.testing as tm -from pandas.conftest import tz_naive_fixture def test_getitem_boolean(test_data): From f812e78e15b48d188cb9f9e4cf735a4f9dce1a67 Mon Sep 17 00:00:00 2001 From: David Krych Date: Fri, 29 Jun 2018 16:35:16 -0400 Subject: [PATCH 10/11] Add whatsnew. --- doc/source/whatsnew/v0.24.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index 1ab67bd80a5e8..d3db940786e64 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -185,6 +185,8 @@ Timezones - Bug in :meth:`Series.truncate` with a tz-aware :class:`DatetimeIndex` which would cause a core dump (:issue:`9243`) - Bug in :class:`Series` constructor which would coerce tz-aware and tz-naive :class:`Timestamp`s to tz-aware (:issue:`13051`) - Bug in :class:`Index` with ``datetime64[ns, tz]`` dtype that did not localize integer data correctly (:issue:`20964`) +- Bug in :meth:`Series.combine_first` with ``datetime64[ns, tz]`` dtype which would return tz-naive result (:issue:`21469`) +- Bugs in :meth:`Series.where` and :meth:`DataFrame.where` with ``datetime64[ns, tz]`` dtype (:issue:`21546`) Offsets ^^^^^^^ From c97b951f434e02cc98d986ac7c1a8016fb7ca37f Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Mon, 2 Jul 2018 19:51:58 -0400 Subject: [PATCH 11/11] doc --- doc/source/whatsnew/v0.24.0.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index 061fd032d216e..dfb7a3675fdd5 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -254,7 +254,6 @@ Timezones - Bug in :meth:`Series.truncate` with a tz-aware :class:`DatetimeIndex` which would cause a core dump (:issue:`9243`) - Bug in :class:`Series` constructor which would coerce tz-aware and tz-naive :class:`Timestamp`s to tz-aware (:issue:`13051`) - Bug in :class:`Index` with ``datetime64[ns, tz]`` dtype that did not localize integer data correctly (:issue:`20964`) -- Bug in :meth:`Series.combine_first` with ``datetime64[ns, tz]`` dtype which would return tz-naive result (:issue:`21469`) - Bug in :class:`DatetimeIndex` where constructing with an integer and tz would not localize correctly (:issue:`12619`) Offsets @@ -288,15 +287,18 @@ Indexing ^^^^^^^^ - The traceback from a ``KeyError`` when asking ``.loc`` for a single missing label is now shorter and more clear (:issue:`21557`) -- When ``.ix`` is asked for a missing integer label in a :class:`MultiIndex` with a first level of integer type, it now raises a ``KeyError`` - consistently with the case of a flat :class:`Int64Index` - rather than falling back to positional indexing (:issue:`21593`) +- When ``.ix`` is asked for a missing integer label in a :class:`MultiIndex` with a first level of integer type, it now raises a ``KeyError``, consistently with the case of a flat :class:`Int64Index, rather than falling back to positional indexing (:issue:`21593`) - Bug in :meth:`DatetimeIndex.reindex` when reindexing a tz-naive and tz-aware :class:`DatetimeIndex` (:issue:`8306`) - Bug in :class:`DataFrame` when setting values with ``.loc`` and a timezone aware :class:`DatetimeIndex` (:issue:`11365`) - Bug when indexing :class:`DatetimeIndex` with nanosecond resolution dates and timezones (:issue:`11679`) -- Bug in :meth:`Series.where` and :meth:`DataFrame.where` with ``datetime64[ns, tz]`` dtype (:issue:`21546`) -- Bug in :func:`DataFrame.fillna` where a ``ValueError`` would raise when one column contained a ``datetime64[ns, tz]`` dtype (:issue:`15522`) - +Missing +^^^^^^^ + +- Bug in :func:`DataFrame.fillna` where a ``ValueError`` would raise when one column contained a ``datetime64[ns, tz]`` dtype (:issue:`15522`) + MultiIndex ^^^^^^^^^^ @@ -337,6 +339,8 @@ Reshaping ^^^^^^^^^ - Bug in :func:`pandas.concat` when joining resampled DataFrames with timezone aware index (:issue:`13783`) +- Bug in :meth:`Series.combine_first` with ``datetime64[ns, tz]`` dtype which would return tz-naive result (:issue:`21469`) +- Bug in :meth:`Series.where` and :meth:`DataFrame.where` with ``datetime64[ns, tz]`` dtype (:issue:`21546`) - -