diff --git a/humanfriendly/__init__.py b/humanfriendly/__init__.py index 4c0a333..212e503 100644 --- a/humanfriendly/__init__.py +++ b/humanfriendly/__init__.py @@ -429,13 +429,14 @@ def format_timespan(num_seconds, detailed=False, max_units=3): '1 year, 2 days and 3 hours' """ num_seconds = coerce_seconds(num_seconds) - if num_seconds < 60 and not detailed: + if abs(num_seconds) < 60 and not detailed: # Fast path. return pluralize(round_number(num_seconds), 'second') else: # Slow path. + is_negative = num_seconds < 0 result = [] - num_seconds = decimal.Decimal(str(num_seconds)) + num_seconds = decimal.Decimal(str(abs(num_seconds))) relevant_units = list(reversed(time_units[0 if detailed else 3:])) for unit in relevant_units: # Extract the unit count from the remaining time. @@ -454,13 +455,16 @@ def format_timespan(num_seconds, detailed=False, max_units=3): result.append(pluralize(count, unit['singular'], unit['plural'])) if len(result) == 1: # A single count/unit combination. - return result[0] + formatted = result[0] else: if not detailed: # Remove `insignificant' data from the formatted timespan. result = result[:max_units] # Format the timespan in a readable way. - return concatenate(result) + formatted = concatenate(result) + # Apply a single leading minus sign for negative timespans instead of + # letting each individual unit carry its own sign. + return ('-' + formatted) if is_negative else formatted def parse_timespan(timespan): diff --git a/humanfriendly/tests.py b/humanfriendly/tests.py index 72dad99..eadcb70 100644 --- a/humanfriendly/tests.py +++ b/humanfriendly/tests.py @@ -448,6 +448,12 @@ def test_format_timespan(self): assert '5 minutes and 0.3 seconds' == format_timespan(300.300) assert '1 second and 15 milliseconds' == format_timespan(1.015, detailed=True) assert '10 seconds and 15 milliseconds' == format_timespan(10.015, detailed=True) + # Negative timespans should get a single leading minus sign instead + # of a minus sign on every individual unit. + assert '-1 hour, 1 minute and 40 seconds' == format_timespan(-3700, detailed=True) + assert '-1 minute and 30 seconds' == format_timespan(-90, detailed=True) + assert '-30 seconds' == format_timespan(-30, detailed=False) + assert '-1 hour, 1 minute and 40 seconds' == format_timespan(-3700, detailed=False) assert '1 microsecond and 50 nanoseconds' == format_timespan(0.00000105, detailed=True) # Test the datetime.timedelta support: # https://github.com/xolox/python-humanfriendly/issues/27