humanize

package module
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2026 License: MIT Imports: 3 Imported by: 0

README

Go Humanize

Small, strict Go helpers for turning machine values into human-readable English display strings.

go-humanize stays intentionally narrow: bytes, durations, relative time, numbers, ordinals, percentages, and count phrases. It has no runtime dependencies, no builders, no option structs, and no locale layer.

Humanized text is for display only. Never feed humanized text back into protocols, schedulers, billing, or audit. Use raw integers, time.Time, or time.Duration for those.

Installation

go get github.com/agentable/go-humanize

Requires Go 1.26.5.

Quick Taste

package main

import (
	"fmt"
	"time"

	"github.com/agentable/go-humanize"
)

func main() {
	ref := time.Date(2024, 2, 24, 12, 0, 0, 0, time.UTC)
	twoHoursAgo := ref.Add(-2 * time.Hour)

	fmt.Println(humanize.Bytes(1500))                  // "1.5 kB"
	fmt.Println(humanize.BinaryBytes(1536))            // "1.5 KiB"
	fmt.Println(humanize.Relative(twoHoursAgo, ref))   // "2 hours ago"
	fmt.Println(humanize.Number(1234567))              // "1,234,567"
	fmt.Println(humanize.Ordinal(3))                   // "3rd"
	fmt.Println(humanize.Percent(0.333))               // "33.3%"
	fmt.Println(humanize.Count(1000, "item", "items")) // "1,000 items"
}

API

Function Purpose Example
Bytes(int64) string Decimal byte units 1500"1.5 kB"
BinaryBytes(int64) string IEC binary byte units 1536"1.5 KiB"
Number(int64) string Comma-separated integers 1234567"1,234,567"
Percent(float64) string Fraction to percentage 0.333"33.3%"
Duration(time.Duration) string Up to two descending units 90*time.Minute"1h 30m"
Relative(target, ref time.Time) string Relative time from an explicit reference "2 hours ago"
Ordinal(int64) string English ordinal suffix 3"3rd"
Count(int64, singular, plural string) string Count phrase with comma separators 1000"1,000 items"

Behavior

Bytes
  • Bytes uses decimal units: kB, MB, GB, TB, PB, EB.
  • BinaryBytes uses IEC units: KiB, MiB, GiB, TiB, PiB, EiB.
  • Output uses at most one decimal place and drops trailing .0.
Duration
  • Duration shows at most two descending units.
  • Days are treated as 24 hours.
  • Sub-microsecond durations collapse to "0s".
Relative
  • Relative(target, ref) always requires an explicit reference time. There is no implicit time.Now() overload.
  • Cutovers: 60s to minute, 60m to hour, 24h to day, 7d to week, 30d to month, 365d to year.
  • Cutovers are lower bounds and quantities count complete units. For example, 359d is 11 months, 364d is 12 months, and 365d is 1 year.
  • Months are 30 days. Years are 365 days. There is no calendar math.
  • Differences wider than time.Duration still render their fixed 365-day year count instead of saturating.
Count
  • Count uses the singular form for 1 and -1, and the plural form for every other count.
  • If the selected form is empty, Count returns only the comma-separated number, with no trailing space.
Percent
  • Percent takes a fraction: Percent(0.333)"33.3%", Percent(1)"100%". Values above 1 are not clamped.
  • NaN and infinities render as "NaN%", "+Inf%", "-Inf%".
  • Extremely large finite percentages may use scientific notation, but they do not render as infinity.

Non-Goals

  • Locale-aware formatting
  • Alternate separator APIs
  • Precision knobs like BytesN
  • Friendly calendar words like yesterday or tomorrow
  • Text utilities, truncation helpers, or string humanizers
  • Builders, config structs, or formatting options
  • Parsing humanized display text

See SPECS/30-design-decisions.md for the standing rejection record behind these boundaries.

Why It Stays Small

Other humanize libraries grow into broad utility bundles. This package does the opposite on purpose: one obvious entry point per need, a short list of exported symbols, and behavior that can be explained on one screen.

SPECS/20-api-specs.md is the product contract. New public API must pass the admission rules in SPECS/20-api-specs.md.

Development

task test          # Run all tests with race detection
task lint          # Run golangci-lint v2.12.2 + go mod tidy check
task fmt           # Format code
task vet           # Run go vet
task bench         # Run benchmark baseline
task vuln          # Run govulncheck
task verify        # Full verification: deps, fmt, vet, lint, test, vuln
task deps          # Download and tidy dependencies
task clean         # Clean build artifacts and caches

License

Released under the MIT License.

Documentation

Overview

Package humanize formats machine values into human-readable English.

It is English-only, stateless, display-side helpers. It does not do internationalization, calendar math, unit conversion, or protocol fields. Humanized text is for display only; never feed it back into protocols, schedulers, billing, or audit.

API

  • Bytes formats with decimal units (kB, MB, GB, ...).
  • BinaryBytes formats with IEC binary units (KiB, MiB, GiB, ...).
  • Number formats integers with comma separators.
  • Percent takes a fraction: Percent(0.333) returns "33.3%".
  • Duration shows at most two descending units: "1h 30m", "2d 5h".
  • Relative formats a target time relative to a reference time.
  • Ordinal adds an English ordinal suffix: "1st", "23rd".
  • Count joins a count and an English noun: "1,000 items".

Relative time cutovers

Relative uses approximate buckets, not calendar math. Months are 30 days, years are 365 days, days are 24 hours.

