diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index 9ea7b740bae8f..3463351d8e349 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -366,3 +366,5 @@ Bug Fixes - Bug in ``pd.read_csv()`` for the C engine where ``usecols`` were being indexed incorrectly with ``parse_dates`` (:issue:`14792`) + +- Bug in ``Series.dt.round`` inconsistent behaviour on NAT's with different arguments (:issue:`14940`) \ No newline at end of file diff --git a/pandas/tseries/base.py b/pandas/tseries/base.py index 63e56e09e91fe..a8dd2238c2063 100644 --- a/pandas/tseries/base.py +++ b/pandas/tseries/base.py @@ -84,6 +84,7 @@ def _round(self, freq, rounder): values = _ensure_datetimelike_to_i8(self) result = (unit * rounder(values / float(unit))).astype('i8') + result = self._maybe_mask_results(result, fill_value=tslib.NaT) attribs = self._get_attributes_dict() if 'freq' in attribs: attribs['freq'] = None diff --git a/pandas/tseries/tests/test_timeseries.py b/pandas/tseries/tests/test_timeseries.py index b7e77c2eb770a..1d622cbd46997 100644 --- a/pandas/tseries/tests/test_timeseries.py +++ b/pandas/tseries/tests/test_timeseries.py @@ -4441,6 +4441,15 @@ def test_nat_operations(self): self.assertEqual(s.min(), exp) self.assertEqual(s.max(), exp) + def test_round_nat(self): + # GH14940 + s = Series([pd.NaT]) + expected = Series(pd.NaT) + for method in ["round", "floor", "ceil"]: + round_method = getattr(s.dt, method) + for freq in ["s", "5s", "min", "5min", "h", "5h"]: + assert_series_equal(round_method(freq), expected) + class TestTimestamp(tm.TestCase): def test_class_ops_pytz(self): @@ -4849,6 +4858,15 @@ def test_is_leap_year(self): self.assertFalse(pd.NaT.is_leap_year) self.assertIsInstance(pd.NaT.is_leap_year, bool) + def test_round_nat(self): + # GH14940 + ts = Timestamp('nat') + print(dir(ts)) + for method in ["round", "floor", "ceil"]: + round_method = getattr(ts, method) + for freq in ["s", "5s", "min", "5min", "h", "5h"]: + self.assertIs(round_method(freq), ts) + class TestSlicing(tm.TestCase): def test_slice_year(self): diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index 9a20c36638bce..215e15d60837a 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -885,7 +885,12 @@ def _make_nan_func(func_name): f.__name__ = func_name return f + +# GH14940 +_round_methods = ['round', 'floor', 'ceil'] + _nat_methods = ['date', 'now', 'replace', 'to_pydatetime', 'today'] +_nat_methods.extend(_round_methods) _nan_methods = ['weekday', 'isoweekday', 'total_seconds'] @@ -895,7 +900,7 @@ _implemented_methods.extend(_nan_methods) for _method_name in _nat_methods: # not all methods exist in all versions of Python - if hasattr(NaTType, _method_name): + if hasattr(NaTType, _method_name) or _method_name in _round_methods: setattr(NaTType, _method_name, _make_nat_func(_method_name)) for _method_name in _nan_methods: