diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 63c9dee04c5..a0ebb544fc5 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -38,6 +38,12 @@ Deprecations Bug fixes ~~~~~~~~~ +- Fix datetime encoding precision loss regression introduced in the previous + release for datetimes encoded with units requiring floating point values, and + a reference date not equal to the first value of the datetime array + (:issue:`8271`, :pull:`8272`). By `Spencer Clark + `_. + Documentation ~~~~~~~~~~~~~ diff --git a/xarray/coding/times.py b/xarray/coding/times.py index 2822f02dd8d..039fe371100 100644 --- a/xarray/coding/times.py +++ b/xarray/coding/times.py @@ -714,7 +714,8 @@ def encode_cf_datetime( if data_units != units: # this accounts for differences in the reference times ref_delta = abs(data_ref_date - ref_date).to_timedelta64() - if ref_delta > np.timedelta64(0, "ns"): + data_delta = _time_units_to_timedelta64(needed_units) + if (ref_delta % data_delta) > np.timedelta64(0, "ns"): needed_units = _infer_time_units_from_diff(ref_delta) # needed time delta to encode faithfully to int64 diff --git a/xarray/tests/test_coding_times.py b/xarray/tests/test_coding_times.py index 5f76a4a2ca8..423e48bd155 100644 --- a/xarray/tests/test_coding_times.py +++ b/xarray/tests/test_coding_times.py @@ -1363,10 +1363,15 @@ def test_roundtrip_timedelta64_nanosecond_precision_warning() -> None: def test_roundtrip_float_times() -> None: + # Regression test for GitHub issue #8271 fill_value = 20.0 - times = [np.datetime64("2000-01-01 12:00:00", "ns"), np.datetime64("NaT", "ns")] + times = [ + np.datetime64("1970-01-01 00:00:00", "ns"), + np.datetime64("1970-01-01 06:00:00", "ns"), + np.datetime64("NaT", "ns"), + ] - units = "days since 2000-01-01" + units = "days since 1960-01-01" var = Variable( ["time"], times, @@ -1374,7 +1379,7 @@ def test_roundtrip_float_times() -> None: ) encoded_var = conventions.encode_cf_variable(var) - np.testing.assert_array_equal(encoded_var, np.array([0.5, 20.0])) + np.testing.assert_array_equal(encoded_var, np.array([3653, 3653.25, 20.0])) assert encoded_var.attrs["units"] == units assert encoded_var.attrs["_FillValue"] == fill_value