Repository: pydata/xarray

Issue:
Saving an empty datetime64 variable with a `calendar: "standard"` encoding fails
### What happened?

Saving a dataset with a zero length datetime64 variable and `calendar: "standard"` fails with the stack trace attached below.

Saving the same dataset with a non-empty time dimension or without `calendar: "standard"` succeeds.

### What did you expect to happen?

Saving the dataset should work in all instances.

### Minimal Complete Verifiable Example

```Python
import numpy
import pandas
import xarray

times = 0
dataset = xarray.Dataset({
    'time': ('time', pandas.date_range('2025-09-09', periods=times)),
})


dataset.coords['time'].encoding = {
    'dtype': numpy.dtype('float64'),
    'units': 'days since 1990-1-1 00:00:00',
    'calendar': 'standard',
}

# If times > 0 this works.
# If there is no calendar encoding this works
# If times = 0 and calendar: standard, this fails
dataset.to_netcdf('out.nc')
```

### MVCE confirmation

- [x] Minimal example — the example is as focused as reasonably possible to demonstrate the underlying issue in xarray.
- [x] Complete example — the example is self-contained, including all data and the text of any traceback.
- [x] Verifiable example — the example copy & pastes into an IPython prompt or [Binder notebook](https://mybinder.org/v2/gh/pydata/xarray/main?urlpath=lab/tree/doc/examples/blank_template.ipynb), returning the result.
- [x] New issue — a search of GitHub Issues suggests this is not a duplicate.
- [x] Recent environment — the issue occurs with the latest version of xarray and its dependencies.

### Relevant log output

```Python
Traceback (most recent call last):
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/coding/times.py", line 1068, in _eagerly_encode_cf_datetime
    if calendar in ["standard", "gregorian"] and np.nanmin(dates).astype(
                                                 ~~~~~~~~~^^^^^^^
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/numpy/lib/_nanfunctions_impl.py", line 356, in nanmin
    res = np.fmin.reduce(a, axis=axis, out=out, **kwargs)
ValueError: zero-size array to reduction operation fmin which has no identity

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "src/cftime/_cftime.pyx", line 197, in cftime._cftime.date2num
IndexError: index 0 is out of bounds for axis 0 with size 0

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/mx-moth/xarray/zero-length-time-coordinate.py", line 22, in <module>
    dataset.to_netcdf('out.nc')
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/core/dataset.py", line 2102, in to_netcdf
    return to_netcdf(  # type: ignore[return-value]  # mypy cannot resolve the overloads:(
        self,
    ...<10 lines>...
        auto_complex=auto_complex,
    )
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/backends/api.py", line 2107, in to_netcdf
    dump_to_store(
    ~~~~~~~~~~~~~^
        dataset, store, writer, encoding=encoding, unlimited_dims=unlimited_dims
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/backends/api.py", line 2157, in dump_to_store
    store.store(variables, attrs, check_encoding, writer, unlimited_dims=unlimited_dims)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/backends/common.py", line 525, in store
    variables, attributes = self.encode(variables, attributes)
                            ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/backends/common.py", line 713, in encode
    variables, attributes = cf_encoder(variables, attributes)
                            ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/conventions.py", line 800, in cf_encoder
    new_vars[k] = encode_cf_variable(v, name=k)
                  ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/conventions.py", line 102, in encode_cf_variable
    var = coder.encode(var, name=name)
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/coding/times.py", line 1375, in encode
    (data, units, calendar) = encode_cf_datetime(data, units, calendar, dtype)
                              ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/coding/times.py", line 1020, in encode_cf_datetime
    return _eagerly_encode_cf_datetime(dates, units, calendar, dtype)
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/coding/times.py", line 1143, in _eagerly_encode_cf_datetime
    num = _encode_datetime_with_cftime(dates, units, calendar)
  File "/home/mx-moth/xarray/.conda/lib/python3.13/site-packages/xarray/coding/times.py", line 967, in _encode_datetime_with_cftime
    encoded_nums = cftime.date2num(
        filtered_dates, units, calendar, longdouble=False
    )
  File "src/cftime/_cftime.pyx", line 199, in cftime._cftime.date2num
ValueError: The truth value of an empty array is ambiguous. Use `array.size > 0` to check that an array is not empty.
Raised while encoding variable 'time' with value <xarray.IndexVariable 'time' (time: 0)> Size: 0B
array([], dtype='datetime64[ns]')
```

### Anything else we need to know?

_No response_

### Environment

<details>


INSTALLED VERSIONS
------------------
commit: None
python: 3.13.7 | packaged by conda-forge | (main, Sep  3 2025, 14:30:35) [GCC 14.3.0]
python-bits: 64
OS: Linux
OS-release: 6.14.0-29-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_AU.UTF-8
LOCALE: ('en_AU', 'UTF-8')
libhdf5: 1.14.6
libnetcdf: 4.9.3

xarray: 2025.9.0
pandas: 2.3.2
numpy: 2.3.2
scipy: None
netCDF4: 1.7.2
pydap: None
h5netcdf: None
h5py: None
zarr: None
cftime: 1.6.4
nc_time_axis: None
iris: None
bottleneck: 1.5.0
dask: 2025.7.0
distributed: 2025.7.0
matplotlib: 3.10.6
cartopy: 0.25.0
seaborn: None
numbagg: None
fsspec: 2025.9.0
cupy: None
pint: None
sparse: None
flox: None
numpy_groupies: None
setuptools: None
pip: 25.2
conda: None
pytest: None
mypy: None
IPython: None
sphinx: None


</details>


Discussion:

Fix (patch):
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 624f161d..a34afce8 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -31,6 +31,9 @@ Bug fixes
 
 - Fix the ``align_chunks`` parameter on the :py:meth:`~xarray.Dataset.to_zarr` method, it was not being
   passed to the underlying :py:meth:`~xarray.backends.api` method (:issue:`10501`, :pull:`10516`).
+- Fix error when encoding an empty :py:class:`numpy.datetime64` array
+  (:issue:`10722`, :pull:`10723`). By `Spencer Clark
+  <https://github.com/spencerkclark>`_.
 
 Documentation
 ~~~~~~~~~~~~~
diff --git a/xarray/coding/times.py b/xarray/coding/times.py
index b5ba123d..f10b9b83 100644
--- a/xarray/coding/times.py
+++ b/xarray/coding/times.py
@@ -1065,9 +1065,12 @@ def _eagerly_encode_cf_datetime(
             # parse with cftime instead
             raise OutOfBoundsDatetime
         assert np.issubdtype(dates.dtype, "datetime64")
-        if calendar in ["standard", "gregorian"] and np.nanmin(dates).astype(
-            "=M8[us]"
-        ).astype(datetime) < datetime(1582, 10, 15):
+        if (
+            calendar in ["standard", "gregorian"]
+            and dates.size > 0
+            and np.nanmin(dates).astype("=M8[us]").astype(datetime)
+            < datetime(1582, 10, 15)
+        ):
             raise_gregorian_proleptic_gregorian_mismatch_error = True
 
         time_unit, ref_date = _unpack_time_unit_and_ref_date(units)


Test changes:
diff --git a/xarray/tests/test_coding_times.py b/xarray/tests/test_coding_times.py
index 10295e44..103e761a 100644
--- a/xarray/tests/test_coding_times.py
+++ b/xarray/tests/test_coding_times.py
@@ -2198,3 +2198,24 @@ def test_roundtrip_0size_timedelta(time_unit: PDDatetimeUnitOptions) -> None:
         decoded.load()
     assert decoded.dtype == np.dtype("=m8[s]")
     assert decoded.encoding == encoding
+
+
+def test_roundtrip_empty_datetime64_array(time_unit: PDDatetimeUnitOptions) -> None:
+    # Regression test for GitHub issue #10722.
+    encoding = {
+        "units": "days since 1990-1-1",
+        "dtype": np.dtype("float64"),
+        "calendar": "standard",
+    }
+    times = date_range("2000", periods=0, unit=time_unit)
+    variable = Variable(["time"], times, encoding=encoding)
+
+    encoded = conventions.encode_cf_variable(variable, name="foo")
+    assert encoded.dtype == np.dtype("float64")
+
+    decode_times = CFDatetimeCoder(time_unit=time_unit)
+    roundtripped = conventions.decode_cf_variable(
+        "foo", encoded, decode_times=decode_times
+    )
+    assert_identical(variable, roundtripped)
+    assert roundtripped.dtype == variable.dtype