diff --git a/doc/whats-new.rst b/doc/whats-new.rst index a807a793c71..aab51d71b09 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -69,6 +69,10 @@ Internal Changes ~~~~~~~~~~~~~~~~ - Move non-CF related ``ensure_dtype_not_object`` from conventions to backends (:pull:`9828`). By `Kai Mühlbauer `_. +- Move handling of scalar datetimes into ``_possibly_convert_objects`` + within ``as_compatible_data``. This is consistent with how lists of these objects + will be converted (:pull:`9900`). + By `Kai Mühlbauer `_. .. _whats-new.2024.11.0: diff --git a/xarray/core/variable.py b/xarray/core/variable.py index 07113d66b5b..9d56555f31b 100644 --- a/xarray/core/variable.py +++ b/xarray/core/variable.py @@ -6,7 +6,6 @@ import numbers import warnings from collections.abc import Callable, Hashable, Mapping, Sequence -from datetime import timedelta from functools import partial from types import EllipsisType from typing import TYPE_CHECKING, Any, NoReturn, cast @@ -232,10 +231,16 @@ def _as_nanosecond_precision(data): def _possibly_convert_objects(values): """Convert arrays of datetime.datetime and datetime.timedelta objects into - datetime64 and timedelta64, according to the pandas convention. For the time - being, convert any non-nanosecond precision DatetimeIndex or TimedeltaIndex - objects to nanosecond precision. While pandas is relaxing this in version - 2.0.0, in xarray we will need to make sure we are ready to handle + datetime64 and timedelta64, according to the pandas convention. + + * datetime.datetime + * datetime.timedelta + * pd.Timestamp + * pd.Timedelta + + For the time being, convert any non-nanosecond precision DatetimeIndex or + TimedeltaIndex objects to nanosecond precision. While pandas is relaxing this + in version 2.0.0, in xarray we will need to make sure we are ready to handle non-nanosecond precision datetimes or timedeltas in our code before allowing such values to pass through unchanged. Converting to nanosecond precision through pandas.Series objects ensures that datetimes and timedeltas are @@ -305,13 +310,6 @@ def convert_non_numpy_type(data): if isinstance(data, tuple): data = utils.to_0d_object_array(data) - if isinstance(data, pd.Timestamp): - # TODO: convert, handle datetime objects, too - data = np.datetime64(data.value, "ns") - - if isinstance(data, timedelta): - data = np.timedelta64(getattr(data, "value", data), "ns") - # we don't want nested self-described arrays if isinstance(data, pd.Series | pd.DataFrame): pandas_data = data.values diff --git a/xarray/tests/test_plot.py b/xarray/tests/test_plot.py index 1e07459061f..8e00b943de8 100644 --- a/xarray/tests/test_plot.py +++ b/xarray/tests/test_plot.py @@ -90,19 +90,19 @@ def text_in_fig() -> set[str]: """ Return the set of all text in the figure """ - return {t.get_text() for t in plt.gcf().findobj(mpl.text.Text)} # type: ignore[attr-defined] # mpl error? + return {t.get_text() for t in plt.gcf().findobj(mpl.text.Text)} def find_possible_colorbars() -> list[mpl.collections.QuadMesh]: # nb. this function also matches meshes from pcolormesh - return plt.gcf().findobj(mpl.collections.QuadMesh) # type: ignore[return-value] # mpl error? + return plt.gcf().findobj(mpl.collections.QuadMesh) def substring_in_axes(substring: str, ax: mpl.axes.Axes) -> bool: """ Return True if a substring is found anywhere in an axes """ - alltxt: set[str] = {t.get_text() for t in ax.findobj(mpl.text.Text)} # type: ignore[attr-defined] # mpl error? + alltxt: set[str] = {t.get_text() for t in ax.findobj(mpl.text.Text)} return any(substring in txt for txt in alltxt) @@ -110,7 +110,7 @@ def substring_not_in_axes(substring: str, ax: mpl.axes.Axes) -> bool: """ Return True if a substring is not found anywhere in an axes """ - alltxt: set[str] = {t.get_text() for t in ax.findobj(mpl.text.Text)} # type: ignore[attr-defined] # mpl error? + alltxt: set[str] = {t.get_text() for t in ax.findobj(mpl.text.Text)} check = [(substring not in txt) for txt in alltxt] return all(check) @@ -122,7 +122,7 @@ def property_in_axes_text( Return True if the specified text in an axes has the property assigned to property_str """ - alltxt: list[mpl.text.Text] = ax.findobj(mpl.text.Text) # type: ignore[assignment] + alltxt: list[mpl.text.Text] = ax.findobj(mpl.text.Text) return all( plt.getp(t, property) == property_str for t in alltxt