Repository: narwhals-dev/narwhals

Issue:
bug: `to_py_scalar` converts `np.datetime64` to `int`
```python
In [8]: nw.to_py_scalar(np.datetime64('2000-01-01', 'ns'))
Out[8]: 946684800000000000

In [9]: nw.to_py_scalar(np.datetime64('2000-01-01', 'us'))
Out[9]: datetime.datetime(2000, 1, 1, 0, 0)
```

I think both should get converted to `datetime.datetime(2000, 1, 1, 0, 0)`

Discussion:

Patch:
diff --git a/narwhals/translate.py b/narwhals/translate.py
index 075f9b3a..65cc4422 100644
--- a/narwhals/translate.py
+++ b/narwhals/translate.py
@@ -857,6 +857,13 @@ def to_py_scalar(scalar_like: Any) -> Any:
         return scalar_like
 
     np = get_numpy()
+    if (
+        np
+        and isinstance(scalar_like, np.datetime64)
+        and scalar_like.dtype == "datetime64[ns]"
+    ):
+        return datetime(1970, 1, 1) + timedelta(microseconds=scalar_like.item() // 1000)
+
     if np and np.isscalar(scalar_like) and hasattr(scalar_like, "item"):
         return scalar_like.item()
 


Test patch:
diff --git a/tests/translate/to_py_scalar_test.py b/tests/translate/to_py_scalar_test.py
index 3519b5e8..b13ba009 100644
--- a/tests/translate/to_py_scalar_test.py
+++ b/tests/translate/to_py_scalar_test.py
@@ -2,7 +2,6 @@ from __future__ import annotations
 
 from datetime import datetime
 from datetime import timedelta
-from typing import TYPE_CHECKING
 from typing import Any
 
 import numpy as np
@@ -11,9 +10,7 @@ import pytest
 
 import narwhals.stable.v1 as nw
 from narwhals.dependencies import get_cudf
-
-if TYPE_CHECKING:
-    from tests.utils import ConstructorEager
+from tests.utils import PANDAS_VERSION
 
 
 @pytest.mark.parametrize(
@@ -28,31 +25,31 @@ if TYPE_CHECKING:
         (b"a", b"a"),
         (datetime(2021, 1, 1), datetime(2021, 1, 1)),
         (timedelta(days=1), timedelta(days=1)),
+        (pd.Timestamp("2020-01-01"), datetime(2020, 1, 1)),
+        (pd.Timedelta(days=3), timedelta(days=3)),
+        (np.datetime64("2020-01-01", "s"), datetime(2020, 1, 1)),
+        (np.datetime64("2020-01-01", "ms"), datetime(2020, 1, 1)),
+        (np.datetime64("2020-01-01", "us"), datetime(2020, 1, 1)),
+        (np.datetime64("2020-01-01", "ns"), datetime(2020, 1, 1)),
     ],
 )
 def test_to_py_scalar(
-    constructor_eager: ConstructorEager,
     input_value: Any,
     expected: Any,
-    request: pytest.FixtureRequest,
 ) -> None:
-    if isinstance(input_value, bytes) and "cudf" in str(constructor_eager):
-        request.applymarker(pytest.mark.xfail)
-    df = nw.from_native(constructor_eager({"a": [input_value]}))
-    output = nw.to_py_scalar(df["a"].item(0))
-    if expected == 1 and constructor_eager.__name__.startswith("pandas"):
+    output = nw.to_py_scalar(input_value)
+    if expected == 1:
         assert not isinstance(output, np.int64)
-    elif isinstance(expected, datetime) and constructor_eager.__name__.startswith(
-        "pandas"
-    ):
-        assert not isinstance(output, pd.Timestamp)
-    elif isinstance(expected, timedelta) and constructor_eager.__name__.startswith(
-        "pandas"
-    ):
-        assert not isinstance(output, pd.Timedelta)
     assert output == expected
 
 
+@pytest.mark.skipif(
+    PANDAS_VERSION < (1,), reason="there was a (better?) time when there was no pd.NA"
+)
+def test_na_to_py_scalar() -> None:
+    assert nw.to_py_scalar(pd.NA) is None
+
+
 @pytest.mark.parametrize(
     "input_value",
     [np.array([1, 2]), [1, 2, 3], {"a": [1, 2, 3]}],