diff --git a/arrow/arrow.py b/arrow/arrow.py index eecf23266..abb390756 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -1382,7 +1382,7 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": # Create an object containing the relative time info time_object_info = dict.fromkeys( - ["seconds", "minutes", "hours", "days", "weeks", "months", "years"], 0 + ["seconds", "minutes", "hours", "days", "weeks", "months", "years"], 0.0 ) # Create an object representing if unit has been seen @@ -1391,8 +1391,8 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": False, ) - # Create a regex pattern object for numbers - num_pattern = re.compile(r"\d+") + # Create a regex pattern object for numbers (integers and decimals like "1.5") + num_pattern = re.compile(r"\d+(?:\.\d+)?") # Search input string for each time unit within locale for unit, unit_object in locale_obj.timeframes.items(): @@ -1408,7 +1408,7 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": for time_delta, time_string in strings_to_search.items(): # Replace {0} with regex \d representing digits search_string = str(time_string) - search_string = search_string.format(r"\d+") + search_string = search_string.format(r"\d+(?:\.\d+)?") # Create search pattern and find within string pattern = re.compile(rf"(^|\b|\d){search_string}") @@ -1425,10 +1425,10 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": # Need for absolute value as some locales have signs included in their objects if not num_match: change_value = ( - 1 if not time_delta.isnumeric() else abs(int(time_delta)) + 1.0 if not time_delta.isnumeric() else abs(float(time_delta)) ) else: - change_value = int(num_match.group()) + change_value = float(num_match.group()) # No time to update if now is the unit if unit == "now": diff --git a/tests/test_arrow.py b/tests/test_arrow.py index b595e4e21..fa9faa953 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -2987,6 +2987,13 @@ def test_czech_slovak(self): assert arw.dehumanize(past_string, locale=lang) == past assert arw.dehumanize(future_string, locale=lang) == future + def test_fractional_units(self): + arw = arrow.Arrow(2026, 1, 1, tzinfo="UTC") + assert arw.dehumanize("1.5 hours ago") == arw.shift(hours=-1.5) + assert arw.dehumanize("0.5 days ago") == arw.shift(days=-0.5) + assert arw.dehumanize("in 1.5 hours") == arw.shift(hours=1.5) + assert arw.dehumanize("2.5 weeks ago") == arw.shift(weeks=-2.5) + class TestArrowIsBetween: def test_start_before_end(self):