@@ -751,7 +751,7 @@ def _emit_freq_deprecation_warning(deprecated_freq):
751
751
emit_user_level_warning (message , FutureWarning )
752
752
753
753
754
- def to_offset (freq ):
754
+ def to_offset (freq , warn = True ):
755
755
"""Convert a frequency string to the appropriate subclass of
756
756
BaseCFTimeOffset."""
757
757
if isinstance (freq , BaseCFTimeOffset ):
@@ -763,7 +763,7 @@ def to_offset(freq):
763
763
raise ValueError ("Invalid frequency string provided" )
764
764
765
765
freq = freq_data ["freq" ]
766
- if freq in _DEPRECATED_FREQUENICES :
766
+ if warn and freq in _DEPRECATED_FREQUENICES :
767
767
_emit_freq_deprecation_warning (freq )
768
768
multiples = freq_data ["multiple" ]
769
769
multiples = 1 if multiples is None else int (multiples )
@@ -1229,7 +1229,8 @@ def date_range(
1229
1229
start = start ,
1230
1230
end = end ,
1231
1231
periods = periods ,
1232
- freq = freq ,
1232
+ # TODO remove translation once requiring pandas >= 2.2
1233
+ freq = _new_to_legacy_freq (freq ),
1233
1234
tz = tz ,
1234
1235
normalize = normalize ,
1235
1236
name = name ,
@@ -1257,6 +1258,96 @@ def date_range(
1257
1258
)
1258
1259
1259
1260
1261
+ def _new_to_legacy_freq (freq ):
1262
+ # xarray will now always return "ME" and "QE" for MonthEnd and QuarterEnd
1263
+ # frequencies, but older versions of pandas do not support these as
1264
+ # frequency strings. Until xarray's minimum pandas version is 2.2 or above,
1265
+ # we add logic to continue using the deprecated "M" and "Q" frequency
1266
+ # strings in these circumstances.
1267
+
1268
+ # NOTE: other conversions ("h" -> "H", ..., "ns" -> "N") not required
1269
+
1270
+ # TODO: remove once requiring pandas >= 2.2
1271
+ if not freq or Version (pd .__version__ ) >= Version ("2.2" ):
1272
+ return freq
1273
+
1274
+ try :
1275
+ freq_as_offset = to_offset (freq )
1276
+ except ValueError :
1277
+ # freq may be valid in pandas but not in xarray
1278
+ return freq
1279
+
1280
+ if isinstance (freq_as_offset , MonthEnd ) and "ME" in freq :
1281
+ freq = freq .replace ("ME" , "M" )
1282
+ elif isinstance (freq_as_offset , QuarterEnd ) and "QE" in freq :
1283
+ freq = freq .replace ("QE" , "Q" )
1284
+ elif isinstance (freq_as_offset , YearBegin ) and "YS" in freq :
1285
+ freq = freq .replace ("YS" , "AS" )
1286
+ elif isinstance (freq_as_offset , YearEnd ):
1287
+ # testing for "Y" is required as this was valid in xarray 2023.11 - 2024.01
1288
+ if "Y-" in freq :
1289
+ # Check for and replace "Y-" instead of just "Y" to prevent
1290
+ # corrupting anchored offsets that contain "Y" in the month
1291
+ # abbreviation, e.g. "Y-MAY" -> "A-MAY".
1292
+ freq = freq .replace ("Y-" , "A-" )
1293
+ elif "YE-" in freq :
1294
+ freq = freq .replace ("YE-" , "A-" )
1295
+ elif "A-" not in freq and freq .endswith ("Y" ):
1296
+ freq = freq .replace ("Y" , "A" )
1297
+ elif freq .endswith ("YE" ):
1298
+ freq = freq .replace ("YE" , "A" )
1299
+
1300
+ return freq
1301
+
1302
+
1303
+ def _legacy_to_new_freq (freq ):
1304
+ # to avoid internal deprecation warnings when freq is determined using pandas < 2.2
1305
+
1306
+ # TODO: remove once requiring pandas >= 2.2
1307
+
1308
+ if not freq or Version (pd .__version__ ) >= Version ("2.2" ):
1309
+ return freq
1310
+
1311
+ try :
1312
+ freq_as_offset = to_offset (freq , warn = False )
1313
+ except ValueError :
1314
+ # freq may be valid in pandas but not in xarray
1315
+ return freq
1316
+
1317
+ if isinstance (freq_as_offset , MonthEnd ) and "ME" not in freq :
1318
+ freq = freq .replace ("M" , "ME" )
1319
+ elif isinstance (freq_as_offset , QuarterEnd ) and "QE" not in freq :
1320
+ freq = freq .replace ("Q" , "QE" )
1321
+ elif isinstance (freq_as_offset , YearBegin ) and "YS" not in freq :
1322
+ freq = freq .replace ("AS" , "YS" )
1323
+ elif isinstance (freq_as_offset , YearEnd ):
1324
+ if "A-" in freq :
1325
+ # Check for and replace "A-" instead of just "A" to prevent
1326
+ # corrupting anchored offsets that contain "Y" in the month
1327
+ # abbreviation, e.g. "A-MAY" -> "YE-MAY".
1328
+ freq = freq .replace ("A-" , "YE-" )
1329
+ elif "Y-" in freq :
1330
+ freq = freq .replace ("Y-" , "YE-" )
1331
+ elif freq .endswith ("A" ):
1332
+ # the "A-MAY" case is already handled above
1333
+ freq = freq .replace ("A" , "YE" )
1334
+ elif "YE" not in freq and freq .endswith ("Y" ):
1335
+ # the "Y-MAY" case is already handled above
1336
+ freq = freq .replace ("Y" , "YE" )
1337
+ elif isinstance (freq_as_offset , Hour ):
1338
+ freq = freq .replace ("H" , "h" )
1339
+ elif isinstance (freq_as_offset , Minute ):
1340
+ freq = freq .replace ("T" , "min" )
1341
+ elif isinstance (freq_as_offset , Second ):
1342
+ freq = freq .replace ("S" , "s" )
1343
+ elif isinstance (freq_as_offset , Millisecond ):
1344
+ freq = freq .replace ("L" , "ms" )
1345
+ elif isinstance (freq_as_offset , Microsecond ):
1346
+ freq = freq .replace ("U" , "us" )
1347
+
1348
+ return freq
1349
+
1350
+
1260
1351
def date_range_like (source , calendar , use_cftime = None ):
1261
1352
"""Generate a datetime array with the same frequency, start and end as
1262
1353
another one, but in a different calendar.
@@ -1301,21 +1392,8 @@ def date_range_like(source, calendar, use_cftime=None):
1301
1392
"`date_range_like` was unable to generate a range as the source frequency was not inferable."
1302
1393
)
1303
1394
1304
- # xarray will now always return "ME" and "QE" for MonthEnd and QuarterEnd
1305
- # frequencies, but older versions of pandas do not support these as
1306
- # frequency strings. Until xarray's minimum pandas version is 2.2 or above,
1307
- # we add logic to continue using the deprecated "M" and "Q" frequency
1308
- # strings in these circumstances.
1309
- if Version (pd .__version__ ) < Version ("2.2" ):
1310
- freq_as_offset = to_offset (freq )
1311
- if isinstance (freq_as_offset , MonthEnd ) and "ME" in freq :
1312
- freq = freq .replace ("ME" , "M" )
1313
- elif isinstance (freq_as_offset , QuarterEnd ) and "QE" in freq :
1314
- freq = freq .replace ("QE" , "Q" )
1315
- elif isinstance (freq_as_offset , YearBegin ) and "YS" in freq :
1316
- freq = freq .replace ("YS" , "AS" )
1317
- elif isinstance (freq_as_offset , YearEnd ) and "YE" in freq :
1318
- freq = freq .replace ("YE" , "A" )
1395
+ # TODO remove once requiring pandas >= 2.2
1396
+ freq = _legacy_to_new_freq (freq )
1319
1397
1320
1398
use_cftime = _should_cftime_be_used (source , calendar , use_cftime )
1321
1399
0 commit comments