|diff| < 1s        just now
[1s,   60s)        N seconds ago / in N seconds
[60s,  60m)        N minutes ago / in N minutes
[60m,  24h)        N hours ago   / in N hours
[24h,   7d)        N days ago    / in N days
[7d,   30d)        N weeks ago   / in N weeks
[30d, 365d)        N months ago  / in N months
>= 365d            N years ago   / in N years

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func BinaryBytes

func BinaryBytes(b int64) string

BinaryBytes formats b with IEC binary byte units such as "1.5 KiB" and "2 MiB". It preserves the sign and saturates math.MinInt64 to math.MaxInt64 when taking the absolute value.

Example
package main

import (
	"fmt"

	"github.com/agentable/go-humanize"
)

func main() {
	const gibibyte = 1024 * 1024 * 1024

	fmt.Println(humanize.BinaryBytes(0))
	fmt.Println(humanize.BinaryBytes(1536))
	fmt.Println(humanize.BinaryBytes(1048576))
	fmt.Println(humanize.BinaryBytes(5 * gibibyte))
}
Output:
0 B
1.5 KiB
1 MiB
5 GiB

func Bytes

func Bytes(b int64) string

Bytes formats b with decimal byte units such as "1.5 kB" and "2 MB". It preserves the sign and saturates math.MinInt64 to math.MaxInt64 when taking the absolute value.

Example
package main

import (
	"fmt"

	"github.com/agentable/go-humanize"
)

func main() {
	const gigabyte = 1000 * 1000 * 1000

	fmt.Println(humanize.Bytes(0))
	fmt.Println(humanize.Bytes(1500))
	fmt.Println(humanize.Bytes(1000000))
	fmt.Println(humanize.Bytes(5 * gigabyte))
}
Output:
0 B
1.5 kB
1 MB
5 GB

func Count

func Count(count int64, singular, plural string) string

Count formats count with Number's comma separators and chooses singular when count is ±1.

Example
package main

import (
	"fmt"

	"github.com/agentable/go-humanize"
)

func main() {
	fmt.Println(humanize.Count(1, "item", "items"))
	fmt.Println(humanize.Count(0, "item", "items"))
	fmt.Println(humanize.Count(1000, "item", "items"))
	fmt.Println(humanize.Count(-1, "person", "people"))
}
Output:
1 item
0 items
1,000 items
-1 person

func Duration

func Duration(d time.Duration) string

Duration formats d with up to two significant units such as "1h 30m" and "2d 5h". It preserves the sign, treats a day as 24 hours, uses ms and µs below a second, and returns "0s" for zero.

Example
package main

import (
	"fmt"
	"time"

	"github.com/agentable/go-humanize"
)

func main() {
	fmt.Println(humanize.Duration(0))
	fmt.Println(humanize.Duration(500 * time.Millisecond))
	fmt.Println(humanize.Duration(90 * time.Minute))
	fmt.Println(humanize.Duration(29 * time.Hour))
}
Output:
0s
500ms
1h 30m
1d 5h

func Number

func Number(n int64) string

Number formats n as a human-readable English integer with comma separators every three digits, such as "1,234,567" and "-1,000,000".

Example
package main

import (
	"fmt"

	"github.com/agentable/go-humanize"
)

func main() {
	fmt.Println(humanize.Number(0))
	fmt.Println(humanize.Number(1234567))
	fmt.Println(humanize.Number(-1000000))
	fmt.Println(humanize.Number(123456789))
}
Output:
0
1,234,567
-1,000,000
123,456,789

func Ordinal

func Ordinal(n int64) string

Ordinal formats n with an English ordinal suffix such as "1st", "23rd", and "-3rd".

Example
package main

import (
	"fmt"

	"github.com/agentable/go-humanize"
)

func main() {
	fmt.Println(humanize.Ordinal(1))
	fmt.Println(humanize.Ordinal(2))
	fmt.Println(humanize.Ordinal(3))
	fmt.Println(humanize.Ordinal(11))
	fmt.Println(humanize.Ordinal(21))
	fmt.Println(humanize.Ordinal(100))
}
Output:
1st
2nd
3rd
11th
21st
100th

func Percent

func Percent(fraction float64) string

Percent formats fraction as a percentage with at most one decimal place. The input is a fraction, so 0.333 renders as "33.3%" and 1 renders as "100%". Values above 1 are not clamped: 1.25 renders as "125%". NaN and infinities render as "NaN%", "+Inf%", and "-Inf%".

Example
package main

import (
	"fmt"

	"github.com/agentable/go-humanize"
)

func main() {
	fmt.Println(humanize.Percent(0.75))
	fmt.Println(humanize.Percent(0.333))
	fmt.Println(humanize.Percent(1.0 / 3))
	fmt.Println(humanize.Percent(0))
}
Output:
75%
33.3%
33.3%
0%

func Relative

func Relative(target, ref time.Time) string

Relative formats target relative to ref using approximate 30-day months and 365-day years. Durations under a second return "just now". The output describes target's position relative to ref: target before ref renders as "N units ago", and target after ref renders as "in N units".

Example
package main

import (
	"fmt"
	"time"

	"github.com/agentable/go-humanize"
)

func main() {
	ref := time.Date(2024, 2, 24, 12, 0, 0, 0, time.UTC)

	fmt.Println(humanize.Relative(ref.Add(-3*24*time.Hour), ref))
	fmt.Println(humanize.Relative(ref.Add(-7*24*time.Hour), ref))
	fmt.Println(humanize.Relative(ref.Add(-30*24*time.Hour), ref))
	fmt.Println(humanize.Relative(ref.Add(2*24*time.Hour), ref))
}
Output:
3 days ago
1 week ago
1 month ago
in 2 days

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL