diff --git a/doc/whats-new.rst b/doc/whats-new.rst index fe698bc358b..1af6d0458be 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -76,6 +76,9 @@ Documentation Internal Changes ~~~~~~~~~~~~~~~~ +- Updated time coding tests to assert exact equality rather than equality with + a tolerance, since xarray's minimum supported version of cftime is greater + than 1.2.1 (:pull:`9961`). By `Spencer Clark `_. .. _whats-new.2025.01.1: diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index 72078da11b9..dc333719d08 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -619,16 +619,13 @@ def test_roundtrip_cftime_datetime_data(self) -> None: dtype = actual.t.dtype expected_decoded_t = expected_decoded_t.astype(dtype) expected_decoded_t0 = expected_decoded_t0.astype(dtype) - abs_diff = abs(actual.t.values - expected_decoded_t) - assert (abs_diff <= np.timedelta64(1, "s")).all() + assert_array_equal(actual.t.values, expected_decoded_t) assert ( actual.t.encoding["units"] == "days since 0001-01-01 00:00:00.000000" ) assert actual.t.encoding["calendar"] == expected_calendar - - abs_diff = abs(actual.t0.values - expected_decoded_t0) - assert (abs_diff <= np.timedelta64(1, "s")).all() + assert_array_equal(actual.t0.values, expected_decoded_t0) assert actual.t0.encoding["units"] == "days since 0001-01-01" assert actual.t.encoding["calendar"] == expected_calendar @@ -4709,11 +4706,8 @@ def test_roundtrip_cftime_datetime_data(self) -> None: expected_decoded_t0 = np.array([date_type(1, 1, 1)]) with self.roundtrip(expected) as actual: - abs_diff = abs(actual.t.values - expected_decoded_t) - assert (abs_diff <= np.timedelta64(1, "s")).all() - - abs_diff = abs(actual.t0.values - expected_decoded_t0) - assert (abs_diff <= np.timedelta64(1, "s")).all() + assert_array_equal(actual.t.values, expected_decoded_t) + assert_array_equal(actual.t0.values, expected_decoded_t0) def test_write_store(self) -> None: # Override method in DatasetIOBase - not applicable to dask diff --git a/xarray/tests/test_coding_times.py b/xarray/tests/test_coding_times.py index 44c0157f1b2..cdf97f08e08 100644 --- a/xarray/tests/test_coding_times.py +++ b/xarray/tests/test_coding_times.py @@ -65,36 +65,36 @@ _ALL_CALENDARS = sorted(_NON_STANDARD_CALENDARS_SET.union(_STANDARD_CALENDARS)) _NON_STANDARD_CALENDARS = sorted(_NON_STANDARD_CALENDARS_SET) _CF_DATETIME_NUM_DATES_UNITS = [ - (np.arange(10), "days since 2000-01-01"), - (np.arange(10).astype("float64"), "days since 2000-01-01"), - (np.arange(10).astype("float32"), "days since 2000-01-01"), - (np.arange(10).reshape(2, 5), "days since 2000-01-01"), - (12300 + np.arange(5), "hours since 1680-01-01 00:00:00"), + (np.arange(10), "days since 2000-01-01", "s"), + (np.arange(10).astype("float64"), "days since 2000-01-01", "s"), + (np.arange(10).astype("float32"), "days since 2000-01-01", "s"), + (np.arange(10).reshape(2, 5), "days since 2000-01-01", "s"), + (12300 + np.arange(5), "hours since 1680-01-01 00:00:00", "s"), # here we add a couple minor formatting errors to test # the robustness of the parsing algorithm. - (12300 + np.arange(5), "hour since 1680-01-01 00:00:00"), - (12300 + np.arange(5), "Hour since 1680-01-01 00:00:00"), - (12300 + np.arange(5), " Hour since 1680-01-01 00:00:00 "), - (10, "days since 2000-01-01"), - ([10], "daYs since 2000-01-01"), - ([[10]], "days since 2000-01-01"), - ([10, 10], "days since 2000-01-01"), - (np.array(10), "days since 2000-01-01"), - (0, "days since 1000-01-01"), - ([0], "days since 1000-01-01"), - ([[0]], "days since 1000-01-01"), - (np.arange(2), "days since 1000-01-01"), - (np.arange(0, 100000, 20000), "days since 1900-01-01"), - (np.arange(0, 100000, 20000), "days since 1-01-01"), - (17093352.0, "hours since 1-1-1 00:00:0.0"), - ([0.5, 1.5], "hours since 1900-01-01T00:00:00"), - (0, "milliseconds since 2000-01-01T00:00:00"), - (0, "microseconds since 2000-01-01T00:00:00"), - (np.int32(788961600), "seconds since 1981-01-01"), # GH2002 - (12300 + np.arange(5), "hour since 1680-01-01 00:00:00.500000"), - (164375, "days since 1850-01-01 00:00:00"), - (164374.5, "days since 1850-01-01 00:00:00"), - ([164374.5, 168360.5], "days since 1850-01-01 00:00:00"), + (12300 + np.arange(5), "hour since 1680-01-01 00:00:00", "s"), + (12300 + np.arange(5), "Hour since 1680-01-01 00:00:00", "s"), + (12300 + np.arange(5), " Hour since 1680-01-01 00:00:00 ", "s"), + (10, "days since 2000-01-01", "s"), + ([10], "daYs since 2000-01-01", "s"), + ([[10]], "days since 2000-01-01", "s"), + ([10, 10], "days since 2000-01-01", "s"), + (np.array(10), "days since 2000-01-01", "s"), + (0, "days since 1000-01-01", "s"), + ([0], "days since 1000-01-01", "s"), + ([[0]], "days since 1000-01-01", "s"), + (np.arange(2), "days since 1000-01-01", "s"), + (np.arange(0, 100000, 20000), "days since 1900-01-01", "s"), + (np.arange(0, 100000, 20000), "days since 1-01-01", "s"), + (17093352.0, "hours since 1-1-1 00:00:0.0", "s"), + ([0.5, 1.5], "hours since 1900-01-01T00:00:00", "s"), + (0, "milliseconds since 2000-01-01T00:00:00", "s"), + (0, "microseconds since 2000-01-01T00:00:00", "s"), + (np.int32(788961600), "seconds since 1981-01-01", "s"), # GH2002 + (12300 + np.arange(5), "hour since 1680-01-01 00:00:00.500000", "us"), + (164375, "days since 1850-01-01 00:00:00", "s"), + (164374.5, "days since 1850-01-01 00:00:00", "s"), + ([164374.5, 168360.5], "days since 1850-01-01 00:00:00", "s"), ] _CF_DATETIME_TESTS = [ num_dates_units + (calendar,) @@ -122,9 +122,15 @@ def _all_cftime_date_types(): @requires_cftime @pytest.mark.filterwarnings("ignore:Ambiguous reference date string") @pytest.mark.filterwarnings("ignore:Times can't be serialized faithfully") -@pytest.mark.parametrize(["num_dates", "units", "calendar"], _CF_DATETIME_TESTS) +@pytest.mark.parametrize( + ["num_dates", "units", "minimum_resolution", "calendar"], _CF_DATETIME_TESTS +) def test_cf_datetime( - num_dates, units, calendar, time_unit: PDDatetimeUnitOptions + num_dates, + units: str, + minimum_resolution: PDDatetimeUnitOptions, + calendar: str, + time_unit: PDDatetimeUnitOptions, ) -> None: import cftime @@ -137,25 +143,23 @@ def test_cf_datetime( actual = decode_cf_datetime(num_dates, units, calendar, time_unit=time_unit) if actual.dtype.kind != "O": - expected = cftime_to_nptime(expected, time_unit=time_unit) - - abs_diff = np.asarray(abs(actual - expected)).ravel() - abs_diff = pd.to_timedelta(abs_diff.tolist()).to_numpy() + if np.timedelta64(1, time_unit) > np.timedelta64(1, minimum_resolution): + expected_unit = minimum_resolution + else: + expected_unit = time_unit + expected = cftime_to_nptime(expected, time_unit=expected_unit) - # once we no longer support versions of netCDF4 older than 1.1.5, - # we could do this check with near microsecond accuracy: - # https://github.com/Unidata/netcdf4-python/issues/355 - assert (abs_diff <= np.timedelta64(1, "s")).all() + assert_array_equal(actual, expected) encoded1, _, _ = encode_cf_datetime(actual, units, calendar) - assert_duckarray_allclose(num_dates, encoded1) + assert_array_equal(num_dates, encoded1) if hasattr(num_dates, "ndim") and num_dates.ndim == 1 and "1000" not in units: # verify that wrapping with a pandas.Index works # note that it *does not* currently work to put # non-datetime64 compatible dates into a pandas.Index encoded2, _, _ = encode_cf_datetime(pd.Index(actual), units, calendar) - assert_duckarray_allclose(num_dates, encoded2) + assert_array_equal(num_dates, encoded2) @requires_cftime @@ -206,11 +210,7 @@ def test_decode_cf_datetime_non_iso_strings() -> None: ] for num_dates, units in cases: actual = decode_cf_datetime(num_dates, units) - abs_diff = abs(actual - expected.values) - # once we no longer support versions of netCDF4 older than 1.1.5, - # we could do this check with near microsecond accuracy: - # https://github.com/Unidata/netcdf4-python/issues/355 - assert (abs_diff <= np.timedelta64(1, "s")).all() + assert_array_equal(actual, expected) @requires_cftime @@ -220,7 +220,7 @@ def test_decode_standard_calendar_inside_timestamp_range( ) -> None: import cftime - units = "days since 0001-01-01" + units = "hours since 0001-01-01" times = pd.date_range( "2001-04-01-00", end="2001-04-30-23", unit=time_unit, freq="h" ) @@ -233,11 +233,7 @@ def test_decode_standard_calendar_inside_timestamp_range( # representable with nanosecond resolution. actual = decode_cf_datetime(time, units, calendar=calendar, time_unit=time_unit) assert actual.dtype == np.dtype(f"=M8[{time_unit}]") - abs_diff = abs(actual - expected) - # once we no longer support versions of netCDF4 older than 1.1.5, - # we could do this check with near microsecond accuracy: - # https://github.com/Unidata/netcdf4-python/issues/355 - assert (abs_diff <= np.timedelta64(1, "s")).all() + assert_array_equal(actual, expected) @requires_cftime @@ -256,11 +252,7 @@ def test_decode_non_standard_calendar_inside_timestamp_range(calendar) -> None: actual = decode_cf_datetime(non_standard_time, units, calendar=calendar) assert actual.dtype == expected_dtype - abs_diff = abs(actual - expected) - # once we no longer support versions of netCDF4 older than 1.1.5, - # we could do this check with near microsecond accuracy: - # https://github.com/Unidata/netcdf4-python/issues/355 - assert (abs_diff <= np.timedelta64(1, "s")).all() + assert_array_equal(actual, expected) @requires_cftime @@ -287,11 +279,7 @@ def test_decode_dates_outside_timestamp_range( warnings.filterwarnings("ignore", "Unable to decode time axis") actual = decode_cf_datetime(time, units, calendar=calendar, time_unit=time_unit) assert all(isinstance(value, expected_date_type) for value in actual) - abs_diff = abs(actual - expected) - # once we no longer support versions of netCDF4 older than 1.1.5, - # we could do this check with near microsecond accuracy: - # https://github.com/Unidata/netcdf4-python/issues/355 - assert (abs_diff <= np.timedelta64(1, "us")).all() + assert_array_equal(actual, expected) @requires_cftime @@ -367,14 +355,8 @@ def test_decode_standard_calendar_multidim_time_inside_timestamp_range( mdim_time, units, calendar=calendar, time_unit=time_unit ) assert actual.dtype == np.dtype(f"=M8[{time_unit}]") - - abs_diff1 = abs(actual[:, 0] - expected1) - abs_diff2 = abs(actual[:, 1] - expected2) - # once we no longer support versions of netCDF4 older than 1.1.5, - # we could do this check with near microsecond accuracy: - # https://github.com/Unidata/netcdf4-python/issues/355 - assert (abs_diff1 <= np.timedelta64(1, "s")).all() - assert (abs_diff2 <= np.timedelta64(1, "s")).all() + assert_array_equal(actual[:, 0], expected1) + assert_array_equal(actual[:, 1], expected2) @requires_cftime @@ -409,13 +391,8 @@ def test_decode_nonstandard_calendar_multidim_time_inside_timestamp_range( actual = decode_cf_datetime(mdim_time, units, calendar=calendar) assert actual.dtype == expected_dtype - abs_diff1 = abs(actual[:, 0] - expected1) - abs_diff2 = abs(actual[:, 1] - expected2) - # once we no longer support versions of netCDF4 older than 1.1.5, - # we could do this check with near microsecond accuracy: - # https://github.com/Unidata/netcdf4-python/issues/355 - assert (abs_diff1 <= np.timedelta64(1, "s")).all() - assert (abs_diff2 <= np.timedelta64(1, "s")).all() + assert_array_equal(actual[:, 0], expected1) + assert_array_equal(actual[:, 1], expected2) @requires_cftime @@ -455,14 +432,8 @@ def test_decode_multidim_time_outside_timestamp_range( dtype = np.dtype(f"=M8[{time_unit}]") assert actual.dtype == dtype - - abs_diff1 = abs(actual[:, 0] - expected1) - abs_diff2 = abs(actual[:, 1] - expected2) - # once we no longer support versions of netCDF4 older than 1.1.5, - # we could do this check with near microsecond accuracy: - # https://github.com/Unidata/netcdf4-python/issues/355 - assert (abs_diff1 <= np.timedelta64(1, "s")).all() - assert (abs_diff2 <= np.timedelta64(1, "s")).all() + assert_array_equal(actual[:, 0], expected1) + assert_array_equal(actual[:, 1], expected2) @requires_cftime pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy