Repository: pandas-dev/pandas

Issue:
BUG: Series[int] + datetime64 should raise
identified [here](https://github.com/pandas-dev/pandas/commit/a6c0d02c99cfaf90119d1316dd897019326472c1#r34783276).  We handle this correctly for timedelta64, but apparently not datetime64

```
iser = pd.Series(range(3))
dt = np.datetime64(3, "D")

>>> iser + dt
0   1970-01-04
1   1970-01-05
2   1970-01-06
```

Should behave like `iser + pd.Timestamp(dt)`:

```
>>> iser + pd.Timestamp(dt)
[...]
ValueError: Cannot add integral value to Timestamp without freq.
```
TST: Fix maybe_promote floating non-boxed tests
Like #28864, this involves changing the underlying function.  Some of this can be de-duplicated once these are in.

I think that this is the last of the xfails for non-boxed test cases.  Really looking forward to having this done with,.


Discussion:

Resolving patch:
diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py
--- a/pandas/core/ops/__init__.py
+++ b/pandas/core/ops/__init__.py
@@ -9,7 +9,7 @@
 
 import numpy as np
 
-from pandas._libs import Timedelta, lib, ops as libops
+from pandas._libs import Timedelta, Timestamp, lib, ops as libops
 from pandas.errors import NullFrequencyError
 from pandas.util._decorators import Appender
 
@@ -148,13 +148,24 @@ def maybe_upcast_for_op(obj, shape: Tuple[int, ...]):
     Be careful to call this *after* determining the `name` attribute to be
     attached to the result of the arithmetic operation.
     """
-    from pandas.core.arrays import TimedeltaArray
+    from pandas.core.arrays import DatetimeArray, TimedeltaArray
 
     if type(obj) is datetime.timedelta:
         # GH#22390  cast up to Timedelta to rely on Timedelta
         # implementation; otherwise operation against numeric-dtype
         # raises TypeError
         return Timedelta(obj)
+    elif isinstance(obj, np.datetime64):
+        # GH#28080 numpy casts integer-dtype to datetime64 when doing
+        #  array[int] + datetime64, which we do not allow
+        if isna(obj):
+            # Avoid possible ambiguities with pd.NaT
+            obj = obj.astype("datetime64[ns]")
+            right = np.broadcast_to(obj, shape)
+            return DatetimeArray(right)
+
+        return Timestamp(obj)
+
     elif isinstance(obj, np.timedelta64):
         if isna(obj):
             # wrapping timedelta64("NaT") in Timedelta returns NaT,
@@ -624,7 +635,13 @@ def wrapper(left, right):
 
         keep_null_freq = isinstance(
             right,
-            (ABCDatetimeIndex, ABCDatetimeArray, ABCTimedeltaIndex, ABCTimedeltaArray),
+            (
+                ABCDatetimeIndex,
+                ABCDatetimeArray,
+                ABCTimedeltaIndex,
+                ABCTimedeltaArray,
+                Timestamp,
+            ),
         )
 
         left, right = _align_method_SERIES(left, right)
@@ -635,13 +652,9 @@ def wrapper(left, right):
 
         rvalues = maybe_upcast_for_op(rvalues, lvalues.shape)
 
-        if should_extension_dispatch(lvalues, rvalues):
-            result = dispatch_to_extension_op(op, lvalues, rvalues, keep_null_freq)
-
-        elif is_timedelta64_dtype(rvalues) or isinstance(rvalues, ABCDatetimeArray):
-            # We should only get here with td64 rvalues with non-scalar values
-            #  for rvalues upcast by maybe_upcast_for_op
-            assert not isinstance(rvalues, (np.timedelta64, np.ndarray))
+        if should_extension_dispatch(left, rvalues) or isinstance(
+            rvalues, (ABCTimedeltaArray, ABCDatetimeArray, Timestamp)
+        ):
             result = dispatch_to_extension_op(op, lvalues, rvalues, keep_null_freq)
 
         else:


Test patch:
diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py
--- a/pandas/tests/arithmetic/test_numeric.py
+++ b/pandas/tests/arithmetic/test_numeric.py
@@ -73,10 +73,10 @@ def test_compare_invalid(self):
 
 
 # ------------------------------------------------------------------
-# Numeric dtypes Arithmetic with Timedelta Scalar
+# Numeric dtypes Arithmetic with Datetime/Timedelta Scalar
 
 
-class TestNumericArraylikeArithmeticWithTimedeltaLike:
+class TestNumericArraylikeArithmeticWithDatetimeLike:
 
     # TODO: also check name retentention
     @pytest.mark.parametrize("box_cls", [np.array, pd.Index, pd.Series])
@@ -235,6 +235,30 @@ def test_add_sub_timedeltalike_invalid(self, numeric_idx, other, box):
         with pytest.raises(TypeError):
             other - left
 
+    @pytest.mark.parametrize(
+        "other",
+        [
+            pd.Timestamp.now().to_pydatetime(),
+            pd.Timestamp.now(tz="UTC").to_pydatetime(),
+            pd.Timestamp.now().to_datetime64(),
+            pd.NaT,
+        ],
+    )
+    @pytest.mark.filterwarnings("ignore:elementwise comp:DeprecationWarning")
+    def test_add_sub_datetimelike_invalid(self, numeric_idx, other, box):
+        # GH#28080 numeric+datetime64 should raise; Timestamp raises
+        #  NullFrequencyError instead of TypeError so is excluded.
+        left = tm.box_expected(numeric_idx, box)
+
+        with pytest.raises(TypeError):
+            left + other
+        with pytest.raises(TypeError):
+            other + left
+        with pytest.raises(TypeError):
+            left - other
+        with pytest.raises(TypeError):
+            other - left
+
 
 # ------------------------------------------------------------------
 # Arithmetic