Skip to content

Reject negative numbers in dehumanize instead of silently dropping the sign - #1344

Open
afonsojanu wants to merge 1 commit into
arrow-py:masterfrom
afonsojanu:fix/dehumanize-reject-negative-numbers
Open

Reject negative numbers in dehumanize instead of silently dropping the sign#1344
afonsojanu wants to merge 1 commit into
arrow-py:masterfrom
afonsojanu:fix/dehumanize-reject-negative-numbers

Conversation

@afonsojanu

Copy link
Copy Markdown

Fixes #1278

dehumanize() extracts the number for each matched time unit with an unsigned pattern (\d+), so a literal minus sign right before it, as in "in -1 hours" or "-3 minutes ago", is invisible to that regex. Direction already comes from matching the whole string against the locale's "ago"/"in" templates separately, so the sign just gets thrown away and the result lands in the opposite direction from what was written, with nothing raised:

now = arrow.now()
now.dehumanize("in 1 hours")   # +1 hour
now.dehumanize("in -1 hours")  # also +1 hour, same as above

Since direction is already conveyed by that phrasing, a signed number on top of it doesn't have a sensible fallback meaning, so this raises a ValueError instead. The check looks at the character in the original input immediately before the matched digits, since the number match itself never includes a preceding minus (word-boundary matching stops right at the digit).

Added test_negative_numbers_rejected covering the two cases from the issue plus "in -2 days", and confirmed unsigned input in both directions still behaves exactly as before. Verified the new test actually catches the bug by reverting just the source change and rerunning it, full suite (1903 tests) passes, and black/flake8/mypy/isort are all clean on the touched files.

…e sign

The number pattern used to extract digits from a matched time unit is
unsigned (\d+), so a literal minus sign right before a number, as in
"in -1 hours" or "-3 minutes ago", is simply invisible to it. The
direction of the shift already comes from matching the whole string
against the locale's "ago"/"in" templates, so the minus sign gets
silently thrown away and the result ends up in the opposite direction
from what was written, with no error at all.

Since direction is already conveyed by that phrasing, a signed number
on top of it is contradictory input rather than something with a
sensible meaning to fall back to, so this now raises instead.
Copilot AI lite review requested due to automatic review settings August 31, 2026 19:04
@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (2224255) to head (bff81bf).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #1344   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           10        10           
  Lines         2315      2318    +3     
  Branches       358       359    +1     
=========================================
+ Hits          2315      2318    +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Arrow.dehumanize() to reject user input containing negative numeric values (e.g., "in -1 hours", "-3 minutes ago") which were previously parsed incorrectly due to unsigned digit matching, and adds regression tests to ensure the new behavior.

Changes:

  • Add a validation check in Arrow.dehumanize() to detect and reject a literal - immediately preceding matched digits.
  • Add a new test (test_negative_numbers_rejected) covering multiple negative-value inputs and confirming unsigned behavior is unchanged.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
arrow/arrow.py Adds explicit rejection of negative numeric literals during dehumanize parsing.
tests/test_arrow.py Adds regression test coverage for negative-number inputs and confirms existing unsigned behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread arrow/arrow.py
"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}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dehumanize() silently ignores negative time values, returning wrong results

2 participants