# notation - how a page writes a figure Source: https://truecopy.dev/docs/notation/ Module: truecopy/notation That 1 234,50 and 1,234.50 are the same quantity says nothing about banks. It says how the page was typeset - and it is the part everybody rewrites and everybody gets wrong once. Notation is not domain. That `(123,45)` is negative and so is `123,45-`, that `25/01` is a day before a month here and after it there: none of it says anything about banking, pensions or invoices. ```ts import { readNumber, findNumbers, isOnlyNumber, readDate, readLeadingDate } from 'truecopy/notation'; ``` ## Numbers ```ts readNumber('1 234,56'); // 1234.56 readNumber('1,234.56'); // 1234.56 readNumber("1'234.56"); // 1234.56 Swiss apostrophe readNumber('1 234,56 €'); // 1234.56 currency, thin spaces readNumber('27800.50'); // 27800.5 NOT 2 780 050 readNumber('27.800'); // 27800 three digits behind is thousands readNumber('1.2'); // 1.2 one or two digits behind is a decimal readNumber('(123,45)'); // -123.45 the accounting negative readNumber('123,45-'); // -123.45 the trailing minus readNumber('no figure'); // null ``` **Null rather than a guess.** On figures somebody will act upon, a refusal costs a correction and a wrong reading costs a decision. ### The two defects this file exists to hold Both were written twice, in two different readers, and both shipped: 1. **Every dot read as a thousands separator** turned `27800.50` into `2 780 050`, a hundred times over, on a document where the figure is compared against a legal threshold. 2. **A run of spaces bounded too tightly** after an opening bracket made the match start _later_ and drop the bracket, turning an accounting negative into a positive amount, silently, on financial data. Both are the kind of defect that passes every test written by whoever caused it. They live in one place now, with the tests that pin them. ## Finding numbers in a line ```ts findNumbers('2018 worked 4 quarters, 28 500,00 paid'); // [{ value: 2018, raw: '2018', index: 0 }, { value: 4, … }, { value: 28500, … }] findNumbers('2018 worked 4 quarters, 28 500,00 paid', 2); // [{ value: 28500, … }] only figures written with two decimals ``` The `decimals` argument is how a page tells a sum of money from a reference number **without the library ever hearing the word "amount"**. That two decimals mean money is your knowledge; it stays in your code. The lookarounds keep a match from starting inside another number. Without them a date glued to a figure (`30/05/2026 300,00` on a flattened line) reads as `026 300,00`. ```ts isOnlyNumber('1 234,56'); // true a cell of figures isOnlyNumber('paid 12,00'); // false prose that quotes one isOnlyNumber('12', 2); // false no fractional part ``` ## Dates ```ts const FRENCH = { dateOrder: 'DMY', months: { janv: 0, fevr: 1, mars: 2 } }; readDate('01/02/2026', FRENCH); // 1 February readDate('01/02/2026', { dateOrder: 'MDY' }); // 2 January readDate('31/04/2026', FRENCH); // null - a 31st in a 30-day month is a misread ``` There is no reading of `01/02/2026` that is right everywhere, and no amount of cleverness makes one: **it is stated, or it is guessed.** ```ts readLeadingDate('25/01/2026 GROCERIES', FRENCH); // { date, length: 10 } - so you strip it and keep the wording readLeadingDate('25/01 GROCERIES', FRENCH, 2026); // the year the document states readLeadingDate('25/01 GROCERIES', FRENCH); // null - nothing says which year readLeadingDate('25 janv. 2026 …', FRENCH); // months come from the notation ``` `months` is **data**, keyed accent-free and lower-case, so a profile served by a backend can carry it, the same argument [`pattern`](https://truecopy.dev/docs/pattern/) makes for everything that varies by market. Map of this site for a model: https://truecopy.dev/llms.txt Every page in one file: https://truecopy.dev/llms-full.txt