diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 9a6f6e21e5e..5c179c0bd61 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -47,6 +47,8 @@ Bug fixes By `Sebastian Weigand `_. - Fix a regression in the removal of duplicate backend entrypoints (:issue:`5944`, :pull:`5959`) By `Kai Mühlbauer `_. +- Fix an issue that datasets from being saved when time variables with units that ``cftime`` can parse but pandas can not were present (:pull:`6049`). + By `Tim Heap `_. Documentation ~~~~~~~~~~~~~ diff --git a/xarray/coding/times.py b/xarray/coding/times.py index ea75219db5a..7d532f8fc38 100644 --- a/xarray/coding/times.py +++ b/xarray/coding/times.py @@ -400,8 +400,9 @@ def _cleanup_netcdf_time_units(units): delta, ref_date = _unpack_netcdf_time_units(units) try: units = "{} since {}".format(delta, format_timestamp(ref_date)) - except OutOfBoundsDatetime: - # don't worry about reifying the units if they're out of bounds + except (OutOfBoundsDatetime, ValueError): + # don't worry about reifying the units if they're out of bounds or + # formatted badly pass return units @@ -482,7 +483,7 @@ def encode_cf_datetime(dates, units=None, calendar=None): num = time_deltas / time_delta num = num.values.reshape(dates.shape) - except (OutOfBoundsDatetime, OverflowError): + except (OutOfBoundsDatetime, OverflowError, ValueError): num = _encode_datetime_with_cftime(dates, units, calendar) num = cast_to_int_if_safe(num) diff --git a/xarray/tests/test_coding_times.py b/xarray/tests/test_coding_times.py index aff2cb8cf3a..930677f75f4 100644 --- a/xarray/tests/test_coding_times.py +++ b/xarray/tests/test_coding_times.py @@ -853,6 +853,23 @@ def test_encode_cf_datetime_pandas_min() -> None: assert calendar == expected_calendar +@requires_cftime +def test_encode_cf_datetime_invalid_pandas_valid_cftime() -> None: + num, units, calendar = encode_cf_datetime( + pd.date_range("2000", periods=3), + # Pandas fails to parse this unit, but cftime is quite happy with it + "days since 1970-01-01 00:00:00 00", + "standard", + ) + + expected_num = [10957, 10958, 10959] + expected_units = "days since 1970-01-01 00:00:00 00" + expected_calendar = "standard" + assert_array_equal(num, expected_num) + assert units == expected_units + assert calendar == expected_calendar + + @requires_cftime def test_time_units_with_timezone_roundtrip(calendar) -> None: # Regression test for GH 2649 diff --git a/xarray/tests/test_formatting.py b/xarray/tests/test_formatting.py index 9e53cac3aa6..aa4c0c49f81 100644 --- a/xarray/tests/test_formatting.py +++ b/xarray/tests/test_formatting.py @@ -188,6 +188,11 @@ def test_pretty_print(self) -> None: def test_maybe_truncate(self) -> None: assert formatting.maybe_truncate("ß", 10) == "ß" + def test_format_timestamp_invalid_pandas_format(self) -> None: + expected = "2021-12-06 17:00:00 00" + with pytest.raises(ValueError): + formatting.format_timestamp(expected) + def test_format_timestamp_out_of_bounds(self) -> None: from datetime import datetime