mrkeyoor.com_
Sun 20 Sept 12:44 UTC
PyPIUtilsupdated 20 Sept 2026

humanize review

humanize 4.16.0 turns Python numbers and dates into display text such as `an hour ago`, `1.0 MiB`, `1.2 million`, `1002nd`, and multi-unit durations. The module exposes small formatting functions for relative time, calendar labels, file sizes, fractions, SI prefixes, number grouping, and short lists. Version 4.16.0 adds Latvian, translates `naturalsize()`, fixes rounding into the next size or metric unit, repairs negative mixed fractions, and handles timezone-aware values in `naturalday()` and `naturaldate()`. Our Python 3.12 package was pure Python, included `py.typed`, and imported in 0.09 seconds.

Verdict

humanize 4.16.0 installed in 0.3 seconds and used 1 MB as a single package, with typed pure-Python code and no audit findings in our sandbox. It fits English-first or synchronous display formatting; use Babel when per-request locale correctness matters.

We installed it

Lab card: what happened when we installed humanizeScreenshot of humanize documentation
Install✓ · 0.3s1 package on disk · 1 MB
Importimport humanize in 0.09s · pure Python · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does humanize install cleanly?

Yes. In a fresh container with an empty cache, pip install humanize finished in 0.3s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does humanize need to run?

Python >=3.10, and nothing compiled: it is pure Python. In our run import humanize succeeded in 0.09s, and the package ships py.typed for type checkers.

humanize or babel: which should you use?

babel: Choose it for CLDR-backed currencies, decimal formats, dates, plural rules, and broad locale coverage. humanize 4.16.0 installed in 0.3 seconds and used 1 MB as a single package, with typed pure-Python code and no audit findings in our sandbox.

When should you not use humanize?

Currencies, dates, plural rules, and numbers must follow CLDR data across many locales. Babel is built for that wider internationalization job.

API stability5/5The 4.x public interface remains a set of direct functions including `naturaltime`, `naturaldelta`, `precisedelta`, `naturalsize`, `intcomma`, and `intword`. Release 4.16.0 adds translation and repairs edge outputs without replacing those calls. Output text can still change on a patch or minor release when rounding and localization bugs are corrected, so applications that compare exact phrases should pin the version and keep representative snapshot tests.
Docs4/5The README demonstrates number, datetime, duration, size, scientific-notation, and localization helpers with literal return values. Read the Docs exposes parameters and the public API, while the localization section shows custom catalog paths and the exception for an unknown locale. It gives less operational guidance on shared state: the thread-local translator and process-timezone behavior require reading source, tests, or issue discussions before using the helpers in concurrent web requests.
Maintenance5/5The GitHub repository is unarchived, has 748 stars, was pushed on August 1, 2026, and currently shows 41 open issues and pull requests. Version 4.16.0 shipped on June 30 with a new translation, performance changes measured by its maintainers, Python-version work, and fixes across rounding, fractions, empty lists, aware dates, Arabic, and Spanish. The release history shows regular bounded changes rather than an abandoned utility.
Ecosystem4/5The supplied snapshot records 13,938,671 weekly PyPI downloads. humanize covers common presentation tasks without requiring a template system or replacement datetime type, and its packaged gettext catalogs include more than 30 named languages in the README. It is still a formatter rather than a full internationalization layer: there is no CLDR-wide currency and date model, and locale activation is shared within a thread.

Use it if

  • A CLI, notification, or server-rendered view needs readable ages, durations, sizes, ordinals, or large counts without a formatting framework.
  • File sizes must switch explicitly between decimal SI, binary IEC, and compact GNU conventions.
  • A report needs a duration split into chosen units through `precisedelta()` rather than one rounded unit.
  • A synchronous renderer can choose one of the gettext catalogs packaged with the distribution.
Skip it if

Setup reality

We installed humanize 4.16.0 in a fresh unprivileged Python 3.12 Bookworm sandbox. The install completed in 0.3 seconds and left one package occupying 1 MB. import humanize worked in 0.09 seconds. Our package check counted five direct dependencies, found pure Python code and py.typed, and confirmed a Python 3.10+ requirement. pip-audit reported zero known vulnerabilities. The package metadata did not identify a license.

The wheel contains its gettext catalogs, so no service or config file is required. humanize.i18n.activate("de_DE") selects a catalog; a missing catalog raises FileNotFoundError, and a custom one needs an explicit path. Activation changes ambient state. Save the previous choice or call deactivate() in finally so a failed render does not leave later output in the wrong language.

That locale state belongs to a thread, not an asyncio task. Two requests running as coroutines on the same loop thread can overwrite one another's translator. Do not wrap individual async responses with activate() and deactivate() unless access is serialized. Use Babel, preselected functions, or another request-local design when concurrent users can ask for different languages.

Time helpers also depend on context outside their arguments. Calls without when= read the current clock, and aware values may be compared through the process timezone. Normalize timestamps to the viewer's timezone first and pass a fixed reference in tests. Version 4.16.0 repairs aware values in naturalday() and naturaldate(), but labels such as today can still change at midnight according to the timezone your application chose.

