Skip to content

Commit ef42335

Browse files
Opt out of floor division for float dtype time encoding (#9497)
1 parent bbc921d commit ef42335

File tree

3 files changed

+38
-13
lines changed

3 files changed

+38
-13
lines changed

doc/whats-new.rst

+4-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ Bug fixes
4747
- Make illegal path-like variable names when constructing a DataTree from a Dataset
4848
(:issue:`9339`, :pull:`9378`)
4949
By `Etienne Schalk <https://github.com/etienneschalk>`_.
50-
50+
- Fix bug when encoding times with missing values as floats in the case when
51+
the non-missing times could in theory be encoded with integers
52+
(:issue:`9488`, :pull:`9497`). By `Spencer Clark
53+
<https://github.com/spencerkclark>`_.
5154

5255

5356
Documentation

xarray/coding/times.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ def _eagerly_encode_cf_datetime(
771771
# needed time delta to encode faithfully to int64
772772
needed_time_delta = _time_units_to_timedelta64(needed_units)
773773

774-
floor_division = True
774+
floor_division = np.issubdtype(dtype, np.integer) or dtype is None
775775
if time_delta > needed_time_delta:
776776
floor_division = False
777777
if dtype is None:
@@ -892,7 +892,7 @@ def _eagerly_encode_cf_timedelta(
892892
# needed time delta to encode faithfully to int64
893893
needed_time_delta = _time_units_to_timedelta64(needed_units)
894894

895-
floor_division = True
895+
floor_division = np.issubdtype(dtype, np.integer) or dtype is None
896896
if time_delta > needed_time_delta:
897897
floor_division = False
898898
if dtype is None:

xarray/tests/test_coding_times.py

+32-10
Original file line numberDiff line numberDiff line change
@@ -1383,24 +1383,46 @@ def test_roundtrip_timedelta64_nanosecond_precision_warning() -> None:
13831383
assert decoded_var.encoding["dtype"] == np.int64
13841384

13851385

1386-
def test_roundtrip_float_times() -> None:
1387-
# Regression test for GitHub issue #8271
1388-
fill_value = 20.0
1389-
times = [
1390-
np.datetime64("1970-01-01 00:00:00", "ns"),
1391-
np.datetime64("1970-01-01 06:00:00", "ns"),
1392-
np.datetime64("NaT", "ns"),
1393-
]
1386+
_TEST_ROUNDTRIP_FLOAT_TIMES_TESTS = {
1387+
"GH-8271": (
1388+
20.0,
1389+
np.array(
1390+
["1970-01-01 00:00:00", "1970-01-01 06:00:00", "NaT"],
1391+
dtype="datetime64[ns]",
1392+
),
1393+
"days since 1960-01-01",
1394+
np.array([3653, 3653.25, 20.0]),
1395+
),
1396+
"GH-9488-datetime64[ns]": (
1397+
1.0e20,
1398+
np.array(["2010-01-01 12:00:00", "NaT"], dtype="datetime64[ns]"),
1399+
"seconds since 2010-01-01",
1400+
np.array([43200, 1.0e20]),
1401+
),
1402+
"GH-9488-timedelta64[ns]": (
1403+
1.0e20,
1404+
np.array([1_000_000_000, "NaT"], dtype="timedelta64[ns]"),
1405+
"seconds",
1406+
np.array([1.0, 1.0e20]),
1407+
),
1408+
}
1409+
13941410

1395-
units = "days since 1960-01-01"
1411+
@pytest.mark.parametrize(
1412+
("fill_value", "times", "units", "encoded_values"),
1413+
_TEST_ROUNDTRIP_FLOAT_TIMES_TESTS.values(),
1414+
ids=_TEST_ROUNDTRIP_FLOAT_TIMES_TESTS.keys(),
1415+
)
1416+
def test_roundtrip_float_times(fill_value, times, units, encoded_values) -> None:
1417+
# Regression test for GitHub issues #8271 and #9488
13961418
var = Variable(
13971419
["time"],
13981420
times,
13991421
encoding=dict(dtype=np.float64, _FillValue=fill_value, units=units),
14001422
)
14011423

14021424
encoded_var = conventions.encode_cf_variable(var)
1403-
np.testing.assert_array_equal(encoded_var, np.array([3653, 3653.25, 20.0]))
1425+
np.testing.assert_array_equal(encoded_var, encoded_values)
14041426
assert encoded_var.attrs["units"] == units
14051427
assert encoded_var.attrs["_FillValue"] == fill_value
14061428

0 commit comments

Comments
 (0)