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
13 changes: 13 additions & 0 deletions arrow/arrow.py
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,19 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow":
1 if not time_delta.isnumeric() else abs(int(time_delta))
)
else:
# num_pattern only matches unsigned digits, so a literal minus
# sign right before the number (e.g. "-1 hours") is invisible
# to it and would otherwise be silently dropped. Direction is
# already conveyed by the "ago"/"in" phrasing, so treat a sign
# here as invalid input rather than letting it disappear.
number_start = match.start() + num_match.start()
if number_start > 0 and input_string[number_start - 1] == "-":
raise ValueError(
"Invalid input String. Negative numbers are not "
"supported by dehumanize(), as direction is already "
"conveyed by phrases such as 'ago' or 'in'. Found a "
f"negative value near: {match_string!r}"
)
change_value = int(num_match.group())

# No time to update if now is the unit
Expand Down
19 changes: 19 additions & 0 deletions tests/test_arrow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2960,6 +2960,25 @@ def test_slavic_locales(self, slavic_locales: List[str]):
assert arw.dehumanize(past_string, locale=lang) == past
assert arw.dehumanize(future_string, locale=lang) == future

def test_negative_numbers_rejected(self):
arw = arrow.Arrow(2000, 6, 18, 5, 55, 0)

# A literal minus sign is invisible to the unsigned number regex, so
# without a check these used to silently return the same result as
# their unsigned counterpart instead of raising.
with pytest.raises(ValueError):
arw.dehumanize("in -1 hours")

with pytest.raises(ValueError):
arw.dehumanize("-3 minutes ago")

with pytest.raises(ValueError):
arw.dehumanize("in -2 days")

# unsigned input must still work exactly as before
assert arw.dehumanize("in 1 hours") == arw.shift(hours=1)
assert arw.dehumanize("3 minutes ago") == arw.shift(minutes=-3)

def test_czech_slovak(self):
# Relevant units for Slavic locale plural logic
units = [
Expand Down
Loading