Skip to content

Commit

Permalink
Merge pull request #144 from phijor/master
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk committed Oct 3, 2020
2 parents 3993a24 + b37dc30 commit d30c2b6
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Expand Up @@ -6,7 +6,7 @@ repos:
args: ["--py36-plus"]

- repo: https://github.com/psf/black
rev: 19.10b0
rev: 20.8b1
hooks:
- id: black
args: ["--target-version", "py36"]
Expand Down
26 changes: 21 additions & 5 deletions src/humanize/time.py
Expand Up @@ -81,7 +81,7 @@ def date_and_delta(value, *, now=None):
return date, abs_timedelta(delta)


def naturaldelta(value, months=True, minimum_unit="seconds"):
def naturaldelta(value, months=True, minimum_unit="seconds", when=None):
"""Return a natural representation of a timedelta or number of seconds.
This is similar to `naturaltime`, but does not add tense to the result.
Expand All @@ -91,16 +91,30 @@ def naturaldelta(value, months=True, minimum_unit="seconds"):
months (bool): If `True`, then a number of months (based on 30.5 days) will be
used for fuzziness between years.
minimum_unit (str): The lowest unit that can be used.
when (datetime.timedelta): Point in time relative to which _value_ is
interpreted. Defaults to the current time in the local timezone.
Returns:
str: A natural representation of the amount of time elapsed.
Examples
Compare two timestamps in a custom local timezone::
import datetime as dt
from dateutil.tz import gettz
berlin = gettz("Europe/Berlin")
now = dt.datetime.now(tz=berlin)
later = now + dt.timedelta(minutes=30)
assert naturaldelta(later, when=now) == "30 minutes"
"""
tmp = Unit[minimum_unit.upper()]
if tmp not in (Unit.SECONDS, Unit.MILLISECONDS, Unit.MICROSECONDS):
raise ValueError(f"Minimum unit '{minimum_unit}' not supported")
minimum_unit = tmp

date, delta = date_and_delta(value)
date, delta = date_and_delta(value, now=when)
if date is None:
return value

Expand Down Expand Up @@ -173,7 +187,7 @@ def naturaldelta(value, months=True, minimum_unit="seconds"):
return ngettext("%d year", "%d years", years) % years


def naturaltime(value, future=False, months=True, minimum_unit="seconds"):
def naturaltime(value, future=False, months=True, minimum_unit="seconds", when=None):
"""Return a natural representation of a time in a resolution that makes sense.
This is more or less compatible with Django's `naturaltime` filter.
Expand All @@ -186,11 +200,13 @@ def naturaltime(value, future=False, months=True, minimum_unit="seconds"):
months (bool): If `True`, then a number of months (based on 30.5 days) will be
used for fuzziness between years.
minimum_unit (str): The lowest unit that can be used.
when (datetime.datetime): Point in time relative to which _value_ is
interpreted. Defaults to the current time in the local timezone.
Returns:
str: A natural representation of the input in a resolution that makes sense.
"""
now = _now()
now = when or _now()
date, delta = date_and_delta(value, now=now)
if date is None:
return value
Expand All @@ -199,7 +215,7 @@ def naturaltime(value, future=False, months=True, minimum_unit="seconds"):
future = date > now

ago = _("%s from now") if future else _("%s ago")
delta = naturaldelta(delta, months, minimum_unit)
delta = naturaldelta(delta, months, minimum_unit, when=when)

if delta == _("a moment"):
return _("now")
Expand Down
33 changes: 33 additions & 0 deletions tests/test_time.py
Expand Up @@ -24,6 +24,8 @@

with freeze_time("2020-02-02"):
NOW = dt.datetime.now()
NOW_UTC = dt.datetime.now(tz=dt.timezone.utc)
NOW_UTC_PLUS_01_00 = dt.datetime.now(tz=dt.timezone(offset=dt.timedelta(hours=1)))
TODAY = dt.date.today()
TOMORROW = TODAY + ONE_DAY_DELTA
YESTERDAY = TODAY - ONE_DAY_DELTA
Expand Down Expand Up @@ -331,6 +333,37 @@ def test_naturaldelta_minimum_unit_explicit(minimum_unit, seconds, expected):
assert humanize.naturaldelta(delta, minimum_unit=minimum_unit) == expected


@pytest.mark.parametrize(
"test_input, when, expected",
[
(NOW, NOW, "a moment"),
(NOW_UTC, NOW_UTC, "a moment"),
],
)
def test_naturaldelta_when_explicit(test_input, when, expected):
# Act / Assert
assert humanize.naturaldelta(test_input, when=when) == expected


@pytest.mark.parametrize(
"value, when",
[
(NOW_UTC, None),
(NOW_UTC, NOW),
(NOW_UTC_PLUS_01_00, None),
(NOW_UTC_PLUS_01_00, NOW),
],
)
def test_naturaldelta_when_missing_tzinfo(value, when):
"""Subtraction `when - value` is not defined by the `datetime` module when
either operand has not timezone-info (`tz=None`) and raises a TypeError.
"""

# Act / Assert
with pytest.raises(TypeError):
humanize.naturaldelta(value, when=when)


@pytest.mark.parametrize(
"seconds, expected",
[
Expand Down

0 comments on commit d30c2b6

Please sign in to comment.