Skip to content

fix: handle negative values in dehumanize()#1303

Open
jermaine-hicks wants to merge 1 commit into
arrow-py:masterfrom
jermaine-hicks:fix/dehumanize-negative-values
Open

fix: handle negative values in dehumanize()#1303
jermaine-hicks wants to merge 1 commit into
arrow-py:masterfrom
jermaine-hicks:fix/dehumanize-negative-values

Conversation

@jermaine-hicks

Copy link
Copy Markdown

Problem

dehumanize() silently ignores a leading minus sign on numeric values. "in -1 hours" is parsed identically to "in 1 hours" and shifts the time forward instead of producing a correct or clearly-invalid result.

>>> base = arrow.Arrow(2025, 1, 1, 12, 0, 0)
>>> base.dehumanize("in 1 hours")
<Arrow [2025-01-01T13:00:00+00:00]>
>>> base.dehumanize("in -1 hours")   # expected error or backward shift
<Arrow [2025-01-01T13:00:00+00:00]>  # identical to "in 1 hours"

Fixes #1278.

Root cause

The numeric value is extracted with a digit-only regex (\d+), and the unit search template substitutes \d+ for {0}. Neither captures a sign, so the - is dropped. Direction is taken solely from the past/future templates ("... ago" / "in ..."), and the magnitude is always treated as positive.

Fix

humanize() only ever emits positive magnitudes and encodes direction through the past/future templates, so a negative number is malformed input — not a second, conflicting direction signal. This aligns with how dehumanize() already handles every other malformed-input case (scrambled input, missing relative unit, etc.): it raises ValueError.

The fix detects a - immediately preceding the matched digits in the original input string and raises a clear ValueError instead of silently dropping it. Positive inputs are completely unaffected.

Tests

Adds TestArrowDehumanize::test_negative_values, covering negative values in both future ("in -1 hours") and past ("-3 hours ago") forms across several units, plus a sanity check that the positive forms still work. Full suite: 1902 passed, 1 skipped; arrow/arrow.py coverage 100%.

dehumanize() silently dropped a leading minus sign on numeric values, so
"in -1 hours" was parsed identically to "in 1 hours" and shifted forward
instead of producing a correct or clearly-invalid result.

The number is extracted with a digit-only regex (\d+) that never captures
a sign, and direction is derived solely from the past/future templates
("... ago" / "in ..."). A negative magnitude is therefore malformed input
rather than a second, conflicting direction signal -- humanize() never
emits negative numbers. To match the existing dehumanize() convention of
raising ValueError on malformed input (scrambled input, missing relative
unit, etc.), detect a leading minus immediately before the matched digits
and raise a clear ValueError instead of silently dropping it.

Adds a regression test covering negative values in both the future
("in -1 hours") and past ("-3 hours ago") forms across several units.

Fixes arrow-py#1278
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

1 participant