Humanizer (Humanizer v0.1.0)

Copy Markdown View Source

Human-friendly formatting helpers for Elixir.

A small, flat set of pure functions that turn raw values into the kind of strings you show to people:

Design

  • English only. No localization in v0.1. For real i18n use ex_cldr.
  • No global config. Every option is a keyword passed to the function.
  • One rounding rule. Decimal output uses round-half-away-from-zero with a single fractional digit by default (:precision to override).
  • No scientific notation, ever. Numbers are formatted from integers, so values up to 10 ** 15 never come out as "1.0e15".

Summary

Functions

Formats a byte count as a human-readable size.

Formats a duration given in seconds as words.

Joins a list of strings into a human-readable enumeration.

Formats a number with a magnitude suffix (K, M, B, T).

Returns the integer with its English ordinal suffix.

Formats a DateTime relative to now (or to a given reference time).

Functions

bytes(n, opts \\ [])

@spec bytes(
  non_neg_integer(),
  keyword()
) :: String.t()

Formats a byte count as a human-readable size.

Uses decimal (SI, base 1000) units by default. Pass system: :binary for IEC (base 1024) units. Precision defaults to one fractional digit.

Options

  • :system:decimal (default) or :binary
  • :precision — number of fractional digits (default 1)

Examples

iex> Humanizer.bytes(2_456_789)
"2.5 MB"

iex> Humanizer.bytes(2_456_789, system: :binary)
"2.3 MiB"

iex> Humanizer.bytes(2_456_789, precision: 2)
"2.46 MB"

iex> Humanizer.bytes(0)
"0 B"

duration(seconds, opts \\ [])

@spec duration(
  number(),
  keyword()
) :: String.t()

Formats a duration given in seconds as words.

Breaks the duration down into days, hours, minutes and seconds (months and years are out of scope in v0.1). By default the two most significant non-zero units are shown.

Options

  • :units — number of units to show, or :all (default 2)
  • :format:long (default, "1 hour") or :short ("1h")

Raises ArgumentError for negative input.

Examples

iex> Humanizer.duration(3725)
"1 hour, 2 minutes"

iex> Humanizer.duration(3725, units: 1)
"1 hour"

iex> Humanizer.duration(3725, units: :all)
"1 hour, 2 minutes, 5 seconds"

iex> Humanizer.duration(45, format: :short)
"45s"

list_join(items, opts \\ [])

@spec list_join(
  [String.t()],
  keyword()
) :: String.t()

Joins a list of strings into a human-readable enumeration.

Options

  • :conjunction — word before the last item (default "and")
  • :oxford — add a serial comma before the conjunction (default false). Has no effect on two-item lists.

Examples

iex> Humanizer.list_join(["Alice", "Bob", "Charlie"])
"Alice, Bob and Charlie"

iex> Humanizer.list_join(["Alice", "Bob"])
"Alice and Bob"

iex> Humanizer.list_join(["Alice", "Bob", "Charlie"], oxford: true)
"Alice, Bob, and Charlie"

iex> Humanizer.list_join(["Alice", "Bob", "Charlie"], conjunction: "or")
"Alice, Bob or Charlie"

number(n, opts \\ [])

@spec number(
  number(),
  keyword()
) :: String.t()

Formats a number with a magnitude suffix (K, M, B, T).

Accepts integers and floats, positive or negative. Values below 1000 are rendered without a suffix or decimals. The ceiling in v0.1 is trillions (T).

Options

  • :precision — number of fractional digits for suffixed values (default 1)

Examples

iex> Humanizer.number(1_234)
"1.2K"

iex> Humanizer.number(1_234_567)
"1.2M"

iex> Humanizer.number(999)
"999"

iex> Humanizer.number(-1_234, precision: 2)
"-1.23K"

ordinal(n)

@spec ordinal(integer()) :: String.t()

Returns the integer with its English ordinal suffix.

Correctly handles the 11/12/13 exceptions and negative numbers.

Examples

iex> Humanizer.ordinal(1)
"1st"

iex> Humanizer.ordinal(11)
"11th"

iex> Humanizer.ordinal(23)
"23rd"

iex> Humanizer.ordinal(-1)
"-1st"

relative_time(datetime, now_or_opts \\ [])

@spec relative_time(
  DateTime.t(),
  keyword()
) :: String.t()

Formats a DateTime relative to now (or to a given reference time).

Past times read as "X ago", future times as "in X", and anything within a minute as "just now". Both arguments are compared as absolute instants, so time zones are handled correctly. The largest unit is days (v0.1).

Examples

iex> Humanizer.relative_time(~U[2026-05-13 10:00:00Z], ~U[2026-05-15 10:00:00Z])
"2 days ago"

iex> Humanizer.relative_time(~U[2026-05-15 13:00:00Z], ~U[2026-05-15 10:00:00Z])
"in 3 hours"

iex> Humanizer.relative_time(~U[2026-05-15 10:00:00Z], ~U[2026-05-15 10:00:00Z])
"just now"

relative_time(datetime, now, opts)

@spec relative_time(DateTime.t(), DateTime.t(), keyword()) :: String.t()