diff --git a/README.md b/README.md
index 6c0fc1a..3f00987 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,20 @@ readable size or throughput. It is localized to:
 'an hour ago'
 ```
 
+### Precise time delta
+
+```pycon
+>>> import humanize
+>>> import datetime as dt
+>>> delta = dt.timedelta(seconds=3633, days=2, microseconds=123000)
+>>> humanize.precisedelta(delta)
+'2 days, 1 hour and 33.12 seconds'
+>>> humanize.precisedelta(delta, minimum_unit="microseconds")
+'2 days, 1 hour, 33 seconds and 123 milliseconds'
+>>> humanize.precisedelta(delta, suppress=["days"], format="%0.4f")
+'49 hours and 33.1230 seconds'
+```
+
 #### Smaller units
 
 If seconds are too large, set `minimum_unit` to milliseconds or microseconds:
diff --git a/src/humanize/__init__.py b/src/humanize/__init__.py
index d1055b6..34f0bb5 100644
--- a/src/humanize/__init__.py
+++ b/src/humanize/__init__.py
@@ -2,7 +2,13 @@ import pkg_resources
 from humanize.filesize import naturalsize
 from humanize.i18n import activate, deactivate
 from humanize.number import apnumber, fractional, intcomma, intword, ordinal, scientific
-from humanize.time import naturaldate, naturalday, naturaldelta, naturaltime
+from humanize.time import (
+    naturaldate,
+    naturalday,
+    naturaldelta,
+    naturaltime,
+    precisedelta,
+)
 
 __version__ = VERSION = pkg_resources.get_distribution(__name__).version
 
@@ -21,6 +27,7 @@ __all__ = [
     "naturalsize",
     "naturaltime",
     "ordinal",
+    "precisedelta",
     "scientific",
     "VERSION",
 ]
diff --git a/src/humanize/time.py b/src/humanize/time.py
index 716648a..bf33e79 100644
--- a/src/humanize/time.py
+++ b/src/humanize/time.py
@@ -4,18 +4,37 @@
 ``contrib.humanize``."""
 
 import datetime as dt
+import math
 from enum import Enum
+from functools import total_ordering
 
 from .i18n import gettext as _
 from .i18n import ngettext
 
-__all__ = ["naturaldelta", "naturaltime", "naturalday", "naturaldate"]
+__all__ = [
+    "naturaldelta",
+    "naturaltime",
+    "naturalday",
+    "naturaldate",
+    "precisedelta",
+]
 
 
+@total_ordering
 class Unit(Enum):
-    MILLISECONDS = 0
-    MICROSECONDS = 1
+    MICROSECONDS = 0
+    MILLISECONDS = 1
     SECONDS = 2
+    MINUTES = 3
+    HOURS = 4
+    DAYS = 5
+    MONTHS = 6
+    YEARS = 7
+
+    def __lt__(self, other):
+        if self.__class__ is other.__class__:
+            return self.value < other.value
+        return NotImplemented
 
 
 def _now():
@@ -67,7 +86,11 @@ def naturaldelta(value, months=True, minimum_unit="seconds"):
     Returns:
         str: A natural representation of the amount of time elapsed.
     """
-    minimum_unit = Unit[minimum_unit.upper()]
+    tmp = Unit[minimum_unit.upper()]
+    if tmp not in (Unit.SECONDS, Unit.MILLISECONDS, Unit.MICROSECONDS):
+        raise ValueError("Minimum unit '%s' not supported" % minimum_unit)
+    minimum_unit = tmp
+
     date, delta = date_and_delta(value)
     if date is None:
         return value
@@ -210,3 +233,255 @@ def naturaldate(value):
     if delta.days >= 5 * 365 / 12:
         return naturalday(value, "%b %d %Y")
     return naturalday(value)
+
+
+def _quotient_and_remainder(value, divisor, unit, minimum_unit, suppress):
+    """Divide ``value`` by ``divisor`` returning the quotient and
+       the remainder as follows:
+
+       If ``unit`` is ``minimum_unit``, makes the quotient a float number
+       and the remainder will be zero. The rational is that if unit
+       is the unit of the quotient, we cannot
+       represent the remainder because it would require a unit smaller
+       than the minimum_unit.
+
+       >>> from humanize.time import _quotient_and_remainder, Unit
+       >>> _quotient_and_remainder(36, 24, Unit.DAYS, Unit.DAYS, [])
+       (1.5, 0)
+
+       If unit is in suppress, the quotient will be zero and the
+       remainder will be the initial value. The idea is that if we
+       cannot use unit, we are forced to use a lower unit so we cannot
+       do the division.
+
+       >>> _quotient_and_remainder(36, 24, Unit.DAYS, Unit.HOURS, [Unit.DAYS])
+       (0, 36)
+
+       In other case return quotient and remainder as ``divmod`` would
+       do it.
+
+       >>> _quotient_and_remainder(36, 24, Unit.DAYS, Unit.HOURS, [])
+       (1, 12)
+
+    """
+
+    if unit == minimum_unit:
+        return (value / divisor, 0)
+    elif unit in suppress:
+        return (0, value)
+    else:
+        return divmod(value, divisor)
+
+
+def _carry(value1, value2, ratio, unit, min_unit, suppress):
+    """Return a tuple with two values as follows:
+
+       If the unit is in suppress multiplies value1
+       by ratio and add it to value2 (carry to right).
+       The idea is that if we cannot represent value1 we need
+       to represent it in a lower unit.
+
+       >>> from humanize.time import _carry, Unit
+       >>> _carry(2, 6, 24, Unit.DAYS, Unit.SECONDS, [Unit.DAYS])
+       (0, 54)
+
+       If the unit is the minimum unit, value2 is divided
+       by ratio and added to value1 (carry to left).
+       We assume that value2 has a lower unit so we need to
+       carry it to value1.
+
+        >>> _carry(2, 6, 24, Unit.DAYS, Unit.DAYS, [])
+        (2.25, 0)
+
+       Otherwise, just return the same input:
+
+       >>> _carry(2, 6, 24, Unit.DAYS, Unit.SECONDS, [])
+       (2, 6)
+    """
+    if unit == min_unit:
+        return (value1 + value2 / ratio, 0)
+    elif unit in suppress:
+        return (0, value2 + value1 * ratio)
+    else:
+        return (value1, value2)
+
+
+def _suitable_minimum_unit(min_unit, suppress):
+    """Return a minimum unit suitable that is not suppressed.
+
+       If not suppressed, return the same unit:
+
+       >>> from humanize.time import _suitable_minimum_unit, Unit
+       >>> _suitable_minimum_unit(Unit.HOURS, [])
+       <Unit.HOURS: 4>
+
+       But if suppressed, find a unit greather than the original one
+       that is not suppressed:
+
+       >>> _suitable_minimum_unit(Unit.HOURS, [Unit.HOURS])
+       <Unit.DAYS: 5>
+
+       >>> _suitable_minimum_unit(Unit.HOURS, [Unit.HOURS, Unit.DAYS])
+       <Unit.MONTHS: 6>
+    """
+    if min_unit in suppress:
+        for unit in Unit:
+            if unit > min_unit and unit not in suppress:
+                return unit
+
+        raise ValueError(
+            "Minimum unit is suppressed and no suitable replacement was found"
+        )
+
+    return min_unit
+
+
+def _suppress_lower_units(min_unit, suppress):
+    """Extend the suppressed units (if any) with all the units that are
+       lower than the minimum unit.
+
+       >>> from humanize.time import _suppress_lower_units, Unit
+       >>> list(sorted(_suppress_lower_units(Unit.SECONDS, [Unit.DAYS])))
+       [<Unit.MICROSECONDS: 0>, <Unit.MILLISECONDS: 1>, <Unit.DAYS: 5>]
+    """
+    suppress = set(suppress)
+    for u in Unit:
+        if u == min_unit:
+            break
+        suppress.add(u)
+
+    return suppress
+
+
+def precisedelta(value, minimum_unit="seconds", suppress=(), format="%0.2f"):
+    """Return a precise representation of a timedelta.
+
+       >>> import datetime as dt
+       >>> from humanize.time import precisedelta
+
+       >>> delta = dt.timedelta(seconds=3633, days=2, microseconds=123000)
+       >>> precisedelta(delta)
+       '2 days, 1 hour and 33.12 seconds'
+
+       A custom format can be specified to control how the fractional part
+       is represented:
+
+       >>> precisedelta(delta, format="%0.4f")
+       '2 days, 1 hour and 33.1230 seconds'
+
+       Instead, the minimum unit can be changed to have a better resolution;
+       the function still will readjust the unit to use the greatest of the
+       units that does not lose precision.
+
+       For example setting microseconds but still representing the date
+       with milliseconds:
+
+       >>> precisedelta(delta, minimum_unit="microseconds")
+       '2 days, 1 hour, 33 seconds and 123 milliseconds'
+
+       If desired, some units can be suppressed: you will not see them
+       represented and the time of the other units will be adjusted
+       to keep representing the same timedelta:
+
+       >>> precisedelta(delta, suppress=['days'])
+       '49 hours and 33.12 seconds'
+
+       Note that microseconds precision is lost if the seconds and all
+       the units below are suppressed:
+
+       >>> delta = dt.timedelta(seconds=90, microseconds=100)
+       >>> precisedelta(delta, suppress=['seconds', 'milliseconds', 'microseconds'])
+       '1.50 minutes'
+
+    """
+
+    date, delta = date_and_delta(value)
+    if date is None:
+        return value
+
+    suppress = [Unit[s.upper()] for s in suppress]
+
+    # Find a suitable minimum unit (it can be greater the one that the
+    # user gave us if it is suppressed).
+    min_unit = Unit[minimum_unit.upper()]
+    min_unit = _suitable_minimum_unit(min_unit, suppress)
+    del minimum_unit
+
+    # Expand the suppressed units list/set to include all the units
+    # that are below the minimum unit
+    suppress = _suppress_lower_units(min_unit, suppress)
+
+    # handy aliases
+    days = delta.days
+    secs = delta.seconds
+    usecs = delta.microseconds
+
+    MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS, MONTHS, YEARS = list(
+        Unit
+    )
+
+    # Given DAYS compute YEARS and the remainder of DAYS as follows:
+    #   if YEARS is the minimum unit, we cannot use DAYS so
+    #   we will use a float for YEARS and 0 for DAYS:
+    #       years, days = years/days, 0
+    #
+    #   if YEARS is suppressed, use DAYS:
+    #       years, days = 0, days
+    #
+    #   otherwise:
+    #       years, days = divmod(years, days)
+    #
+    # The same applies for months, hours, minutes and milliseconds below
+    years, days = _quotient_and_remainder(days, 365, YEARS, min_unit, suppress)
+    months, days = _quotient_and_remainder(days, 30.5, MONTHS, min_unit, suppress)
+
+    # If DAYS is not in suppress, we can represent the days but
+    # if it is a suppressed unit, we need to carry it to a lower unit,
+    # seconds in this case.
+    #
+    # The same applies for secs and usecs below
+    days, secs = _carry(days, secs, 24 * 3600, DAYS, min_unit, suppress)
+
+    hours, secs = _quotient_and_remainder(secs, 3600, HOURS, min_unit, suppress)
+    minutes, secs = _quotient_and_remainder(secs, 60, MINUTES, min_unit, suppress)
+
+    secs, usecs = _carry(secs, usecs, 1e6, SECONDS, min_unit, suppress)
+
+    msecs, usecs = _quotient_and_remainder(
+        usecs, 1000, MILLISECONDS, min_unit, suppress
+    )
+
+    # if _unused != 0 we had lost some precision
+    usecs, _unused = _carry(usecs, 0, 1, MICROSECONDS, min_unit, suppress)
+
+    fmts = [
+        ("%d year", "%d years", years),
+        ("%d month", "%d months", months),
+        ("%d day", "%d days", days),
+        ("%d hour", "%d hours", hours),
+        ("%d minute", "%d minutes", minutes),
+        ("%d second", "%d seconds", secs),
+        ("%d millisecond", "%d milliseconds", msecs),
+        ("%d microsecond", "%d microseconds", usecs),
+    ]
+
+    texts = []
+    for unit, fmt in zip(reversed(Unit), fmts):
+        singular_txt, plural_txt, value = fmt
+        if value > 0:
+            fmt_txt = ngettext(singular_txt, plural_txt, value)
+            if unit == min_unit and math.modf(value)[0] > 0:
+                fmt_txt = fmt_txt.replace("%d", format)
+
+            texts.append(fmt_txt % value)
+
+        if unit == min_unit:
+            break
+
+    if len(texts) == 1:
+        return texts[0]
+
+    head = ", ".join(texts[:-1])
+    tail = texts[-1]
+
+    return " and ".join((head, tail))

function to print timedeltas exactly
Hi,
I find myself using a function regularly that prints a time delta as "4 hours, 3 minutes, and 15.3 seconds". Is this a function you'd be interested in adding to your library?

I'd rather add it to a common library rather than copy and pasting it the whole time.

If you want it I'll open a PR with the requisite code.

Cheers,

Chris


diff --git a/tests/test_time.py b/tests/test_time.py
index af094af..565f6c1 100644
--- a/tests/test_time.py
+++ b/tests/test_time.py
@@ -376,3 +376,222 @@ def test_naturaltime_minimum_unit_explicit(minimum_unit, seconds, expected):
 
     # Act / Assert
     assert humanize.naturaltime(delta, minimum_unit=minimum_unit) == expected
+
+
+@pytest.mark.parametrize(
+    "val, min_unit, expected",
+    [
+        (dt.timedelta(microseconds=1), "microseconds", "1 microsecond"),
+        (dt.timedelta(microseconds=2), "microseconds", "2 microseconds"),
+        (dt.timedelta(microseconds=1000), "microseconds", "1 millisecond"),
+        (dt.timedelta(microseconds=2000), "microseconds", "2 milliseconds"),
+        (dt.timedelta(seconds=1), "seconds", "1 second"),
+        (1, "seconds", "1 second"),
+        (2, "seconds", "2 seconds"),
+        (60, "seconds", "1 minute"),
+        (120, "seconds", "2 minutes"),
+        (3600, "seconds", "1 hour"),
+        (3600 * 2, "seconds", "2 hours"),
+        (3600 * 24, "seconds", "1 day"),
+        (3600 * 24 * 2, "seconds", "2 days"),
+        (3600 * 24 * 365, "seconds", "1 year"),
+        (3600 * 24 * 365 * 2, "seconds", "2 years"),
+    ],
+)
+def test_precisedelta_one_unit_enough(val, min_unit, expected):
+    assert humanize.precisedelta(val, minimum_unit=min_unit) == expected
+
+
+@pytest.mark.parametrize(
+    "val, min_unit, expected",
+    [
+        (
+            dt.timedelta(microseconds=1001),
+            "microseconds",
+            "1 millisecond and 1 microsecond",
+        ),
+        (
+            dt.timedelta(microseconds=2002),
+            "microseconds",
+            "2 milliseconds and 2 microseconds",
+        ),
+        (
+            dt.timedelta(seconds=1, microseconds=2),
+            "microseconds",
+            "1 second and 2 microseconds",
+        ),
+        (
+            dt.timedelta(hours=4, seconds=3, microseconds=2),
+            "microseconds",
+            "4 hours, 3 seconds and 2 microseconds",
+        ),
+        (
+            dt.timedelta(days=5, hours=4, seconds=3, microseconds=2),
+            "microseconds",
+            "5 days, 4 hours, 3 seconds and 2 microseconds",
+        ),
+        (
+            dt.timedelta(days=370, hours=4, seconds=3, microseconds=2),
+            "microseconds",
+            "1 year, 5 days, 4 hours, 3 seconds and 2 microseconds",
+        ),
+        (
+            dt.timedelta(days=370, microseconds=2),
+            "microseconds",
+            "1 year, 5 days and 2 microseconds",
+        ),
+        (
+            dt.timedelta(days=370, seconds=2),
+            "microseconds",
+            "1 year, 5 days and 2 seconds",
+        ),
+    ],
+)
+def test_precisedelta_multiple_units(val, min_unit, expected):
+    assert humanize.precisedelta(val, minimum_unit=min_unit) == expected
+
+
+@pytest.mark.parametrize(
+    "val, min_unit, fmt, expected",
+    [
+        (
+            dt.timedelta(microseconds=1001),
+            "milliseconds",
+            "%0.4f",
+            "1.0010 milliseconds",
+        ),
+        (
+            dt.timedelta(microseconds=2002),
+            "milliseconds",
+            "%0.4f",
+            "2.0020 milliseconds",
+        ),
+        (dt.timedelta(microseconds=2002), "milliseconds", "%0.2f", "2.00 milliseconds"),
+        (
+            dt.timedelta(seconds=1, microseconds=230000),
+            "seconds",
+            "%0.2f",
+            "1.23 seconds",
+        ),
+        (
+            dt.timedelta(hours=4, seconds=3, microseconds=200000),
+            "seconds",
+            "%0.2f",
+            "4 hours and 3.20 seconds",
+        ),
+        (
+            dt.timedelta(days=5, hours=4, seconds=30 * 60),
+            "seconds",
+            "%0.2f",
+            "5 days, 4 hours and 30 minutes",
+        ),
+        (
+            dt.timedelta(days=5, hours=4, seconds=30 * 60),
+            "hours",
+            "%0.2f",
+            "5 days and 4.50 hours",
+        ),
+        (dt.timedelta(days=5, hours=4, seconds=30 * 60), "days", "%0.2f", "5.19 days"),
+        (dt.timedelta(days=120), "months", "%0.2f", "3.93 months"),
+        (dt.timedelta(days=183), "years", "%0.1f", "0.5 years"),
+    ],
+)
+def test_precisedelta_custom_format(val, min_unit, fmt, expected):
+    assert humanize.precisedelta(val, minimum_unit=min_unit, format=fmt) == expected
+
+
+@pytest.mark.parametrize(
+    "val, min_unit, suppress, expected",
+    [
+        (
+            dt.timedelta(microseconds=1200),
+            "microseconds",
+            [],
+            "1 millisecond and 200 microseconds",
+        ),
+        (
+            dt.timedelta(microseconds=1200),
+            "microseconds",
+            ["milliseconds"],
+            "1200 microseconds",
+        ),
+        (
+            dt.timedelta(microseconds=1200),
+            "microseconds",
+            ["microseconds"],
+            "1.20 milliseconds",
+        ),
+        (
+            dt.timedelta(seconds=1, microseconds=200),
+            "microseconds",
+            ["seconds"],
+            "1000 milliseconds and 200 microseconds",
+        ),
+        (
+            dt.timedelta(seconds=1, microseconds=200000),
+            "microseconds",
+            ["milliseconds"],
+            "1 second and 200000 microseconds",
+        ),
+        (
+            dt.timedelta(seconds=1, microseconds=200000),
+            "microseconds",
+            ["milliseconds", "microseconds"],
+            "1.20 seconds",
+        ),
+        (
+            dt.timedelta(hours=4, seconds=30, microseconds=200),
+            "microseconds",
+            ["microseconds"],
+            "4 hours, 30 seconds and 0.20 milliseconds",
+        ),
+        (
+            dt.timedelta(hours=4, seconds=30, microseconds=200),
+            "microseconds",
+            ["seconds"],
+            "4 hours, 30000 milliseconds and 200 microseconds",
+        ),
+        (
+            dt.timedelta(hours=4, seconds=30, microseconds=200),
+            "microseconds",
+            ["seconds", "milliseconds"],
+            "4 hours and 30000200 microseconds",
+        ),
+        (
+            dt.timedelta(hours=4, seconds=30, microseconds=200),
+            "microseconds",
+            ["hours"],
+            "240 minutes, 30 seconds and 200 microseconds",
+        ),
+        (
+            dt.timedelta(hours=4, seconds=30, microseconds=200),
+            "microseconds",
+            ["hours", "seconds", "milliseconds", "microseconds"],
+            "240.50 minutes",
+        ),
+    ],
+)
+def test_precisedelta_suppress_units(val, min_unit, suppress, expected):
+    assert (
+        humanize.precisedelta(val, minimum_unit=min_unit, suppress=suppress) == expected
+    )
+
+
+def test_precisedelta_bogus_call():
+    assert humanize.precisedelta(None) is None
+
+    with pytest.raises(ValueError):
+        humanize.precisedelta(1, minimum_unit="years", suppress=["years"])
+
+    with pytest.raises(ValueError):
+        humanize.naturaldelta(1, minimum_unit="years")
+
+
+def test_time_unit():
+    years, minutes = time.Unit["YEARS"], time.Unit["MINUTES"]
+    assert minutes < years
+    assert years > minutes
+    assert minutes == minutes
+
+    with pytest.raises(TypeError):
+        years < "foo"

pip install -e .[tests]
parse_log_pytest
requirements.txt
pytest pytest-cov
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coverage==7.8.0
exceptiongroup==1.2.2
freezegun==1.5.1
-e git+https://github.com/jmoiron/humanize.git@976ad4a342c50958fdc5668f7e84cd271a7ac7cd#egg=humanize
iniconfig==2.1.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
pytest-cov==6.0.0
python-dateutil==2.9.0.post0
six==1.17.0
tomli==2.2.1

name: humanize
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - freezegun==1.5.1
      - humanize==2.4.1
      - iniconfig==2.1.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - python-dateutil==2.9.0.post0
      - six==1.17.0
      - tomli==2.2.1
prefix: /opt/conda/envs/humanize

tests/test_time.py::test_precisedelta_one_unit_enough[val0-microseconds-1 tests/test_time.py::test_precisedelta_one_unit_enough[val1-microseconds-2 tests/test_time.py::test_precisedelta_one_unit_enough[val2-microseconds-1 tests/test_time.py::test_precisedelta_one_unit_enough[val3-microseconds-2 tests/test_time.py::test_precisedelta_one_unit_enough[val4-seconds-1 tests/test_time.py::test_precisedelta_one_unit_enough[1-seconds-1 tests/test_time.py::test_precisedelta_one_unit_enough[2-seconds-2 tests/test_time.py::test_precisedelta_one_unit_enough[60-seconds-1 tests/test_time.py::test_precisedelta_one_unit_enough[120-seconds-2 tests/test_time.py::test_precisedelta_one_unit_enough[3600-seconds-1 tests/test_time.py::test_precisedelta_one_unit_enough[7200-seconds-2 tests/test_time.py::test_precisedelta_one_unit_enough[86400-seconds-1 tests/test_time.py::test_precisedelta_one_unit_enough[172800-seconds-2 tests/test_time.py::test_precisedelta_one_unit_enough[31536000-seconds-1 tests/test_time.py::test_precisedelta_one_unit_enough[63072000-seconds-2 tests/test_time.py::test_precisedelta_multiple_units[val0-microseconds-1 tests/test_time.py::test_precisedelta_multiple_units[val1-microseconds-2 tests/test_time.py::test_precisedelta_multiple_units[val2-microseconds-1 tests/test_time.py::test_precisedelta_multiple_units[val3-microseconds-4 tests/test_time.py::test_precisedelta_multiple_units[val4-microseconds-5 tests/test_time.py::test_precisedelta_multiple_units[val5-microseconds-1 tests/test_time.py::test_precisedelta_multiple_units[val6-microseconds-1 tests/test_time.py::test_precisedelta_multiple_units[val7-microseconds-1 tests/test_time.py::test_precisedelta_custom_format[val0-milliseconds-%0.4f-1.0010 tests/test_time.py::test_precisedelta_custom_format[val1-milliseconds-%0.4f-2.0020 tests/test_time.py::test_precisedelta_custom_format[val2-milliseconds-%0.2f-2.00 tests/test_time.py::test_precisedelta_custom_format[val3-seconds-%0.2f-1.23 tests/test_time.py::test_precisedelta_custom_format[val4-seconds-%0.2f-4 tests/test_time.py::test_precisedelta_custom_format[val5-seconds-%0.2f-5 tests/test_time.py::test_precisedelta_custom_format[val6-hours-%0.2f-5 tests/test_time.py::test_precisedelta_custom_format[val7-days-%0.2f-5.19 tests/test_time.py::test_precisedelta_custom_format[val8-months-%0.2f-3.93 tests/test_time.py::test_precisedelta_custom_format[val9-years-%0.1f-0.5 tests/test_time.py::test_precisedelta_suppress_units[val0-microseconds-suppress0-1 tests/test_time.py::test_precisedelta_suppress_units[val1-microseconds-suppress1-1200 tests/test_time.py::test_precisedelta_suppress_units[val2-microseconds-suppress2-1.20 tests/test_time.py::test_precisedelta_suppress_units[val3-microseconds-suppress3-1000 tests/test_time.py::test_precisedelta_suppress_units[val4-microseconds-suppress4-1 tests/test_time.py::test_precisedelta_suppress_units[val5-microseconds-suppress5-1.20 tests/test_time.py::test_precisedelta_suppress_units[val6-microseconds-suppress6-4 tests/test_time.py::test_precisedelta_suppress_units[val7-microseconds-suppress7-4 tests/test_time.py::test_precisedelta_suppress_units[val8-microseconds-suppress8-4 tests/test_time.py::test_precisedelta_suppress_units[val9-microseconds-suppress9-240 tests/test_time.py::test_precisedelta_suppress_units[val10-microseconds-suppress10-240.50 tests/test_time.py::test_precisedelta_bogus_call tests/test_time.py::test_time_unit
tests/test_time.py::test_date_and_delta tests/test_time.py::test_naturaldelta_nomonths[test_input0-7 tests/test_time.py::test_naturaldelta_nomonths[test_input1-31 tests/test_time.py::test_naturaldelta_nomonths[test_input2-230 tests/test_time.py::test_naturaldelta_nomonths[test_input3-1 tests/test_time.py::test_naturaldelta[0-a tests/test_time.py::test_naturaldelta[1-a tests/test_time.py::test_naturaldelta[30-30 tests/test_time.py::test_naturaldelta[test_input3-a tests/test_time.py::test_naturaldelta[test_input4-2 tests/test_time.py::test_naturaldelta[test_input5-an tests/test_time.py::test_naturaldelta[test_input6-23 tests/test_time.py::test_naturaldelta[test_input7-a tests/test_time.py::test_naturaldelta[test_input8-1 tests/test_time.py::test_naturaldelta[test_input9-2 tests/test_time.py::test_naturaldelta[test_input10-a tests/test_time.py::test_naturaldelta[test_input11-30 tests/test_time.py::test_naturaldelta[test_input12-a tests/test_time.py::test_naturaldelta[test_input13-2 tests/test_time.py::test_naturaldelta[test_input14-an tests/test_time.py::test_naturaldelta[test_input15-23 tests/test_time.py::test_naturaldelta[test_input16-a tests/test_time.py::test_naturaldelta[test_input17-1 tests/test_time.py::test_naturaldelta[test_input18-2 tests/test_time.py::test_naturaldelta[test_input19-27 tests/test_time.py::test_naturaldelta[test_input20-1 tests/test_time.py::test_naturaldelta[test_input22-2 tests/test_time.py::test_naturaldelta[test_input23-1 tests/test_time.py::test_naturaldelta[test_input24-a tests/test_time.py::test_naturaldelta[test_input25-2 tests/test_time.py::test_naturaldelta[test_input26-9 tests/test_time.py::test_naturaldelta[test_input27-a tests/test_time.py::test_naturaldelta[NaN-NaN] tests/test_time.py::test_naturaltime[test_input0-now] tests/test_time.py::test_naturaltime[test_input1-a tests/test_time.py::test_naturaltime[test_input2-30 tests/test_time.py::test_naturaltime[test_input3-a tests/test_time.py::test_naturaltime[test_input4-2 tests/test_time.py::test_naturaltime[test_input5-an tests/test_time.py::test_naturaltime[test_input6-23 tests/test_time.py::test_naturaltime[test_input7-a tests/test_time.py::test_naturaltime[test_input8-1 tests/test_time.py::test_naturaltime[test_input9-2 tests/test_time.py::test_naturaltime[test_input10-a tests/test_time.py::test_naturaltime[test_input11-30 tests/test_time.py::test_naturaltime[test_input12-a tests/test_time.py::test_naturaltime[test_input13-2 tests/test_time.py::test_naturaltime[test_input14-an tests/test_time.py::test_naturaltime[test_input15-23 tests/test_time.py::test_naturaltime[test_input16-a tests/test_time.py::test_naturaltime[test_input17-1 tests/test_time.py::test_naturaltime[test_input18-2 tests/test_time.py::test_naturaltime[test_input19-27 tests/test_time.py::test_naturaltime[test_input20-1 tests/test_time.py::test_naturaltime[30-30 tests/test_time.py::test_naturaltime[test_input22-2 tests/test_time.py::test_naturaltime[test_input23-1 tests/test_time.py::test_naturaltime[NaN-NaN] tests/test_time.py::test_naturaltime_nomonths[test_input0-now] tests/test_time.py::test_naturaltime_nomonths[test_input1-a tests/test_time.py::test_naturaltime_nomonths[test_input2-30 tests/test_time.py::test_naturaltime_nomonths[test_input3-a tests/test_time.py::test_naturaltime_nomonths[test_input4-2 tests/test_time.py::test_naturaltime_nomonths[test_input5-an tests/test_time.py::test_naturaltime_nomonths[test_input6-23 tests/test_time.py::test_naturaltime_nomonths[test_input7-a tests/test_time.py::test_naturaltime_nomonths[test_input8-17 tests/test_time.py::test_naturaltime_nomonths[test_input9-47 tests/test_time.py::test_naturaltime_nomonths[test_input10-1 tests/test_time.py::test_naturaltime_nomonths[test_input11-2 tests/test_time.py::test_naturaltime_nomonths[test_input12-a tests/test_time.py::test_naturaltime_nomonths[test_input13-30 tests/test_time.py::test_naturaltime_nomonths[test_input14-a tests/test_time.py::test_naturaltime_nomonths[test_input15-2 tests/test_time.py::test_naturaltime_nomonths[test_input16-an tests/test_time.py::test_naturaltime_nomonths[test_input17-23 tests/test_time.py::test_naturaltime_nomonths[test_input18-a tests/test_time.py::test_naturaltime_nomonths[test_input19-1 tests/test_time.py::test_naturaltime_nomonths[test_input20-2 tests/test_time.py::test_naturaltime_nomonths[test_input21-27 tests/test_time.py::test_naturaltime_nomonths[test_input22-1 tests/test_time.py::test_naturaltime_nomonths[30-30 tests/test_time.py::test_naturaltime_nomonths[test_input24-2 tests/test_time.py::test_naturaltime_nomonths[test_input25-1 tests/test_time.py::test_naturaltime_nomonths[NaN-NaN] tests/test_time.py::test_naturalday[test_args0-today] tests/test_time.py::test_naturalday[test_args1-tomorrow] tests/test_time.py::test_naturalday[test_args2-yesterday] tests/test_time.py::test_naturalday[test_args3-Mar tests/test_time.py::test_naturalday[test_args4-02/26/1984] tests/test_time.py::test_naturalday[test_args5-1982.06.27] tests/test_time.py::test_naturalday[test_args6-None] tests/test_time.py::test_naturalday[test_args7-Not tests/test_time.py::test_naturalday[test_args8-expected8] tests/test_time.py::test_naturalday[test_args9-expected9] tests/test_time.py::test_naturaldate[test_input0-today] tests/test_time.py::test_naturaldate[test_input1-tomorrow] tests/test_time.py::test_naturaldate[test_input2-yesterday] tests/test_time.py::test_naturaldate[test_input3-Mar tests/test_time.py::test_naturaldate[test_input4-Jun tests/test_time.py::test_naturaldate[None-None] tests/test_time.py::test_naturaldate[Not tests/test_time.py::test_naturaldate[test_input7-expected7] tests/test_time.py::test_naturaldate[test_input8-expected8] tests/test_time.py::test_naturaldate[test_input9-Feb tests/test_time.py::test_naturaldate[test_input10-Mar tests/test_time.py::test_naturaldate[test_input11-Apr tests/test_time.py::test_naturaldate[test_input12-May tests/test_time.py::test_naturaldate[test_input13-Jun tests/test_time.py::test_naturaldate[test_input14-Jul tests/test_time.py::test_naturaldate[test_input15-Aug tests/test_time.py::test_naturaldate[test_input16-Sep tests/test_time.py::test_naturaldate[test_input17-Oct tests/test_time.py::test_naturaldate[test_input18-Nov tests/test_time.py::test_naturaldate[test_input19-Dec tests/test_time.py::test_naturaldate[test_input20-Jan tests/test_time.py::test_naturaldate[test_input21-today] tests/test_time.py::test_naturaldate[test_input22-Mar tests/test_time.py::test_naturaldate[test_input23-Apr tests/test_time.py::test_naturaldate[test_input24-May tests/test_time.py::test_naturaldate[test_input25-Jun tests/test_time.py::test_naturaldate[test_input26-Jul tests/test_time.py::test_naturaldate[test_input27-Aug tests/test_time.py::test_naturaldate[test_input28-Sep tests/test_time.py::test_naturaldate[test_input29-Oct tests/test_time.py::test_naturaldate[test_input30-Nov tests/test_time.py::test_naturaldate[test_input31-Dec tests/test_time.py::test_naturaldate[test_input32-Jan tests/test_time.py::test_naturaldate[test_input33-Feb tests/test_time.py::test_naturaldelta_minimum_unit_default[1e-06-a tests/test_time.py::test_naturaldelta_minimum_unit_default[4e-06-a tests/test_time.py::test_naturaldelta_minimum_unit_default[0.001-a tests/test_time.py::test_naturaldelta_minimum_unit_default[0.004-a tests/test_time.py::test_naturaldelta_minimum_unit_default[2-2 tests/test_time.py::test_naturaldelta_minimum_unit_default[4-4 tests/test_time.py::test_naturaldelta_minimum_unit_default[3600.004-an tests/test_time.py::test_naturaldelta_minimum_unit_default[86400.004-a tests/test_time.py::test_naturaldelta_minimum_unit_default[31557600.000004-a tests/test_time.py::test_naturaldelta_minimum_unit_explicit[seconds-1e-06-a tests/test_time.py::test_naturaldelta_minimum_unit_explicit[seconds-4e-06-a tests/test_time.py::test_naturaldelta_minimum_unit_explicit[seconds-0.001-a tests/test_time.py::test_naturaldelta_minimum_unit_explicit[seconds-0.004-a tests/test_time.py::test_naturaldelta_minimum_unit_explicit[seconds-2-2 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[seconds-4-4 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[seconds-3600.004-an tests/test_time.py::test_naturaldelta_minimum_unit_explicit[seconds-86400.004-a tests/test_time.py::test_naturaldelta_minimum_unit_explicit[seconds-31557600.000004-a tests/test_time.py::test_naturaldelta_minimum_unit_explicit[microseconds-1e-06-1 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[microseconds-4e-06-4 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[microseconds-2-2 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[microseconds-4-4 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[microseconds-3600.004-an tests/test_time.py::test_naturaldelta_minimum_unit_explicit[microseconds-86400.004-a tests/test_time.py::test_naturaldelta_minimum_unit_explicit[microseconds-31557600.000004-a tests/test_time.py::test_naturaldelta_minimum_unit_explicit[milliseconds-0.001-1 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[milliseconds-0.004-4 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[milliseconds-2-2 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[milliseconds-4-4 tests/test_time.py::test_naturaldelta_minimum_unit_explicit[milliseconds-3600.004-an tests/test_time.py::test_naturaldelta_minimum_unit_explicit[milliseconds-31557600.000004-a tests/test_time.py::test_naturaltime_minimum_unit_default[1e-06-now] tests/test_time.py::test_naturaltime_minimum_unit_default[4e-06-now] tests/test_time.py::test_naturaltime_minimum_unit_default[0.001-now] tests/test_time.py::test_naturaltime_minimum_unit_default[0.004-now] tests/test_time.py::test_naturaltime_minimum_unit_default[2-2 tests/test_time.py::test_naturaltime_minimum_unit_default[4-4 tests/test_time.py::test_naturaltime_minimum_unit_default[3600.004-an tests/test_time.py::test_naturaltime_minimum_unit_default[86400.004-a tests/test_time.py::test_naturaltime_minimum_unit_default[31557600.000004-a tests/test_time.py::test_naturaltime_minimum_unit_explicit[seconds-1e-06-now] tests/test_time.py::test_naturaltime_minimum_unit_explicit[seconds-4e-06-now] tests/test_time.py::test_naturaltime_minimum_unit_explicit[seconds-0.001-now] tests/test_time.py::test_naturaltime_minimum_unit_explicit[seconds-0.004-now] tests/test_time.py::test_naturaltime_minimum_unit_explicit[seconds-2-2 tests/test_time.py::test_naturaltime_minimum_unit_explicit[seconds-4-4 tests/test_time.py::test_naturaltime_minimum_unit_explicit[seconds-3600.004-an tests/test_time.py::test_naturaltime_minimum_unit_explicit[seconds-86400.004-a tests/test_time.py::test_naturaltime_minimum_unit_explicit[seconds-31557600.000004-a tests/test_time.py::test_naturaltime_minimum_unit_explicit[microseconds-1e-06-1 tests/test_time.py::test_naturaltime_minimum_unit_explicit[microseconds-4e-06-4 tests/test_time.py::test_naturaltime_minimum_unit_explicit[microseconds-2-2 tests/test_time.py::test_naturaltime_minimum_unit_explicit[microseconds-4-4 tests/test_time.py::test_naturaltime_minimum_unit_explicit[microseconds-3600.004-an tests/test_time.py::test_naturaltime_minimum_unit_explicit[microseconds-86400.004-a tests/test_time.py::test_naturaltime_minimum_unit_explicit[microseconds-31557600.000004-a tests/test_time.py::test_naturaltime_minimum_unit_explicit[milliseconds-0.001-1 tests/test_time.py::test_naturaltime_minimum_unit_explicit[milliseconds-0.004-4 tests/test_time.py::test_naturaltime_minimum_unit_explicit[milliseconds-2-2 tests/test_time.py::test_naturaltime_minimum_unit_explicit[milliseconds-4-4 tests/test_time.py::test_naturaltime_minimum_unit_explicit[milliseconds-3600.004-an tests/test_time.py::test_naturaltime_minimum_unit_explicit[milliseconds-31557600.000004-a
src/humanize/__init__.py README.md src/humanize/time.py
src/humanize/__init__.py README.md src/humanize/time.py