Skip to content
35 changes: 12 additions & 23 deletions pandas/core/internals/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2184,7 +2184,7 @@ def _holder(self):

@property
def fill_value(self):
return tslibs.iNaT
return np.datetime64("NaT", "ns")

def get_values(self, dtype=None):
"""
Expand Down Expand Up @@ -2266,14 +2266,9 @@ def _can_hold_element(self, element):
if self.is_datetimetz:
return tz_compare(element.tzinfo, self.dtype.tz)
return element.tzinfo is None
elif is_integer(element):
return element == tslibs.iNaT

return is_valid_nat_for_dtype(element, self.dtype)

def _coerce_values(self, values):
return values.view("i8")

def _try_coerce_args(self, other):
"""
Coerce other to dtype 'i8'. NaN and NaT convert to
Expand All @@ -2290,16 +2285,15 @@ def _try_coerce_args(self, other):
base-type other
"""
if is_valid_nat_for_dtype(other, self.dtype):
other = tslibs.iNaT
elif is_integer(other) and other == tslibs.iNaT:
pass
other = np.datetime64("NaT", "ns")
elif isinstance(other, (datetime, np.datetime64, date)):
other = self._box_func(other)
if getattr(other, "tz") is not None:
raise TypeError("cannot coerce a Timestamp with a tz on a naive Block")
other = other.asm8.view("i8")
other = other.asm8
elif hasattr(other, "dtype") and is_datetime64_dtype(other):
other = other.astype("i8", copy=False).view("i8")
# TODO: can we get here with non-nano?
pass
else:
# coercion issues
# let higher levels handle
Expand Down Expand Up @@ -2458,8 +2452,7 @@ def _slice(self, slicer):
return self.values[slicer]

def _coerce_values(self, values):
# asi8 is a view, needs copy
return _block_shape(values.view("i8"), ndim=self.ndim)
return _block_shape(values, ndim=self.ndim)

def _try_coerce_args(self, other):
"""
Expand All @@ -2484,21 +2477,17 @@ def _try_coerce_args(self, other):
other = self._holder(other, dtype=self.dtype)

elif is_valid_nat_for_dtype(other, self.dtype):
other = tslibs.iNaT
elif is_integer(other) and other == tslibs.iNaT:
pass
other = np.datetime64("NaT", "ns")
elif isinstance(other, self._holder):
if other.tz != self.values.tz:
if not tz_compare(other.tz, self.values.tz):
raise ValueError("incompatible or non tz-aware value")
other = _block_shape(other.asi8, ndim=self.ndim)

elif isinstance(other, (np.datetime64, datetime, date)):
other = tslibs.Timestamp(other)
tz = getattr(other, "tz", None)

# test we can have an equal time zone
if tz is None or str(tz) != str(self.values.tz):
if not tz_compare(other.tz, self.values.tz):
raise ValueError("incompatible or non tz-aware value")
other = other.value
else:
raise TypeError(other)

Expand Down Expand Up @@ -2654,8 +2643,8 @@ def fillna(self, value, **kwargs):

def _try_coerce_args(self, other):
"""
Coerce values and other to int64, with null values converted to
iNaT. values is always ndarray-like, other may not be
Coerce values and other to datetime64[ns], with null values
converted to datetime64("NaT", "ns").

Parameters
----------
Expand Down
18 changes: 10 additions & 8 deletions pandas/core/nanops.py
Original file line number Diff line number Diff line change
Expand Up @@ -1360,14 +1360,6 @@ def _nanpercentile_1d(values, mask, q, na_value, interpolation):
quantiles : scalar or array
"""
# mask is Union[ExtensionArray, ndarray]
if values.dtype.kind == "m":
# need to cast to integer to avoid rounding errors in numpy
result = _nanpercentile_1d(values.view("i8"), mask, q, na_value, interpolation)

# Note: we have to do do `astype` and not view because in general we
# have float result at this point, not i8
return result.astype(values.dtype)

values = values[~mask]

if len(values) == 0:
Expand Down Expand Up @@ -1399,6 +1391,16 @@ def nanpercentile(values, q, axis, na_value, mask, ndim, interpolation):
-------
quantiles : scalar or array
"""
if values.dtype.kind in ["m", "M"]:
# need to cast to integer to avoid rounding errors in numpy
result = nanpercentile(
values.view("i8"), q, axis, na_value.view("i8"), mask, ndim, interpolation
)

# Note: we have to do do `astype` and not view because in general we
# have float result at this point, not i8
return result.astype(values.dtype)

if not lib.is_scalar(mask) and mask.any():
if ndim == 1:
return _nanpercentile_1d(
Expand Down
7 changes: 5 additions & 2 deletions pandas/tests/frame/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,7 @@ def test_fancy_index_int_labels_exceptions(self, float_frame):
with pytest.raises(KeyError, match=msg):
float_frame.ix[:, ["E"]] = 1

# FIXME: don't leave commented-out
# partial setting now allows this GH2578
# pytest.raises(KeyError, float_frame.ix.__setitem__,
# (slice(None, None), 'E'), 1)
Expand Down Expand Up @@ -1676,9 +1677,11 @@ def test_setitem_single_column_mixed_datetime(self):
)
assert_series_equal(result, expected)

# set an allowable datetime64 type
# GH#16674 iNaT is treated as an integer when given by the user
df.loc["b", "timestamp"] = iNaT
assert isna(df.loc["b", "timestamp"])
assert not isna(df.loc["b", "timestamp"])
assert df["timestamp"].dtype == np.object_
assert df.loc["b", "timestamp"] == iNaT

# allow this syntax
df.loc["c", "timestamp"] = np.nan
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/internals/test_internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ def test_try_coerce_arg(self):
vals = (np.datetime64("2010-10-10"), datetime(2010, 10, 10), date(2010, 10, 10))
for val in vals:
coerced = block._try_coerce_args(val)
assert np.int64 == type(coerced)
assert np.datetime64 == type(coerced)
assert pd.Timestamp("2010-10-10") == pd.Timestamp(coerced)


Expand Down