display

package
v2.2.4 Latest Latest
Warning

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

Go to latest
Published: May 7, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package display provides human-readable formatting for CalcMark types.

This package separates display concerns from the core type system:

  • spec/types: Model layer - stores exact values, String() returns precise representation
  • format/display: View layer - formats values for human consumption (100K instead of 100000)

The Formatter type holds locale-specific configuration. Package-level functions (Format, FormatNumber, etc.) delegate to a default en-US formatter singleton.

Usage:

import "github.com/CalcMark/go-calcmark/v2/format/display"

// Package-level (en-US default):
fmt.Println(display.Format(result))  // "100K users"

// Locale-aware:
f := display.NewFormatter(display.DefaultConfig())
fmt.Println(f.Format(result))

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Format

func Format(t types.Type) string

Format returns a human-readable string representation of any CalcMark type. This is the main entry point for display formatting using en-US defaults.

func FormatCurrency

func FormatCurrency(c *types.Currency) string

FormatCurrency formats a currency value in human-readable form. Preserves 2 decimal places for small values, uses suffixes for large values. Normalizes currency codes to symbols (USD -> $) and handles sign positioning.

Examples:

FormatCurrency($1500000) → "$1.5M"
FormatCurrency($42.50) → "$42.50"
FormatCurrency($1500) → "$1,500.00"
FormatCurrency(USD100) → "$100.00"
FormatCurrency(CNY1000) → "CNY 1,000.00"

func FormatDate

func FormatDate(d *types.Date) string

FormatDate formats a date in locale-aware short form using en-US defaults.

Examples:

FormatDate(Jan 12 2025) → "Sun, Jan 12, 2025"

func FormatDuration

func FormatDuration(d *types.Duration) string

FormatDuration formats a duration in human-readable form.

Examples:

FormatDuration(1 month) → "1 month"
FormatDuration(365 days) → "365 days"

func FormatFractionUnicode

func FormatFractionUnicode(f *types.Fraction) string

FormatFractionUnicode renders a Fraction using Unicode Number Forms where available, falling back to ASCII (e.g., "7/12") for fractions without a Unicode codepoint. Mixed numbers combine integer + Unicode fraction: "2⅓" instead of "2 1/3".

func FormatNumber

func FormatNumber(value decimal.Decimal) string

FormatNumber formats a decimal number in human-readable form. Uses K/M/B/T suffixes for large numbers, preserves small numbers as-is.

Examples:

FormatNumber(100000) → "100K"
FormatNumber(1500000) → "1.5M"
FormatNumber(42) → "42"
FormatNumber(0.5) → "0.5"

func FormatPercentage

func FormatPercentage(p *types.Percentage) string

FormatPercentage formats a percentage in human-readable form. Applies magnitude-based rounding to the display value (e.g., 62.166…% → 62.2%).

Examples:

FormatPercentage(0.32) → "32%"
FormatPercentage(0.6217) → "62.2%"

func FormatQuantity

func FormatQuantity(q *types.Quantity) string

FormatQuantity formats a quantity (value + unit) in human-readable form. For known units (length, mass, volume, data, etc.), it normalizes to the most appropriate unit scale (e.g., 1000000 GB → 976.56 TB). For unknown/arbitrary units, it uses K/M/B/T number suffixes. Napkin estimates (IsNapkin=true) are prefixed with tilde (~).

Examples:

FormatQuantity(1000 m) → "1 km"
FormatQuantity(23400000 GB) → "22.31 PB"
FormatQuantity(100000 users) → "100K users"
FormatQuantity(400 GB, isNapkin=true) → "~400 GB"

func FormatRate

func FormatRate(r *types.Rate) string

FormatRate formats a rate (quantity per time) in human-readable form. For known units, normalizes to appropriate scale (e.g., 1000000 bytes/s → 976.56 KB/s).

Examples:

FormatRate(1000000 bytes/s) → "976.56 KB/s"
FormatRate(100000 users/day) → "100K users/day"

func NormalizeForDisplay

func NormalizeForDisplay(value decimal.Decimal, unit string) (decimal.Decimal, string)

NormalizeForDisplay converts a value+unit to the most human-readable form. It finds the largest unit in the same family where the value is >= 1. Unknown units are returned unchanged.

Examples:

NormalizeForDisplay(1000, "m") → (1, "km")
NormalizeForDisplay(23400000, "GB") → (22.89, "PB")
NormalizeForDisplay(100000, "users") → (100000, "users")  // unknown unit

Types

type DisplayConfig

type DisplayConfig struct {
	// Tag is the parsed BCP 47 language tag.
	Tag language.Tag

	// DecimalSep is the decimal point character (e.g., "." for en-US, "," for de-DE).
	DecimalSep string

	// ThousandSep is the thousands grouping separator (e.g., "," for en-US, "." for de-DE).
	ThousandSep string

	// UnicodeFractions enables Unicode Number Forms (e.g., ½, ⅓, ¾) for fraction display.
	// Only used in TUI; JSON and CLI output always uses ASCII fractions.
	UnicodeFractions bool

	// DateFormat overrides the locale's default date layout when set.
	// Uses the user-friendly DSL (see date_format_dsl.go): "MON dd,
	// YYYY", "YYYY-MM-dd", etc. Empty string falls back to the
	// locale's default ("Mon, Jan 2, 2006" for en-US).
	DateFormat string

	// PeriodDateFormat overrides the date layout used for endpoints
	// inside a Period's display ("01-Jan-2026 – 31-Mar-2026"). Empty
	// string falls back to the dd-MON-YYYY default. When DateFormat
	// is set but PeriodDateFormat is not, the period default still
	// applies — Period output stays compact even when single-Date
	// output is set to a verbose layout.
	PeriodDateFormat string
}