Patterns

Describe past and future instants format-relative-time

import datetime as dt
import humanize

now = dt.datetime.now()
humanize.naturaltime(now - dt.timedelta(seconds=1))
humanize.naturaltime(now - dt.timedelta(hours=1))
humanize.naturaltime(now + dt.timedelta(days=2))

Datetime and timedelta inputs determine tense from their sign. A bare numeric value is treated as seconds and uses the `future` flag for direction.

Make a relative-time test repeatable fix-reference-clock

import datetime as dt
import humanize

created = dt.datetime(2026, 8, 1, 12, 0, tzinfo=dt.timezone.utc)
reference = dt.datetime(2026, 8, 1, 15, 0, tzinfo=dt.timezone.utc)
assert humanize.naturaltime(created, when=reference) == "3 hours ago"

Supplying `when` removes the live clock from the result. Normalize both values before comparison so the host timezone cannot shift the phrase.

Choose decimal, binary, or GNU sizes format-byte-count

import humanize

humanize.naturalsize(1_000_000)
humanize.naturalsize(1_000_000, binary=True)
humanize.naturalsize(1_000_000, gnu=True)
humanize.naturalsize(1_000_000, format="%.2f")

Default SI output uses powers of 1,000; binary output uses 1,024. Pick one convention for a product so the same byte count does not appear inconsistent.

Group, scale, and ordinalize integers format-large-count

import humanize

humanize.intcomma(1_234_567)
humanize.intword(1_200_000)
humanize.apnumber(4)
humanize.ordinal(1002)

`intword` keeps values below one thousand unscaled. Table columns may need a separate alignment rule for mixed scaled and unscaled results.

Reduce a duration to readable units round-duration

import datetime as dt
import humanize

humanize.naturaldelta(dt.timedelta(seconds=1001))
humanize.naturaldelta(dt.timedelta(milliseconds=4))
humanize.naturaldelta(
    dt.timedelta(milliseconds=4), minimum_unit="milliseconds"
)

Subsecond values become `a moment` unless `minimum_unit` permits a smaller unit. Month conversion is approximate and should not drive billing dates.

Retain several parts of a timedelta show-precise-duration

import datetime as dt
import humanize

delta = dt.timedelta(days=2, seconds=3633, microseconds=123_000)
humanize.precisedelta(delta)
humanize.precisedelta(delta, minimum_unit="microseconds")
humanize.precisedelta(delta, suppress=["days"], format="%0.4f")

Suppressing a unit rolls its value into another displayed unit. `minimum_unit` selects the smallest part and may leave a fractional remainder.

Name dates near today label-calendar-day

import datetime as dt
import humanize

today = dt.date.today()
humanize.naturalday(today)
humanize.naturalday(today - dt.timedelta(days=1))
humanize.naturaldate(dt.date(2007, 6, 5))

`naturalday` may omit the year for its fallback format, while `naturaldate` keeps it. Convert instants into the viewer's timezone before deriving a date.

Select and then clear a gettext catalog activate-translation

import datetime as dt
import humanize

humanize.activate("de_DE")
try:
    label = humanize.naturaltime(dt.timedelta(seconds=3))
finally:
    humanize.deactivate()

The selected translator is thread-local. `finally` prevents a formatter exception from leaking that language into later synchronous work on the same thread.

Render a short natural-language list join-short-list

import humanize

humanize.natural_list(["one"])
humanize.natural_list(["one", "two"])
humanize.natural_list(["one", "two", "three"])
humanize.natural_list([])

Items are converted with `str()`, and an empty input returns an empty string in 4.16.0. Map objects to deliberate labels before calling it.

Clamp a value and attach an SI prefix format-si-value

import humanize

humanize.clamp(0.0001, floor=0.01)
humanize.clamp(1.0, format="{:.0%}", ceil=0.99)
humanize.metric(1500, "V")
humanize.metric(0.0002, "F")

`clamp` uses `str.format` syntax, unlike helpers that take percent-style formats. `metric` may emit Unicode symbols such as micro, which matters in ASCII-only exports.

Expose selected helpers to Jinja register-jinja-filters

from jinja2 import Environment
import humanize

env = Environment()
env.filters["naturaltime"] = humanize.naturaltime
env.filters["naturalsize"] = humanize.naturalsize
env.filters["intcomma"] = humanize.intcomma

text = env.from_string("{{ n|intcomma }} bytes").render(n=1234567)

The module functions can be registered directly. Check nullable fields before rendering because permissive string conversion can turn `None` into visible text.

Alternatives

PackageRegistryPick it when
babelPyPIChoose it for CLDR-backed currencies, decimal formats, dates, plural rules, and broad locale coverage.
inflectPyPIChoose it for English articles, plurals, number words, and grammar-oriented text generation.
naturalsizePyPIChoose it when readable byte counts are the only feature required.
arrowPyPIChoose it when datetime parsing, timezone conversion, ranges, and humanized relative time should share one date library.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.