diff --git a/doc/source/whatsnew/v0.19.2.txt b/doc/source/whatsnew/v0.19.2.txt index 4cd58f0148ae8..c9056054fb1d7 100644 --- a/doc/source/whatsnew/v0.19.2.txt +++ b/doc/source/whatsnew/v0.19.2.txt @@ -38,6 +38,7 @@ Other Enhancements Bug Fixes ~~~~~~~~~ +- Bug in fillna() in which timezone aware datetime64 values were incorrectly rounded (:issue:'14872') - Compat with ``dateutil==2.6.0``; segfault reported in the testing suite (:issue:`14621`) - Allow ``nanoseconds`` in ``Timestamp.replace`` as a kwarg (:issue:`14621`) - Bug in ``pd.read_csv`` in which aliasing was being done for ``na_values`` when passed in as a dictionary (:issue:`14203`) diff --git a/pandas/core/missing.py b/pandas/core/missing.py index f1191ff1c7009..e83a0518d97f6 100644 --- a/pandas/core/missing.py +++ b/pandas/core/missing.py @@ -10,9 +10,8 @@ from pandas.compat import range, string_types from pandas.types.common import (is_numeric_v_string_like, is_float_dtype, is_datetime64_dtype, - is_integer_dtype, _ensure_float64, - is_scalar, - _DATELIKE_DTYPES, + is_datetime64tz_dtype, is_integer_dtype, + _ensure_float64, is_scalar, needs_i8_conversion) from pandas.types.missing import isnull @@ -450,7 +449,7 @@ def pad_1d(values, limit=None, mask=None, dtype=None): _method = None if is_float_dtype(values): _method = getattr(algos, 'pad_inplace_%s' % dtype.name, None) - elif dtype in _DATELIKE_DTYPES or is_datetime64_dtype(values): + elif is_datetime64_dtype(dtype) or is_datetime64tz_dtype(dtype): _method = _pad_1d_datetime elif is_integer_dtype(values): values = _ensure_float64(values) @@ -475,7 +474,7 @@ def backfill_1d(values, limit=None, mask=None, dtype=None): _method = None if is_float_dtype(values): _method = getattr(algos, 'backfill_inplace_%s' % dtype.name, None) - elif dtype in _DATELIKE_DTYPES or is_datetime64_dtype(values): + elif is_datetime64_dtype(dtype) or is_datetime64tz_dtype(dtype): _method = _backfill_1d_datetime elif is_integer_dtype(values): values = _ensure_float64(values) @@ -501,7 +500,7 @@ def pad_2d(values, limit=None, mask=None, dtype=None): _method = None if is_float_dtype(values): _method = getattr(algos, 'pad_2d_inplace_%s' % dtype.name, None) - elif dtype in _DATELIKE_DTYPES or is_datetime64_dtype(values): + elif is_datetime64_dtype(dtype) or is_datetime64tz_dtype(dtype): _method = _pad_2d_datetime elif is_integer_dtype(values): values = _ensure_float64(values) @@ -531,7 +530,7 @@ def backfill_2d(values, limit=None, mask=None, dtype=None): _method = None if is_float_dtype(values): _method = getattr(algos, 'backfill_2d_inplace_%s' % dtype.name, None) - elif dtype in _DATELIKE_DTYPES or is_datetime64_dtype(values): + elif is_datetime64_dtype(dtype) or is_datetime64tz_dtype(dtype): _method = _backfill_2d_datetime elif is_integer_dtype(values): values = _ensure_float64(values) diff --git a/pandas/tests/series/test_missing.py b/pandas/tests/series/test_missing.py index 5666a07cad4b8..58f50ffbf08f5 100644 --- a/pandas/tests/series/test_missing.py +++ b/pandas/tests/series/test_missing.py @@ -17,6 +17,9 @@ from .common import TestData +import datetime +import pytz + def _skip_if_no_pchip(): try: @@ -908,6 +911,24 @@ def test_interp_timedelta64(self): index=pd.to_timedelta([1, 2, 4])) assert_series_equal(result, expected) + # GH 14872 + def test_dtype_utc(self): + + data = pd.Series([pd.NaT, pd.NaT, + datetime.datetime(2016, 12, 12, 22, 24, 6, 100001, + tzinfo=pytz.utc)]) + + filled = data.fillna(method='bfill') + + expected = pd.Series([ + datetime.datetime(2016, 12, 12, 22, 24, 6, + 100001, tzinfo=pytz.utc), + datetime.datetime(2016, 12, 12, 22, 24, 6, + 100001, tzinfo=pytz.utc), + datetime.datetime(2016, 12, 12, 22, 24, 6, + 100001, tzinfo=pytz.utc)]) + + assert_series_equal(filled, expected) if __name__ == '__main__': import nose diff --git a/pandas/types/common.py b/pandas/types/common.py index 754ff80924c07..5d161efa838de 100644 --- a/pandas/types/common.py +++ b/pandas/types/common.py @@ -22,6 +22,7 @@ _NS_DTYPE = np.dtype('M8[ns]') _TD_DTYPE = np.dtype('m8[ns]') _INT64_DTYPE = np.dtype(np.int64) + _DATELIKE_DTYPES = set([np.dtype(t) for t in ['M8[ns]', 'M8[ns]', 'm8[ns]', 'm8[ns]']])