diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index bc66211aa03eb..621ae0d31fce0 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -6537,17 +6537,20 @@ cpdef to_offset(freq, bint is_period=False): else: result = result + offset except (ValueError, TypeError) as err: - raise_invalid_freq( - freq=freq, - extra_message=f"Failed to parse with error message: {repr(err)}" - ) + # --- FALLBACK--- + try: + td = Timedelta(freq) + result = delta_to_tick(td) + except (ValueError, TypeError): + # If Timedelta parsing also fails, raise the original error + raise_invalid_freq( + freq=freq, + extra_message=f"Failed to parse with error message: {repr(err)}" + ) - # TODO(3.0?) once deprecation of "d" is enforced, the check for it here - # can be removed if ( isinstance(result, Hour) and result.n % 24 == 0 - and ("d" in freq or "D" in freq) ): # Since Day is no longer a Tick, delta_to_tick returns Hour above, # so we convert back here. diff --git a/pandas/tests/tseries/offsets/test_offsets_fallback.py b/pandas/tests/tseries/offsets/test_offsets_fallback.py new file mode 100644 index 0000000000000..4a9ddfea57d9b --- /dev/null +++ b/pandas/tests/tseries/offsets/test_offsets_fallback.py @@ -0,0 +1,38 @@ +import pytest + +from pandas import Timedelta + +from pandas.tseries.frequencies import to_offset + + +def test_to_offset_timedelta_string_fallback(): + # This is the case you found: str(Timedelta) + # Previously, this might have failed in the regex loop + freq_str = "0 days 01:30:00" + result = to_offset(freq_str) + + expected = to_offset("1h30min") + assert result == expected + + +@pytest.mark.parametrize( + "freq", + [ + "1 days 02:00:00", + "00:00:00.001", + "1h 30min", # spaces between components + ], +) +def test_to_offset_consistent_with_timedelta(freq): + # Ensure our fallback produces the same result as direct Timedelta conversion + result = to_offset(freq) + expected_td = Timedelta(freq) + + # Convert result to total seconds to compare magnitude + assert result.nanos == expected_td.value + + +def test_to_offset_invalid_still_raises(): + # Ensure that if BOTH fail, we still get the Invalid Frequency error + with pytest.raises(ValueError, match="Invalid frequency"): + to_offset("not_a_time_at_all")