Repository: narwhals-dev/narwhals

Issue:
[Doc]: Add a docstring example in `narwhals.dtypes` for Datetime - beginner friendly
### What type of report is this?

Improvement

### Please describe the issue.

Thank you for contributing to Narwhals
May the Undwerwater Unicorn Magic be with You! 🌊🦄

Add a docstring example for Datetime - dtype, that will be visible here:
https://narwhals-dev.github.io/narwhals/api-reference/dtypes/#narwhals.dtypes.Datetime

Check https://github.com/narwhals-dev/narwhals/blob/main/CONTRIBUTING.md
for instructions how to set up your environment

Check https://narwhals-dev.github.io/narwhals/api-reference/dtypes/#narwhals.dtypes.List
as a reference of how it should look like

and https://github.com/narwhals-dev/narwhals/blob/main/narwhals/dtypes.py
as a reference on how to create docstrings

If you need any help just let us know!
Happy Contributing💻🎉

### If you have a suggestion on how it should be, add it below.

_No response_

Discussion:
Machele-codez: @MarcoGorelli can you help me out with the failing check? It doesn't seem to be an effect of some code I wrote :thinking: (I may be wrong)

Patch:
diff --git a/narwhals/dependencies.py b/narwhals/dependencies.py
index 463a64a6..61937f08 100644
--- a/narwhals/dependencies.py
+++ b/narwhals/dependencies.py
@@ -24,6 +24,9 @@ if TYPE_CHECKING:
     import pyarrow as pa
     import pyspark.sql as pyspark_sql
 
+    from narwhals.dataframe import DataFrame
+    from narwhals.dataframe import LazyFrame
+    from narwhals.series import Series
     from narwhals.typing import IntoSeries
 
 # We silently allow these but - given that they claim
@@ -320,6 +323,42 @@ def is_into_dataframe(native_dataframe: Any) -> bool:
     )
 
 
