Skip to content
Closed
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
24 changes: 14 additions & 10 deletions src/humanize/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ def scientific(value, precision=2):
>>> scientific(int(500))
'5.00 x 10²'
>>> scientific(-1000)
'1.00 x 10⁻³'
'-1.00 x 10³'
>>> scientific(1000, 1)
'1.0 x 10³'
>>> scientific(1000, 3)
Expand Down Expand Up @@ -357,18 +357,23 @@ def scientific(value, precision=2):
"+": "⁺",
"-": "⁻",
}
negative = False
try:
if "-" in str(value):
value = str(value).replace("-", "")
negative = True

if isinstance(value, str):
value = float(value)
else:
value = float(value)
except (ValueError, TypeError):
return value

if not math.isfinite(value):
return value

negative = value < 0
value = abs(value)

try:
fmt = "{:.%se}" % str(int(precision))
n = fmt.format(value)

except (ValueError, TypeError):
return value

Expand All @@ -380,13 +385,12 @@ def scientific(value, precision=2):
part2 = part2.replace("+0", "")

new_part2 = []
if negative:
new_part2.append(exponents["-"])

for char in part2:
new_part2.append(exponents[char])

final_str = part1 + " x 10" + "".join(new_part2)
if negative:
final_str = "-" + final_str

return final_str

Expand Down
9 changes: 7 additions & 2 deletions src/humanize/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,13 @@ def naturaldelta(

use_months = months

seconds = abs(delta.seconds)
days = abs(delta.days)
# timedelta normalizes negatives as (days=-1, seconds=86395) for -5s;
# abs(days)/abs(seconds) separately would report that as "a day".
if delta.total_seconds() < 0:
delta = -delta

seconds = delta.seconds
days = delta.days
years = days // 365
days = days % 365
months = int(days // 30.5)
Expand Down
12 changes: 11 additions & 1 deletion tests/test_number.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def test_fractional(test_input, expected):
"test_args, expected",
[
([1000], "1.00 x 10³"),
([-1000], "1.00 x 10⁻³"),
([-1000], "-1.00 x 10³"),
([5.5], "5.50 x 10⁰"),
([5781651000], "5.78 x 10⁹"),
(["1000"], "1.00 x 10³"),
Expand Down Expand Up @@ -172,3 +172,13 @@ def test_scientific(test_args, expected):
)
def test_clamp(test_args, expected):
assert humanize.clamp(*test_args) == expected


def test_scientific_negative_and_nonfinite():
assert humanize.scientific(-1000) == "-1.00 x 10³"
assert humanize.scientific(-0.001) == "-1.00 x 10⁻³"
# non-finite values are returned unchanged (no cryptic split errors)
assert humanize.scientific(float("inf")) == float("inf")
assert humanize.scientific(float("-inf")) == float("-inf")
import math
assert math.isnan(humanize.scientific(float("nan")))
7 changes: 7 additions & 0 deletions tests/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,3 +623,10 @@ def test_time_unit():

with pytest.raises(TypeError):
years < "foo"


def test_naturaldelta_negative_duration():
"""Negative durations must match the positive magnitude (not timedelta day borrow)."""
assert humanize.naturaldelta(-5) == humanize.naturaldelta(5)
assert humanize.naturaldelta(dt.timedelta(seconds=-5)) == "5 seconds"
assert humanize.naturaldelta(dt.timedelta(days=-1)) == "a day"