humanize

package
v2.10.0 Latest Latest
Warning

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

Go to latest
Published: May 17, 2023 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package humanize is an elegant, general-purpose, extensible, modular, locale-aware way to format and parse numbers and quantities - like distances, bytes, and time - in a human-readable way ideal for config files and as a building-block for fully translated ergonomic user interfaces.

This is the frozen version of the package previously at `tawesoft.co.uk/go/humanizex`. See migration instructions.

## Alternative - What about dustin's go-humanize?

dustin's go-humanize (https://github.com/dustin/go-humanize) is 3.9 to 4.5 times faster formatting and 2 times faster parsing, if this is a bottleneck for you. It's also quite mature, so is probably very well tested by now. If you're only targeting the English language it also has more handy "out of the box" features.

On the other hand, tawesoft's humanizex is more general purpose and has better localisation support.

Example (CustomDurations)
package main

import (
	"fmt"
	"time"

	humanizex "github.com/tawesoft/golib/v2/legacy/humanize"
	"golang.org/x/text/language"
)

func main() {
	plural := func(x float64) string {
		if x > 0.99 && x < 1.01 {
			return ""
		}
		return "s"
	}

	duration := (2 * time.Hour) + (20 * time.Second)

	// prints "Basic time: 2 h 20 s"
	fmt.Printf("Basic time: %s\n", humanizex.NewHumanizer(language.English).FormatDuration(duration))

	// Get the raw format parts
	parts := humanizex.FormatParts(
		duration.Seconds(),
		humanizex.CommonUnits.Second,
		humanizex.CommonFactors.Time,
	)

	// prints "Nice time: 2 hours and 20 seconds ago"
	fmt.Printf("Nice time: ")
	if (len(parts) == 1) && (parts[0].Unit.Utf8 == "s") {
		fmt.Printf("just now\n")
	} else {
		for i, part := range parts {
			fmt.Printf("%d", int(part.Magnitude+0.5))

			if part.Unit.Utf8 == "y" {
				fmt.Printf(" year%s", plural(part.Magnitude))
			} else if part.Unit.Utf8 == "d" {
				fmt.Printf(" day%s", plural(part.Magnitude))
			} else if part.Unit.Utf8 == "h" {
				fmt.Printf(" hour%s", plural(part.Magnitude))
			} else if part.Unit.Utf8 == "min" {
				fmt.Printf(" minute%s", plural(part.Magnitude))
			} else if part.Unit.Utf8 == "s" {
				fmt.Printf(" second%s", plural(part.Magnitude))
			}

			if i+1 < len(parts) {
				fmt.Printf(" and ")
			} else {
				fmt.Printf(" ago\n")
			}
		}
	}

}
Output:

Basic time: 2 h 20 s
Nice time: 2 hours and 20 seconds ago
Example (CustomFactors)
package main

import (
	"fmt"

	humanizex "github.com/tawesoft/golib/v2/legacy/humanize"
	"golang.org/x/text/language"
)

func main() {
	factors := humanizex.Factors{
		Factors: []humanizex.Factor{
			{1, humanizex.Unit{"millicenton", "millicenton"}, humanizex.FactorModeReplace},
			{60, humanizex.Unit{"centon", "centon"}, humanizex.FactorModeReplace},
			{60 * 60, humanizex.Unit{"centar", "centar"}, humanizex.FactorModeReplace},
			{24 * 60 * 60, humanizex.Unit{"cycle", "cycle"}, humanizex.FactorModeReplace},
			{7 * 24 * 60 * 60, humanizex.Unit{"secton", "secton"}, humanizex.FactorModeReplace},
			{28 * 24 * 60 * 60, humanizex.Unit{"sectar", "sectar"}, humanizex.FactorModeReplace},
			{365 * 24 * 60 * 60, humanizex.Unit{"yahren", "yahren"}, humanizex.FactorModeReplace},
			{100 * 365 * 24 * 60 * 60, humanizex.Unit{"centauron", "centauron"}, humanizex.FactorModeReplace},
		},
		Components: 2,
	}

	h := humanizex.NewHumanizer(language.English)

	est := float64((2 * 365 * 24 * 60 * 60) + 1)

	fmt.Printf("Hey, I'll be with you in %s. Watch out for toasters!\n",
		h.Format(est, humanizex.Unit{"millicenton", "millicenton"}, factors).Utf8)

}
Output:

