Skip to content
Open
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
17 changes: 10 additions & 7 deletions pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
38 changes: 38 additions & 0 deletions pandas/tests/tseries/offsets/test_offsets_fallback.py
Original file line number Diff line number Diff line change
@@ -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")
Loading