Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions properties/test_encode_decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

"""

import warnings

import pytest

pytest.importorskip("hypothesis")
Expand All @@ -17,7 +19,8 @@

import xarray as xr
from xarray.coding.times import _parse_iso8601_without_reso
from xarray.testing.strategies import variables
from xarray.testing.strategies import CFTimeStrategyISO8601, variables
from xarray.tests import requires_cftime


@pytest.mark.slow
Expand Down Expand Up @@ -47,8 +50,10 @@ def test_CFScaleOffset_coder_roundtrip(original) -> None:
xr.testing.assert_identical(original, roundtripped)


# TODO: add cftime.datetime
@given(dt=st.datetimes())
@requires_cftime
@given(dt=st.datetimes() | CFTimeStrategyISO8601())
def test_iso8601_decode(dt):
iso = dt.isoformat()
assert dt == _parse_iso8601_without_reso(type(dt), iso)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", message=".*date/calendar/year zero.*")
assert dt == _parse_iso8601_without_reso(type(dt), iso)
34 changes: 34 additions & 0 deletions xarray/testing/strategies.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import datetime
import warnings
from collections.abc import Hashable, Iterable, Mapping, Sequence
from typing import TYPE_CHECKING, Any, Protocol, overload

Expand All @@ -8,6 +10,7 @@
import xarray as xr
from xarray.core.types import T_DuckArray
from xarray.core.utils import attempt_import
from xarray.tests.test_coding_times import _all_cftime_date_types

if TYPE_CHECKING:
from xarray.core.types import _DTypeLikeNested, _ShapeLike
Expand Down Expand Up @@ -473,3 +476,34 @@ def unique_subset_of(
return (
{k: objs[k] for k in subset_keys} if isinstance(objs, Mapping) else subset_keys
)


class CFTimeStategy(st.SearchStrategy):
def __init__(self, min_value, max_value):
self.min_value = min_value
self.max_value = max_value

def do_draw(self, data):
unit_microsecond = datetime.timedelta(microseconds=1)
timespan_microseconds = (self.max_value - self.min_value) // unit_microsecond
result = data.draw_integer(0, timespan_microseconds)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", message=".*date/calendar/year zero.*")
return self.min_value + datetime.timedelta(microseconds=result)


class CFTimeStrategyISO8601(st.SearchStrategy):
def __init__(self):
self.date_types = _all_cftime_date_types()
self.calendars = list(self.date_types)

def do_draw(self, data):
calendar = data.draw(st.sampled_from(self.calendars))
date_type = self.date_types[calendar]
with warnings.catch_warnings():
warnings.filterwarnings("ignore", message=".*date/calendar/year zero.*")
daysinmonth = date_type(99999, 12, 1).daysinmonth
min_value = date_type(-99999, 1, 1)
max_value = date_type(99999, 12, daysinmonth, 23, 59, 59, 999999)
strategy = CFTimeStategy(min_value, max_value)
return strategy.do_draw(data)