Hey, I'll be with you in 2 yahren 1 millicenton. Watch out for toasters!
Example (Simple)
package main

import (
	"fmt"

	humanizex "github.com/tawesoft/golib/v2/legacy/humanize"
	"golang.org/x/text/language"
)

func main() {
	mustInt64 := func(v int64, err error) int64 {
		if err != nil {
			panic(err)
		}
		return v
	}

	hEnglish := humanizex.NewHumanizer(language.English)
	hDanish := humanizex.NewHumanizer(language.Danish)
	hBengali := humanizex.NewHumanizer(language.Bengali)

	// prints 1.5 KiB
	fmt.Println(hEnglish.FormatBytesIEC(1024 + 512))

	// prints 1,5 KiB
	fmt.Println(hDanish.FormatBytesIEC(1024 + 512))

	// prints ১.৫ KiB
	fmt.Println(hBengali.FormatBytesIEC(1024 + 512))

	// prints 1536
	fmt.Println(mustInt64(hEnglish.ParseBytesIEC("1.5 KiB")))

}
Output:

1.5 KiB
1,5 KiB
১.৫ KiB
1536

Index

Examples

Constants

View Source
const (
	// FactorModeIdentity indicates that the given factor label represents the
	// unit with no changes. For example, if you're talking about distance in
	// metres, FactorModeIdentity means that the current factor is measured in
	// metres and not millimetres, or kilometres.
	FactorModeIdentity = FactorMode(0)

	// FactorModeUnitPrefix indicates the given factor label is a unit prefix
	// e.g. "Ki" is a byte prefix giving "KiB".
	FactorModeUnitPrefix = FactorMode(1)

	// FactorModeReplace indicates the given factor label replaces the current
	// unit e.g. the duration of time 100 s becomes 1 min 40 s, not 1 hs
	// (which would read as a "hectosecond"!).
	FactorModeReplace = FactorMode(2)

	// FactorModeInputCompat indicates that the given factor label should only
	// be considered on input. This may be combined with any other FactorMode
	// by a bitwise OR operation. For example, when measuring distance,
	// you might accept based on context that "K" probably refers to the
	// "kilo"-prefix, not a Kelvin-metre!
	FactorModeInputCompat = FactorMode(4)
)

Variables

View Source
var CommonFactors = struct {
	// Time is time units in seconds, minutes, hours, days and years as min, h,
	// d, and y. These are non-SI units but generally accepted in context.
	// For times smaller than a second (e.g. nanoseconds), use SI instead.
	// The expected unit is a second (Unit{"s", "s"} or CommonUnits.Second)
	Time Factors

	// Distance are SI units that stop at kilo (because nobody uses
	// megametres or gigametres!) but includes centi. The expected unit is the
	// SI unit for distance, the metre (Unit{"m", "m"} or CommonUnits.Meter)
	Distance Factors

	// IEC are the "ibi" unit prefixes for bytes e.g. Ki, Mi, Gi with a
	// factor of 1024.
	IEC Factors

	// JEDEC are the old unit prefixes for bytes: K, M, G (only) with a factor
	// of 1024.
	JEDEC Factors

	// SIBytes are the SI unit prefixes for bytes e.g. k, M, G with a
	// factor of 1000. Unlike the normal SI Factors, it is assumed based on
	// context that when a "K" is input this is intended to mean the "k" SI
	// unit prefix instead of Kelvin - I've never heard of a Kelvin-Byte!
	SIBytes Factors

	// SIUncommon are the SI unit prefixes including deci, deca, and hecto
	SIUncommon Factors

	// SI are the SI unit prefixes except centi, deci, deca, and hecto
	SI Factors
}{
	Time: Factors{
		Factors: []Factor{
			{1, Unit{"s", "s"}, FactorModeReplace},
			{60, Unit{"min", "min"}, FactorModeReplace},
			{60 * 60, Unit{"h", "h"}, FactorModeReplace},
			{24 * 60 * 60, Unit{"d", "d"}, FactorModeReplace},
			{365.2422 * 24 * 60 * 60, Unit{"y", "y"}, FactorModeReplace},
		},
		Components: 2,
	},
	Distance: Factors{
		Factors: []Factor{
			{1E-9, Unit{"n", "n"}, FactorModeUnitPrefix},
			{1E-6, Unit{"μ", "u"}, FactorModeUnitPrefix},
			{1E-3, Unit{"m", "m"}, FactorModeUnitPrefix},
			{1E-2, Unit{"c", "c"}, FactorModeUnitPrefix},
			{1, Unit{"", ""}, FactorModeIdentity},
			{1000, Unit{"k", "k"}, FactorModeUnitPrefix},
		},
	},
	IEC: Factors{
		Factors: []Factor{
			{1, Unit{"", ""}, FactorModeUnitPrefix},
			{1024, Unit{"Ki", "Ki"}, FactorModeUnitPrefix},
			{1024 * 1024, Unit{"Mi", "Mi"}, FactorModeUnitPrefix},
			{1024 * 1024 * 1024, Unit{"Gi", "Gi"}, FactorModeUnitPrefix},
			{1024 * 1024 * 1024 * 1024, Unit{"Ti", "Ti"}, FactorModeUnitPrefix},
		},
	},
	JEDEC: Factors{
		Factors: []Factor{
			{1, Unit{"", ""}, FactorModeIdentity},
			{1024, Unit{"K", "K"}, FactorModeUnitPrefix},
			{1024 * 1024, Unit{"M", "M"}, FactorModeUnitPrefix},
			{1024 * 1024 * 1024, Unit{"G", "G"}, FactorModeUnitPrefix},
		},
	},
	SIBytes: Factors{
		Factors: []Factor{
			{1, Unit{"", ""}, FactorModeIdentity},
			{1E3, Unit{"k", "k"}, FactorModeUnitPrefix},
			{1E3, Unit{"K", "K"}, FactorModeUnitPrefix | FactorModeInputCompat},
			{1E6, Unit{"M", "M"}, FactorModeUnitPrefix},
			{1E9, Unit{"G", "G"}, FactorModeUnitPrefix},
			{1E12, Unit{"T", "T"}, FactorModeUnitPrefix},
		},
	},
	SIUncommon: Factors{
		Factors: []Factor{
			{1E-9, Unit{"n", "n"}, FactorModeUnitPrefix},
			{1E-6, Unit{"μ", "u"}, FactorModeUnitPrefix},
			{1E-3, Unit{"m", "m"}, FactorModeUnitPrefix},
			{1E-2, Unit{"c", "c"}, FactorModeUnitPrefix},
			{1E-1, Unit{"d", "d"}, FactorModeUnitPrefix},
			{1, Unit{"", ""}, FactorModeIdentity},
			{1E1, Unit{"da", "da"}, FactorModeUnitPrefix},
			{1E2, Unit{"h", "h"}, FactorModeUnitPrefix},
			{1E3, Unit{"k", "k"}, FactorModeUnitPrefix},
			{1E6, Unit{"M", "M"}, FactorModeUnitPrefix},
			{1E9, Unit{"G", "G"}, FactorModeUnitPrefix},
			{1E12, Unit{"T", "T"}, FactorModeUnitPrefix},
		},
	},
	SI: Factors{
		Factors: []Factor{
			{1E-9, Unit{"n", "n"}, FactorModeUnitPrefix},
			{1E-6, Unit{"μ", "u"}, FactorModeUnitPrefix},
			{1E-3, Unit{"m", "m"}, FactorModeUnitPrefix},
			{1, Unit{"", ""}, FactorModeIdentity},
			{1E3, Unit{"k", "k"}, FactorModeUnitPrefix},
			{1E6, Unit{"M", "M"}, FactorModeUnitPrefix},
			{1E9, Unit{"G", "G"}, FactorModeUnitPrefix},
			{1E12, Unit{"T", "T"}, FactorModeUnitPrefix},
		},
	},
}
View Source
var CommonUnits = struct {
	None          Unit
	Second        Unit
	Meter         Unit
	Byte          Unit
	Bit           Unit
	BitsPerSecond Unit
}{
	None:          Unit{"", ""},
	Second:        Unit{"s", "s"},
	Meter:         Unit{"m", "m"},
	Byte:          Unit{"B", "B"},
	Bit:           Unit{"b", "b"},
	BitsPerSecond: Unit{"bps", "bps"},
}

Functions

This section is empty.

Types

type Factor

type Factor struct {

	// Magnitude defines the absolute size of the factor e.g. 1000000 for the
	// SI unit prefix "M".
	Magnitude float64

	// Label describes the magnitude, usually as a unit prefix (like SI "M")
	// or as a replacement (like "min"), controlled by Mode.
	Unit Unit

	// Mode controls the formatting of this factor
	Mode FactorMode
}

