66import numpy as np
77import pandas as pd
88import pytest
9- from packaging .version import Version
109
1110import xarray as xr
12- from xarray .coding .cftime_offsets import _new_to_legacy_freq
11+ from xarray .coding .cftime_offsets import (
12+ CFTIME_TICKS ,
13+ Day ,
14+ _new_to_legacy_freq ,
15+ to_offset ,
16+ )
1317from xarray .coding .cftimeindex import CFTimeIndex
1418from xarray .core .resample_cftime import CFTimeGrouper
19+ from xarray .tests import has_pandas_3
1520
1621cftime = pytest .importorskip ("cftime" )
1722
5459]
5560
5661
62+ def has_tick_resample_freq (freqs ):
63+ resample_freq , _ = freqs
64+ resample_freq_as_offset = to_offset (resample_freq )
65+ return isinstance (resample_freq_as_offset , CFTIME_TICKS )
66+
67+
68+ def has_non_tick_resample_freq (freqs ):
69+ return not has_tick_resample_freq (freqs )
70+
71+
72+ FREQS_WITH_TICK_RESAMPLE_FREQ = list (filter (has_tick_resample_freq , FREQS ))
73+ FREQS_WITH_NON_TICK_RESAMPLE_FREQ = list (filter (has_non_tick_resample_freq , FREQS ))
74+
75+
5776def compare_against_pandas (
5877 da_datetimeindex ,
5978 da_cftimeindex ,
@@ -110,22 +129,14 @@ def da(index) -> xr.DataArray:
110129 )
111130
112131
113- @pytest .mark .parametrize ("freqs" , FREQS , ids = lambda x : "{}->{}" .format (* x ))
132+ @pytest .mark .parametrize (
133+ "freqs" , FREQS_WITH_TICK_RESAMPLE_FREQ , ids = lambda x : "{}->{}" .format (* x )
134+ )
114135@pytest .mark .parametrize ("closed" , [None , "left" , "right" ])
115136@pytest .mark .parametrize ("label" , [None , "left" , "right" ])
116137@pytest .mark .parametrize ("offset" , [None , "5s" ], ids = lambda x : f"{ x } " )
117- def test_resample (freqs , closed , label , offset ) -> None :
138+ def test_resample_with_tick_resample_freq (freqs , closed , label , offset ) -> None :
118139 initial_freq , resample_freq = freqs
119- if (
120- resample_freq == "4001D"
121- and closed == "right"
122- and Version (pd .__version__ ) < Version ("2.2" )
123- ):
124- pytest .skip (
125- "Pandas fixed a bug in this test case in version 2.2, which we "
126- "ported to xarray, so this test no longer produces the same "
127- "result as pandas for earlier pandas versions."
128- )
129140 start = "2000-01-01T12:07:01"
130141 origin = "start"
131142
@@ -149,6 +160,43 @@ def test_resample(freqs, closed, label, offset) -> None:
149160 )
150161
151162
163+ @pytest .mark .parametrize (
164+ "freqs" , FREQS_WITH_NON_TICK_RESAMPLE_FREQ , ids = lambda x : "{}->{}" .format (* x )
165+ )
166+ @pytest .mark .parametrize ("closed" , [None , "left" , "right" ])
167+ @pytest .mark .parametrize ("label" , [None , "left" , "right" ])
168+ def test_resample_with_non_tick_resample_freq (freqs , closed , label ) -> None :
169+ initial_freq , resample_freq = freqs
170+ resample_freq_as_offset = to_offset (resample_freq )
171+ if isinstance (resample_freq_as_offset , Day ) and not has_pandas_3 :
172+ pytest .skip ("Only valid for pandas >= 3.0" )
173+ start = "2000-01-01T12:07:01"
174+
175+ # Set offset and origin to their default values since they have no effect
176+ # on resampling data with a non-tick resample frequency.
177+ offset = None
178+ origin = "start_day"
179+
180+ datetime_index = pd .date_range (
181+ start = start , periods = 5 , freq = _new_to_legacy_freq (initial_freq )
182+ )
183+ cftime_index = xr .date_range (
184+ start = start , periods = 5 , freq = initial_freq , use_cftime = True
185+ )
186+ da_datetimeindex = da (datetime_index )
187+ da_cftimeindex = da (cftime_index )
188+
189+ compare_against_pandas (
190+ da_datetimeindex ,
191+ da_cftimeindex ,
192+ resample_freq ,
193+ closed = closed ,
194+ label = label ,
195+ offset = offset ,
196+ origin = origin ,
197+ )
198+
199+
152200@pytest .mark .parametrize (
153201 ("freq" , "expected" ),
154202 [
@@ -228,7 +276,7 @@ def test_invalid_offset_error(offset: str | int) -> None:
228276 cftime_index = xr .date_range ("2000" , periods = 5 , use_cftime = True )
229277 da_cftime = da (cftime_index )
230278 with pytest .raises (ValueError , match = "offset must be" ):
231- da_cftime .resample (time = "2D " , offset = offset ) # type: ignore[arg-type]
279+ da_cftime .resample (time = "2h " , offset = offset ) # type: ignore[arg-type]
232280
233281
234282def test_timedelta_offset () -> None :
@@ -238,6 +286,15 @@ def test_timedelta_offset() -> None:
238286 cftime_index = xr .date_range ("2000" , periods = 5 , use_cftime = True )
239287 da_cftime = da (cftime_index )
240288
241- timedelta_result = da_cftime .resample (time = "2D " , offset = timedelta ).mean ()
242- string_result = da_cftime .resample (time = "2D " , offset = string ).mean ()
289+ timedelta_result = da_cftime .resample (time = "2h " , offset = timedelta ).mean ()
290+ string_result = da_cftime .resample (time = "2h " , offset = string ).mean ()
243291 xr .testing .assert_identical (timedelta_result , string_result )
292+
293+
294+ @pytest .mark .parametrize (("option" , "value" ), [("offset" , "5s" ), ("origin" , "start" )])
295+ def test_non_tick_option_warning (option , value ) -> None :
296+ cftime_index = xr .date_range ("2000" , periods = 5 , use_cftime = True )
297+ da_cftime = da (cftime_index )
298+ kwargs = {option : value }
299+ with pytest .warns (RuntimeWarning , match = option ):
300+ da_cftime .resample (time = "ME" , ** kwargs )
0 commit comments