From abd606bc592fc4f6f5aee34c7cfee62416398496 Mon Sep 17 00:00:00 2001 From: aDragon0707 Date: Mon, 7 Sep 2026 19:28:22 +0800 Subject: [PATCH 1/2] fix precisedelta dropping sign of negative timedeltas (fix #379) --- src/humanize/time.py | 20 +++++++++++++++----- tests/test_time.py | 11 +++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/humanize/time.py b/src/humanize/time.py index 975cc03c..4b8d32e3 100644 --- a/src/humanize/time.py +++ b/src/humanize/time.py @@ -5,6 +5,8 @@ from __future__ import annotations +import datetime + __lazy_modules__ = {"humanize.i18n", "humanize.number"} from enum import Enum @@ -546,6 +548,13 @@ def precisedelta( ``` """ + if isinstance(value, datetime.timedelta): + negative = value < datetime.timedelta(0) + elif isinstance(value, (int, float)): + negative = value < 0 + else: + negative = False + date, delta = _date_and_delta(value, precise=True) if date is None: return str(value) @@ -673,12 +682,13 @@ def precisedelta( break if len(texts) == 1: - return texts[0] - - head = ", ".join(texts[:-1]) - tail = texts[-1] + result = texts[0] + else: + head = ", ".join(texts[:-1]) + tail = texts[-1] + result = _("%s and %s") % (head, tail) - return _("%s and %s") % (head, tail) + return f"-{result}" if negative else result def _rounding_by_fmt(format: str, value: float) -> float | int: diff --git a/tests/test_time.py b/tests/test_time.py index c3743bbc..fef2e0a7 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -837,6 +837,17 @@ def test_precisedelta_suppress_units( def test_precisedelta_bogus_call() -> None: assert humanize.precisedelta(None) == "None" + +def test_precisedelta_negative() -> None: + # regression: negative timedeltas must keep their sign + # https://github.com/python-humanize/humanize/issues/379 + assert humanize.precisedelta(dt.timedelta(seconds=-3661)) == "-1 hour, 1 minute and 1 second" + assert humanize.precisedelta(dt.timedelta(seconds=3661)) == "1 hour, 1 minute and 1 second" + assert ( + humanize.precisedelta(dt.timedelta(seconds=-3661), minimum_unit="minutes") + == "-1 hour and 1.02 minutes" + ) + with pytest.raises( ValueError, match="Minimum unit is suppressed and no suitable replacement was found", From a8551abc7a8b951d042a347892c277eeb23aba38 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 13:24:03 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_time.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_time.py b/tests/test_time.py index fef2e0a7..10004d03 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -841,8 +841,14 @@ def test_precisedelta_bogus_call() -> None: def test_precisedelta_negative() -> None: # regression: negative timedeltas must keep their sign # https://github.com/python-humanize/humanize/issues/379 - assert humanize.precisedelta(dt.timedelta(seconds=-3661)) == "-1 hour, 1 minute and 1 second" - assert humanize.precisedelta(dt.timedelta(seconds=3661)) == "1 hour, 1 minute and 1 second" + assert ( + humanize.precisedelta(dt.timedelta(seconds=-3661)) + == "-1 hour, 1 minute and 1 second" + ) + assert ( + humanize.precisedelta(dt.timedelta(seconds=3661)) + == "1 hour, 1 minute and 1 second" + ) assert ( humanize.precisedelta(dt.timedelta(seconds=-3661), minimum_unit="minutes") == "-1 hour and 1.02 minutes"