Factor defines one entry in an ordered list of Factors.

type FactorMode

type FactorMode int

FactorMode controls the formatting of a factor.

type Factors

type Factors struct {
	// Factors is a list of Factor entries in ascending order of magnitude.
	Factors []Factor

	// Components controls how the formatting is broken up. If set to zero or
	// one, formatting has a single component e.g. "1.5 M". If set to two or
	// more, formatting is broken up into previous factors e.g. "1 h 50 min"
	// (with 2 components) or "1 h 50 min 25 s" (with 3 components).
	Components int
}

Factors describes a way to format a quantity with units.

func (Factors) Min added in v2.0.9

func (f Factors) Min(n float64) int

Min returns the index of the first Factor greater or equal to n. If n is smaller than the first Factor, returns the first Factor instead. Ignores all factors that have mode FactorModeInputCompat.

type Humanizer

type Humanizer interface {
	// Format is a general purpose locale-aware way to format any quantity
	// with a defined set of factors. The unit argument is the base unit
	// e.g. s for seconds, m for meters, B for bytes.
	Format(value float64, unit Unit, factors Factors) String

	FormatNumber(number float64) String           // e.g. 12 k
	FormatDistance(meters float64) String         // e.g. 10 µm, 10 km
	FormatDuration(duration time.Duration) string // e.g. 1 h 50 min
	FormatSeconds(seconds float64) string         // e.g. 1 h 50 min
	FormatBytesJEDEC(bytes int64) string          // e.g. 12 KB, 5 MB
	FormatBytesIEC(bytes int64) string            // e.g. 12 kB, 5 MB
	FormatBytesSI(bytes int64) string             // e.g. 12 KiB, 5 MiB

	// Accept is a general purpose locale-aware way to parse any quantity
	// with a defined set of factors from the start of the string str. The
	// provided unit is optional and is accepted if it appears in str.
	//
	// Accept returns the value, the number of bytes successfully parsed (which
	// may be zero), or an error.
	Accept(str string, unit Unit, factors Factors) (float64, int, error)

	// Parse is a general purpose locale-aware way to parse any quantity
	// with a defined set of factors.  The provided unit is optional and is
	// accepted if it appears in str.
	Parse(str string, unit Unit, factors Factors) (float64, error)

	ParseDuration(str string) (time.Duration, error)
	ParseBytesJEDEC(str string) (int64, error)
	ParseBytesIEC(str string) (int64, error)
	ParseBytesSI(str string) (int64, error)
}

Humanizer implements a locale-aware way to parse and format humanized quantities.

func NewHumanizer

func NewHumanizer(tag language.Tag, options ...interface{}) Humanizer

NewHumanizer initialises a human language number encoder/decoder for the given tag (representing a specific language or locale).

The language.Tag is usually a named language from golang.org/x/text/language e.g. language.English and controls how numbers are written e.g. comma placement, decimal point, digits.

type Part

type Part struct {
	Magnitude float64
	Unit      Unit
}

Part describes some component of a formatting result e.g. 1.5 km or 1 hour

func FormatParts

func FormatParts(n float64, unit Unit, factors Factors) []Part

FormatParts is a general purpose locale-aware way to format any quantity with a defined set of factors into a list of parts. The unit argument is the base unit e.g. s for seconds, m for meters, B for bytes. In the simple case a list of only one Part is returned, e.g. a Part representing 1.5 km. In other cases, there may be multiple parts e.g. the two parts "1 h" and "30 min" making up the time "1 h 30 min". The number of parts returned is never more than that defined by the factors argument's Components field (but may be fewer).

type String

type String struct {
	// Utf8 is the native Utf8-encoded Unicode representation
	Utf8 string

	// Ascii is an alternative version accepted for non-Unicode inputs (such
	// as when a user does not know how to enter µ on their keyboard (on mine,
	// it's Right-Alt+m)) or for non-Unicode output (such as legacy systems).
	Ascii string
}

String holds an Utf8-encoded and an Ascii-compatible encoding of a string.

type Unit

type Unit String

Unit describes some quantity e.g. "m" for length in metres, "k" for the SI unit prefix kilo, "km" for a kilometre.

func (Unit) Cat

func (u Unit) Cat(v Unit) Unit

Cat concatenates two units (u + v) and returns the result.

Jump to

Keyboard shortcuts

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