Skip to content

Commit b449735

Browse files
authored
Merge branch 'main' into complex-grid-mapping
2 parents 5aa2878 + f0b8083 commit b449735

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

cf_xarray/helpers.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import datetime
34
from collections.abc import Hashable, Sequence
45

56
import numpy as np
@@ -230,6 +231,10 @@ def _get_core_dim_orders(core_dim_coords: dict[str, np.ndarray]) -> dict[str, st
230231
# Cast to float64 for safe comparison
231232
diffs_float = diffs.astype("float64")
232233
nonzero_diffs = diffs_float[diffs_float != 0]
234+
elif isinstance(diffs[0], datetime.timedelta):
235+
# For datetime timedelta, we use the total_seconds method
236+
diffs_float = np.array([x.total_seconds() for x in diffs])
237+
nonzero_diffs = diffs_float[diffs_float != 0]
233238
else:
234239
zero = 0
235240
nonzero_diffs = diffs[diffs != zero]
@@ -360,9 +365,17 @@ def _is_bounds_monotonic(bounds: np.ndarray) -> bool:
360365
# Cannot cast ufunc 'greater' input 0 from dtype('<m8[ns]') to dtype('<m8')
361366
# with casting rule 'same_kind' To avoid this, always cast to float64 before
362367
# np.diff.
363-
arr_numeric = bounds.astype("float64").flatten()
364-
diffs = np.diff(arr_numeric)
365-
nonzero_diffs = diffs[diffs != 0]
368+
369+
diffs = np.diff(bounds.flatten())
370+
371+
if isinstance(diffs[0], datetime.timedelta):
372+
# For datetime timedelta, we use the total_seconds method
373+
diffs_float = np.array([x.total_seconds() for x in diffs])
374+
375+
else:
376+
diffs_float = diffs.astype("float64")
377+
378+
nonzero_diffs = diffs_float[diffs_float != 0]
366379

367380
# All values are equal, treat as monotonic
368381
if nonzero_diffs.size == 0:

cf_xarray/tests/test_helpers.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import numpy as np
12
import xarray as xr
23
from numpy.testing import assert_array_equal
34
from xarray.testing import assert_equal
45

56
import cf_xarray as cfxr # noqa
67

78
from ..datasets import airds, mollwds, rotds
9+
from . import requires_cftime
810

911
try:
1012
from dask.array import Array as DaskArray
@@ -121,3 +123,36 @@ def test_vertices_to_bounds() -> None:
121123
# 2D case
122124
lon_b = cfxr.vertices_to_bounds(mollwds.lon_vertices, out_dims=("bounds", "x", "y"))
123125
assert_array_equal(mollwds.lon_bounds, lon_b)
126+
127+
128+
@requires_cftime
129+
def test_bounds_to_vertices_cftime() -> None:
130+
import cftime
131+
132+
# Create cftime objects for monthly bounds
133+
periods = 3
134+
# start = cftime.DatetimeGregorian(2000, 1, 1)
135+
edges = [cftime.DatetimeGregorian(2000, m, 1) for m in range(1, periods + 2)]
136+
137+
# Bounds as [start, end) for each month
138+
bnds = np.array([[edges[i], edges[i + 1]] for i in range(periods)])
139+
mid = np.array([edges[i] + (edges[i + 1] - edges[i]) / 2 for i in range(periods)])
140+
141+
# Sample data
142+
values = xr.DataArray(
143+
np.arange(periods, dtype=float), dims=("time",), coords={"time": mid}
144+
)
145+
146+
# Build dataset with CF-style bounds
147+
ds = xr.Dataset(
148+
{"foo": values},
149+
coords={
150+
"time": ("time", mid, {"bounds": "time_bounds"}),
151+
"time_bounds": (("time", "bounds"), bnds),
152+
"bounds": ("bounds", [0, 1]),
153+
},
154+
)
155+
156+
time_c = cfxr.bounds_to_vertices(ds.time_bounds, "bounds")
157+
time_b = cfxr.vertices_to_bounds(time_c, out_dims=("bounds", "time"))
158+
assert_array_equal(ds.time_bounds, time_b)

0 commit comments

Comments
 (0)