DisplayConfig holds locale-specific formatting preferences. It is a value type designed to be stack-allocatable.

func DefaultConfig

func DefaultConfig() DisplayConfig

DefaultConfig returns the en-US display configuration.

func NewConfig

func NewConfig(locale string) (DisplayConfig, error)

NewConfig creates a DisplayConfig from a locale string (e.g., "en-US", "de-DE"). Returns an error if the locale is invalid. Use DefaultConfig() for en-US defaults.

func (DisplayConfig) Validate

func (c DisplayConfig) Validate() error

Validate checks that the DisplayConfig has valid, non-empty separators.

type Formatter

type Formatter struct {
	// contains filtered or unexported fields
}

Formatter formats CalcMark types for human-readable display. It is a value type wrapping a DisplayConfig. Use NewFormatter to create one, or DefaultFormatter() for en-US defaults.

func DefaultFormatter

func DefaultFormatter() Formatter

DefaultFormatter returns a Formatter with en-US defaults.

func NewFormatter

func NewFormatter(cfg DisplayConfig) Formatter

NewFormatter creates a Formatter from a DisplayConfig.

func (Formatter) Config

func (f Formatter) Config() DisplayConfig

Config returns the underlying DisplayConfig.

func (Formatter) Format

func (f Formatter) Format(t types.Type) string

Format returns a human-readable string representation of any CalcMark type.

func (Formatter) FormatCurrency

func (f Formatter) FormatCurrency(c *types.Currency) string

FormatCurrency formats a currency value in human-readable form.

func (Formatter) FormatDate

func (f Formatter) FormatDate(d *types.Date) string

FormatDate formats a date in locale-aware short form. Uses abbreviated day-of-week and month names with locale-specific ordering. Examples: "Wed, Jan 12, 2025" (en-US), "Mi., 12. Jan. 2025" (de-DE).

When DisplayConfig.DateFormat is set, that DSL string overrides the locale default — see date_format_dsl.go for the supported tokens (MON dd, YYYY etc.). The locale still applies to month / weekday names within the custom layout.

The machine-readable Date.String() ("Monday, January 2, 2006") is intentionally different — it is the model layer's precise representation. This method is the display layer's human-friendly form.

func (Formatter) FormatDuration

func (f Formatter) FormatDuration(d *types.Duration) string

FormatDuration formats a duration in human-readable form.

func (Formatter) FormatNumber

func (f Formatter) FormatNumber(value decimal.Decimal) string

FormatNumber formats a decimal number in human-readable form. Applies magnitude-based rounding to keep computed results readable (e.g., 113.097… → 113, 19.209… → 19.2).

func (Formatter) FormatPercentage

func (f Formatter) FormatPercentage(p *types.Percentage) string

FormatPercentage formats a percentage in human-readable form. Applies magnitude-based rounding to the display value (e.g., 62.166…% → 62.2%).

func (Formatter) FormatPeriod

func (f Formatter) FormatPeriod(p *types.Period) string

FormatPeriod renders a Period for human display as a compact start–end range using the dd-MON-YYYY layout:

Q1                       → "01-Jan-2026 – 31-Mar-2026"
this fiscal quarter      → "01-Apr-2026 – 30-Jun-2026"
April                    → "01-Apr-2026 – 30-Apr-2026"
between Apr 15 and Jul 4 → "15-Apr-2026 – 04-Jul-2026"

Why no kind label: the source already names the period (`this fiscal quarter`, `Q1`, etc.). Echoing the label as part of the result is redundant — same reason `today` resolves to a formatted date without echoing "today". Period values surface the boundaries; the kind context lives in the source line.

Why dd-MON-YYYY (not the regular FormatDate output): a Period renders TWO dates side-by-side, so each one needs to be compact. FormatDate's day-of-week prefix ("Wed, Apr 1, 2026") is great for a single Date but reads as noise when twinned. dd-MON-YYYY is unambiguous (no MM/DD vs DD/MM ambiguity) and locale-aware for the month abbreviation via the existing monday library.

The kind label is still available via Period.String() for diagnostics / debug output / JSON `value` field, where the surrounding source isn't visible.

func (Formatter) FormatQuantity

func (f Formatter) FormatQuantity(q *types.Quantity) string

FormatQuantity formats a quantity (value + unit) in human-readable form.

func (Formatter) FormatRate

func (f Formatter) FormatRate(r *types.Rate) string

FormatRate formats a rate (quantity per time) in human-readable form. When the rate's amount unit is a currency symbol ($, €, £, ¥), the symbol is rendered as a prefix (e.g., "$363.46/day") matching FormatCurrency style.

func (*Formatter) SetMeasurement

func (f *Formatter) SetMeasurement(mc *units.MeasurementConfig, strict bool)

SetMeasurement configures the formatter to annotate bare ambiguous units in output. When strict is true (the default), bare units like "oz" are displayed as "us oz" or "troy oz" depending on the active convention.

type UnitFamily

type UnitFamily struct {
	BaseUnit string      // The reference unit (ToBase = 1)
	Units    []UnitScale // Ordered smallest to largest
}

UnitFamily represents a group of related units that can be normalized. Units are ordered from smallest to largest (by magnitude of what they measure).

type UnitScale

type UnitScale struct {
	Unit   string          // Display symbol (e.g., "km", "MB")
	ToBase decimal.Decimal // Multiplier to convert TO base unit (e.g., mm->m: 0.001)
}

UnitScale represents a unit with its conversion factor. ToBase converts a value in this unit to the base unit. Example: For millimeters in a meter-based family, ToBase = 0.001 (1mm = 0.001m)

Jump to

Keyboard shortcuts

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