diff --git a/doc/api.rst b/doc/api.rst
index 96b4864804f..f2ff809e45f 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -557,6 +557,7 @@ Datetimelike properties
DataArray.dt.seconds
DataArray.dt.microseconds
DataArray.dt.nanoseconds
+ DataArray.dt.total_seconds
**Timedelta methods**:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index b6bad62dd7c..e28177814b7 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -24,6 +24,8 @@ New Features
- Use `opt_einsum `_ for :py:func:`xarray.dot` by default if installed.
By `Deepak Cherian `_. (:issue:`7764`, :pull:`8373`).
+- Add ``DataArray.dt.total_seconds()`` method to match the Pandas API. (:pull:`8435`).
+ By `Ben Mares `_.
Breaking changes
~~~~~~~~~~~~~~~~
diff --git a/xarray/core/accessor_dt.py b/xarray/core/accessor_dt.py
index 0d4a402cd19..b57c2f3857c 100644
--- a/xarray/core/accessor_dt.py
+++ b/xarray/core/accessor_dt.py
@@ -74,6 +74,8 @@ def _access_through_series(values, name):
if name == "season":
months = values_as_series.dt.month.values
field_values = _season_from_months(months)
+ elif name == "total_seconds":
+ field_values = values_as_series.dt.total_seconds().values
elif name == "isocalendar":
# special NaT-handling can be removed when
# https://github.com/pandas-dev/pandas/issues/54657 is resolved
@@ -574,6 +576,13 @@ class TimedeltaAccessor(TimeAccessor[T_DataArray]):
43200, 64800])
Coordinates:
* time (time) timedelta64[ns] 1 days 00:00:00 ... 5 days 18:00:00
+ >>> ts.dt.total_seconds()
+
+ array([ 86400., 108000., 129600., 151200., 172800., 194400., 216000.,
+ 237600., 259200., 280800., 302400., 324000., 345600., 367200.,
+ 388800., 410400., 432000., 453600., 475200., 496800.])
+ Coordinates:
+ * time (time) timedelta64[ns] 1 days 00:00:00 ... 5 days 18:00:00
"""
@property
@@ -596,6 +605,11 @@ def nanoseconds(self) -> T_DataArray:
"""Number of nanoseconds (>= 0 and less than 1 microsecond) for each element"""
return self._date_field("nanoseconds", np.int64)
+ # Not defined as a property in order to match the Pandas API
+ def total_seconds(self) -> T_DataArray:
+ """Total duration of each element expressed in seconds."""
+ return self._date_field("total_seconds", np.float64)
+
class CombinedDatetimelikeAccessor(
DatetimeAccessor[T_DataArray], TimedeltaAccessor[T_DataArray]
diff --git a/xarray/tests/test_accessor_dt.py b/xarray/tests/test_accessor_dt.py
index 64b487628c8..a8d5e722b66 100644
--- a/xarray/tests/test_accessor_dt.py
+++ b/xarray/tests/test_accessor_dt.py
@@ -6,6 +6,7 @@
import xarray as xr
from xarray.tests import (
+ assert_allclose,
assert_array_equal,
assert_chunks_equal,
assert_equal,
@@ -100,6 +101,19 @@ def test_field_access(self, field) -> None:
assert expected.dtype == actual.dtype
assert_identical(expected, actual)
+ def test_total_seconds(self) -> None:
+ # Subtract a value in the middle of the range to ensure that some values
+ # are negative
+ delta = self.data.time - np.datetime64("2000-01-03")
+ actual = delta.dt.total_seconds()
+ expected = xr.DataArray(
+ np.arange(-48, 52, dtype=np.float64) * 3600,
+ name="total_seconds",
+ coords=[self.data.time],
+ )
+ # This works with assert_identical when pandas is >=1.5.0.
+ assert_allclose(expected, actual)
+
@pytest.mark.parametrize(
"field, pandas_field",
[