Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an "all" granularity to humanize #1018

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
10 changes: 8 additions & 2 deletions arrow/arrow.py
Expand Up @@ -69,6 +69,7 @@

_GRANULARITY = Literal[
"auto",
"all",
"second",
"minute",
"hour",
Expand Down Expand Up @@ -1129,7 +1130,8 @@ def humanize(
:param locale: (optional) a ``str`` specifying a locale. Defaults to 'en-us'.
:param only_distance: (optional) returns only time difference eg: "11 seconds" without "in" or "ago" part.
:param granularity: (optional) defines the precision of the output. Set it to strings 'second', 'minute',
'hour', 'day', 'week', 'month' or 'year' or a list of any combination of these strings
'hour', 'day', 'week', 'month' or 'year' or a list of any combination of these strings.
Set it to 'all' to include all possible units in the granularity.

Usage::

Expand Down Expand Up @@ -1227,7 +1229,7 @@ def humanize(
years = sign * max(delta_second // self._SECS_PER_YEAR, 2)
return locale.describe("years", years, only_distance=only_distance)

elif isinstance(granularity, str):
elif isinstance(granularity, str) and granularity != "all":
granularity = cast(TimeFrameLiteral, granularity) # type: ignore[assignment]

if granularity == "second":
Expand Down Expand Up @@ -1290,6 +1292,10 @@ def gather_timeframes(_delta: float, _frame: TimeFrameLiteral) -> float:
"minute",
"second",
)

if granularity == "all":
granularity = cast(List[_GRANULARITY], frames)

for frame in frames:
delta = gather_timeframes(delta, frame)

Expand Down
2 changes: 2 additions & 0 deletions docs/guide.rst
Expand Up @@ -235,6 +235,8 @@ Indicate a specific time granularity (or multiple):
'an hour and 6 minutes ago'
>>> future.humanize(present, only_distance=True, granularity=["hour", "minute"])
'an hour and 6 minutes'
>>> future.humanize(present, granularity="all")
'in 0 years 0 months 0 weeks 0 days an hour 6 minutes and 0 seconds'

Support for a growing number of locales (see ``locales.py`` for supported languages):

Expand Down
43 changes: 43 additions & 0 deletions tests/test_arrow.py
Expand Up @@ -1919,6 +1919,49 @@ def test_multiple_granularity(self):
== "a minute and 2 seconds ago"
)

def test_all_granularity(self):
assert (
self.now.humanize(self.now, granularity="all")
== "in 0 years 0 quarters 0 months 0 weeks 0 days 0 hours 0 minutes and 0 seconds"
)

later105 = self.arrow.shift(seconds=10**5)
assert (
self.arrow.humanize(later105, granularity="all")
== "0 years 0 quarters 0 months 0 weeks a day 3 hours 46 minutes and 40 seconds ago"
)
assert (
later105.humanize(self.arrow, granularity="all")
== "in 0 years 0 quarters 0 months 0 weeks a day 3 hours 46 minutes and 40 seconds"
)

later108 = self.arrow.shift(seconds=10**8)
assert (
self.arrow.humanize(later108, granularity="all")
== "3 years 0 quarters 2 months 0 weeks a day 9 hours 46 minutes and 40 seconds ago"
)
assert (
later108.humanize(self.arrow, granularity="all")
== "in 3 years 0 quarters 2 months 0 weeks a day 9 hours 46 minutes and 40 seconds"
)
assert (
self.arrow.humanize(later108, granularity="all", only_distance=True)
== "3 years 0 quarters 2 months 0 weeks a day 9 hours 46 minutes and 40 seconds"
)

later_two_months = self.arrow.shift(days=61)
assert (
self.arrow.humanize(later_two_months, granularity="all")
== "0 years 0 quarters 2 months 0 weeks 0 days 0 hours 0 minutes and 0 seconds ago"
)
assert (
later_two_months.humanize(self.arrow, granularity="all")
== "in 0 years 0 quarters 2 months 0 weeks 0 days 0 hours 0 minutes and 0 seconds"
)

with pytest.raises(ValueError):
self.arrow.humanize(later108, granularity=["all", "year"])

def test_seconds(self):
later = self.now.shift(seconds=10)

Expand Down