Numbers and currency
Numbers, amounts, percentages, units, byte sizes, durations, lists and ranges.
Numbers#
format. Precision, notation and sign come from format;
separators, grouping and digits come from the presentation.
A preset:
'integer','decimal','decimal-,1' 'decimal-,2' 'decimal-or4' 'compact'.Or a
Number: decimals or significant digits (the type refuses both), plus a notation (Spec standard,scientific,engineering,compact) and a sign display.
An out-of-range or inconsistent specification throws a Range; it is never clamped.
Currency amounts#
format.
The currency comes from the data, never from the language.
'EUR'in an English text is still euros.Decimals follow the currency's minor units (EUR 2, JPY 0, KWD 3), rounded half away from zero. Override them only as a deliberate presentation choice, with
decimals.Exact amounts: pass a
Decimal(fromString to) and the digits reachDecimal String Intlas that exact decimal, never through a floating-point number.A localised string such as
'1.234,56'is user input, not an amount: read it withclassifyfirst.Number Input
| EUR, en | |
| EUR, nl | |
| JPY (no minor unit) | |
| KWD (three decimals) | |
| Whole amounts | |
| A change, signed | |
| Exact decimal string | |
| The same value as a number | |
| No amount |
// apps/docs/examples/format/currency-amounts.tsx'use client';
import {formatCurrency, toDecimalString} from '@workspace/active-format';import { Table, TableBody, TableCell, TableRow,} from '@workspace/active-ui/components/primitives/table';
const EXACT = '12345678901234567.89';
/** * The currency comes from the data, the notation from the presentation. * Decimals follow the currency's minor units unless the call overrides them. */export default function CurrencyAmounts() { const rows: [string, string][] = [ ['EUR, en', formatCurrency({amount: 1234.5, currency: 'EUR'}, 'en')], ['EUR, nl', formatCurrency({amount: 1234.5, currency: 'EUR'}, 'nl')], [ 'JPY (no minor unit)', formatCurrency({amount: 1234.5, currency: 'JPY'}, 'en'), ], [ 'KWD (three decimals)', formatCurrency({amount: 1234.5, currency: 'KWD'}, 'en'), ], [ 'Whole amounts', formatCurrency({amount: 1234.5, currency: 'EUR'}, 'en', {decimals: 0}), ], [ 'A change, signed', formatCurrency({amount: 12.5, currency: 'EUR'}, 'en', { sign: 'exceptZero', }), ], [ 'Exact decimal string', formatCurrency({amount: toDecimalString(EXACT), currency: 'EUR'}, 'en'), ], [ 'The same value as a number', formatCurrency({amount: Number(EXACT), currency: 'EUR'}, 'en'), ], [ 'No amount', formatCurrency({amount: null, currency: 'EUR'}, 'en') || '(empty string)', ], ]; return ( <Table> <TableBody> {rows.map(([label, text]) => ( <TableRow key={label}> <TableCell className="text-muted-foreground">{label}</TableCell> <TableCell className="font-mono"> <output suppressHydrationWarning>{text}</output> </TableCell> </TableRow> ))} </TableBody> </Table> );}Fields
- Type
number
Number of decimals, an integer from 0 to 100, solely as a deliberate presentation choice of the call site (e.g. whole amounts on a dashboard). Without this option the decimals follow the currency's natural minor units via Intl (EUR/USD 2, JPY 0, KWD 3); that is the norm and a fixed count is the exception. Rounding is Intl's half-expand (half away from zero), on the exact decimal for a Decimal.
- Type
NumberSign
When the sign is written; see Number. Default auto: a minus for a negative amount, also when it rounds to zero (-€ 0,00). except shows a delta (+€ 12,50); negative never signs a zero.
Use UNKNOWN_ ('XXX') only for an amount whose currency cannot be established, never as a
substitute for a currency you could know.
Percentages#
The stored form decides the function:
format: the value is a fraction; 0.125 is 12.5 %.Fraction As Percent(0.125, 'decimal- 1', presentation) format: the value is in points; 12.5 is 12.5 %.Percentage Points(12.5, 'decimal- 1', presentation)
Units, sizes and durations#
format: a measured quantity in the unit you supply, never converted.Unit(value, unit, presentation, options?) format: a count of bytes. TheByte Size(bytes, presentation, {system}) systemis required, becausedecimal(1000) andbinary(1024) differ by up to 12.6 %.Fields
systemrequired- Type
ByteSize System
The scale:
decimal(1000, SI unitsk…B PB) orbinary(1024, IEC unitsKi…B Pi). Required: the two differ by 2.4% at kilo and 12.6% at peta, and a reader cannot tell which one a bareB KBmeant.- Type
number
The largest number of decimals shown, an integer from 0 to 20; trailing zeros are dropped (
1.5, not1.50). Default1, owned by this package. A whole number of bytes never shows decimals.format: an elapsed duration in fixed units, from milliseconds to days of exactly 24 hours, never months. It needsElapsed Duration(value, input Unit, presentation, options) Intl.and throws without it.Duration Format
Lists and ranges#
formatjoins your own text in the reader's language (List(items, presentation, options?) Amsterdam, Utrecht and Delft).formatandNumber Range(start, end, format, presentation) formatwrite a range with the language's own range separator. A start after its end, or a missing bound, givesDate Only Range(start, end, preset, presentation) ''.
Empty and invalid values#
An empty value (null, undefined, '', and Na or ±Infinity for numbers) and an unreadable
value return ''. Your interface chooses and translates its own placeholder. A programming or
configuration error, such as an unknown unit or an invalid decimals, throws.