+def is_narwhals_dataframe(df: Any) -> TypeGuard[DataFrame[Any]]:
+    """Check whether `df` is a Narwhals DataFrame.
+
+    This is useful if you expect a user to pass in a Narwhals
+    DataFrame directly, and you want to catch both ``narwhals.DataFrame``
+    and ``narwhals.stable.v1.DataFrame`.
+    """
+    from narwhals.dataframe import DataFrame
+
+    return isinstance(df, DataFrame)
+
+
+def is_narwhals_lazyframe(lf: Any) -> TypeGuard[LazyFrame[Any]]:
+    """Check whether `lf` is a Narwhals LazyFrame.
+
+    This is useful if you expect a user to pass in a Narwhals
+    LazyFrame directly, and you want to catch both ``narwhals.LazyFrame``
+    and ``narwhals.stable.v1.LazyFrame`.
+    """
+    from narwhals.dataframe import LazyFrame
+
+    return isinstance(lf, LazyFrame)
+
+
+def is_narwhals_series(ser: Any) -> TypeGuard[Series[Any]]:
+    """Check whether `ser` is a Narwhals Series.
+
+    This is useful if you expect a user to pass in a Narwhals
+    Series directly, and you want to catch both ``narwhals.Series``
+    and ``narwhals.stable.v1.Series`.
+    """
+    from narwhals.series import Series
+
+    return isinstance(ser, Series)
+
+
 __all__ = [
     "get_cudf",
     "get_ibis",
@@ -336,6 +375,9 @@ __all__ = [
     "is_into_series",
     "is_modin_dataframe",
     "is_modin_series",
+    "is_narwhals_dataframe",
+    "is_narwhals_lazyframe",
+    "is_narwhals_series",
     "is_numpy_array",
     "is_pandas_dataframe",
     "is_pandas_index",
diff --git a/narwhals/dtypes.py b/narwhals/dtypes.py
index b0434125..b086d216 100644
--- a/narwhals/dtypes.py
+++ b/narwhals/dtypes.py
@@ -386,6 +386,33 @@ class Datetime(TemporalType):
 
     Notes:
         Adapted from [Polars implementation](https://github.com/pola-rs/polars/blob/py-1.7.1/py-polars/polars/datatypes/classes.py#L398-L457)
+
+    Examples:
+        >>> import pandas as pd
+        >>> import polars as pl
+        >>> import pyarrow as pa
+        >>> import pyarrow.compute as pc
+        >>> import narwhals as nw
+        >>> from datetime import datetime, timedelta
+        >>> data = [datetime(2024, 12, 9) + timedelta(days=n) for n in range(5)]
+        >>> ser_pd = (
+        ...     pd.Series(data)
+        ...     .dt.tz_localize("Africa/Accra")
+        ...     .astype("datetime64[ms, Africa/Accra]")
+        ... )
+        >>> ser_pl = (
+        ...     pl.Series(data).cast(pl.Datetime("ms")).dt.replace_time_zone("Africa/Accra")
+        ... )
+        >>> ser_pa = pc.assume_timezone(
+        ...     pa.chunked_array([data], type=pa.timestamp("ms")), "Africa/Accra"
+        ... )
+
+        >>> nw.from_native(ser_pd, series_only=True).dtype
+        Datetime(time_unit='ms', time_zone='Africa/Accra')
+        >>> nw.from_native(ser_pl, series_only=True).dtype
+        Datetime(time_unit='ms', time_zone='Africa/Accra')
+        >>> nw.from_native(ser_pa, series_only=True).dtype
+        Datetime(time_unit='ms', time_zone='Africa/Accra')
     """
 
     def __init__(
diff --git a/narwhals/stable/v1/_dtypes.py b/narwhals/stable/v1/_dtypes.py
index 22cf05db..41dc854f 100644
--- a/narwhals/stable/v1/_dtypes.py
+++ b/narwhals/stable/v1/_dtypes.py
@@ -37,6 +37,33 @@ class Datetime(NwDatetime):
 
     Notes:
         Adapted from [Polars implementation](https://github.com/pola-rs/polars/blob/py-1.7.1/py-polars/polars/datatypes/classes.py#L398-L457)
+
+    Examples:
+        >>> import pandas as pd
+        >>> import polars as pl
+        >>> import pyarrow as pa
+        >>> import pyarrow.compute as pc
+        >>> import narwhals as nw
+        >>> from datetime import datetime, timedelta
+        >>> data = [datetime(2024, 12, 9) + timedelta(days=n) for n in range(5)]
+        >>> ser_pd = (
+        ...     pd.Series(data)
+        ...     .dt.tz_localize("Africa/Accra")
+        ...     .astype("datetime64[ms, Africa/Accra]")
+        ... )
+        >>> ser_pl = (
+        ...     pl.Series(data).cast(pl.Datetime("ms")).dt.replace_time_zone("Africa/Accra")
+        ... )
+        >>> ser_pa = pc.assume_timezone(
+        ...     pa.chunked_array([data], type=pa.timestamp("ms")), "Africa/Accra"
+        ... )
+
+        >>> nw.from_native(ser_pd, series_only=True).dtype
+        Datetime(time_unit='ms', time_zone='Africa/Accra')
+        >>> nw.from_native(ser_pl, series_only=True).dtype
+        Datetime(time_unit='ms', time_zone='Africa/Accra')
+        >>> nw.from_native(ser_pa, series_only=True).dtype
+        Datetime(time_unit='ms', time_zone='Africa/Accra')
     """
 
     def __hash__(self) -> int:
diff --git a/narwhals/stable/v1/dependencies.py b/narwhals/stable/v1/dependencies.py
index 6a020622..8ad1ad20 100644
--- a/narwhals/stable/v1/dependencies.py
+++ b/narwhals/stable/v1/dependencies.py
@@ -15,6 +15,9 @@ from narwhals.dependencies import is_into_dataframe
 from narwhals.dependencies import is_into_series
 from narwhals.dependencies import is_modin_dataframe
 from narwhals.dependencies import is_modin_series
+from narwhals.dependencies import is_narwhals_dataframe
+from narwhals.dependencies import is_narwhals_lazyframe
+from narwhals.dependencies import is_narwhals_series
 from narwhals.dependencies import is_numpy_array
 from narwhals.dependencies import is_pandas_dataframe
 from narwhals.dependencies import is_pandas_index
@@ -43,6 +46,9 @@ __all__ = [
     "is_into_series",
     "is_modin_dataframe",
     "is_modin_series",
+    "is_narwhals_dataframe",
+    "is_narwhals_lazyframe",
+    "is_narwhals_series",
     "is_numpy_array",
     "is_pandas_dataframe",
     "is_pandas_index",


Test patch:
diff --git a/tests/dependencies/is_narwhals_dataframe_test.py b/tests/dependencies/is_narwhals_dataframe_test.py
new file mode 100644
index 00000000..0a25bc8b
--- /dev/null
+++ b/tests/dependencies/is_narwhals_dataframe_test.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+import narwhals as nw
+import narwhals.stable.v1 as nws
+from narwhals.stable.v1.dependencies import is_narwhals_dataframe
+
+if TYPE_CHECKING:
+    from tests.utils import ConstructorEager
+
+
+def test_is_narwhals_dataframe(constructor_eager: ConstructorEager) -> None:
+    df = constructor_eager({"col1": [1, 2], "col2": [3, 4]})
+
+    assert is_narwhals_dataframe(nw.from_native(df))
+    assert is_narwhals_dataframe(nws.from_native(df))
+    assert not is_narwhals_dataframe(df)
diff --git a/tests/dependencies/is_narwhals_lazyframe_test.py b/tests/dependencies/is_narwhals_lazyframe_test.py
new file mode 100644
index 00000000..1dcbe5fc
--- /dev/null
+++ b/tests/dependencies/is_narwhals_lazyframe_test.py
@@ -0,0 +1,19 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+import narwhals as nw
+import narwhals.stable.v1 as nws
+from narwhals.stable.v1.dependencies import is_narwhals_lazyframe
+from tests.utils import Constructor
+
+if TYPE_CHECKING:
+    from tests.utils import Constructor
+
+
+def test_is_narwhals_lazyframe(constructor: Constructor) -> None:
+    lf = constructor({"a": [1, 2, 3]})
+
+    assert is_narwhals_lazyframe(nw.from_native(lf).lazy())
+    assert is_narwhals_lazyframe(nws.from_native(lf).lazy())
+    assert not is_narwhals_lazyframe(lf)
diff --git a/tests/dependencies/is_narwhals_series_test.py b/tests/dependencies/is_narwhals_series_test.py
new file mode 100644
index 00000000..0beb3fc1
--- /dev/null
+++ b/tests/dependencies/is_narwhals_series_test.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+import narwhals as nw
+import narwhals.stable.v1 as nws
+from narwhals.stable.v1.dependencies import is_narwhals_series
+
+if TYPE_CHECKING:
+    from tests.utils import ConstructorEager
+
+
+def test_is_narwhals_series(constructor_eager: ConstructorEager) -> None:
+    df = constructor_eager({"col1": [1, 2], "col2": [3, 4]})
+
+    assert is_narwhals_series(nw.from_native(df, eager_only=True)["col1"])
+    assert is_narwhals_series(nws.from_native(df, eager_only=True)["col1"])
+    assert not is_narwhals_series(nw.from_native(df, eager_only=True)["col1"].to_native())