# Formatting

> Display numbers, dates, calendar dates, coordinates and unit labels from .well data without floating-point noise or time zone shifts, using the display helpers of @welldot/utils.

These helpers turn stored values into text for screens, PDFs and tables. They are framework-free and are shared by the welldot.org editor and `@welldot/pdf`. Anything that needs translated words takes them as plain strings, so you keep your own message catalog.

## Numbers

Floating-point arithmetic produces values like `39.99998784`. `formatNumber` bounds the decimals so that noise never reaches the reader:

```ts
import { formatNumber } from '@welldot/utils';

formatNumber(39.99998784, { fractionDigits: 2, suffix: 'm' }); // '40.00 m'
formatNumber(1234.5, { locale: 'pt-BR', maximumFractionDigits: 1 }); // '1.234,5'
formatNumber(undefined); // '—'
```

Options: `fractionDigits` (fixed), `minimumFractionDigits`, `maximumFractionDigits`, `suffix`, `locale` (a BCP 47 tag, default `en-US`) and `fallback` (default `—`, shown for `null`, `undefined` and `NaN`). An absent value shows as a dash, never as `0`, following the format's "absent means unknown" rule.

## Dates

The format has two kinds of temporal values ([datetime conventions](/format/spec/v2/format-reference#datetime-conventions)): calendar dates (`YYYY-MM-DD`, for construction and permits) and instants (RFC 3339 with an offset, for everything measured). Handle them differently:

```ts
import {
  formatCalendarDate,
  formatDate,
  fromCalendarDate,
  toCalendarDate,
} from '@welldot/utils';

formatCalendarDate('2025-03-01'); // '01/03/2025', no time zone shift
formatCalendarDate('2025-03-01', 'yyyy-MM-dd'); // any date-fns pattern

formatDate(new Date('2025-03-01T10:00:00Z')); // 'dd/MM/yyyy HH:mm' in the runtime's time zone

toCalendarDate(new Date(2025, 2, 1)); // '2025-03-01', from a local Date, never through UTC
fromCalendarDate('2025-03-01'); // a local Date, or null when malformed
```

::warning
Never pass a calendar date to `new Date('2025-03-01')`: JavaScript reads it as UTC midnight, which is the previous day in the Americas. Use `fromCalendarDate` and `formatCalendarDate`.
::

For the local date of an instant at the well site, use `instantLocalDate(instant, timeZone)` with the zone from `getWellTimeZone(well)` ([Permits](/utils/guides/permits)).

## Coordinates

```ts
import { formatCoord, parseToDd } from '@welldot/utils';

formatCoord(-1.4558, 'DMS', true); // `1°27'20.88"S` (true = latitude)
formatCoord(-1.4558, 'DD', true); // '-1.455800'
parseToDd(`1°27'20.88"S`); // -1.4558, from DD or DMS text; NaN when unreadable
```

`clampLat` and `clampLng` keep typed values in range. Projected coordinates are not handled here; the file stores latitude and longitude only ([CRS scope](/format/spec/v2/format-reference#crs-scope)).

## Unit labels

| Function                                 | Returns                                                           |
| ---------------------------------------- | ----------------------------------------------------------------- |
| `resolveFlowUnitLabel(unit)`             | `m³/h`, `L/s`, `gpm` for the storage tokens `m3/h`, `L/s`, `gpm`. |
| `resolveVolumeUnitLabel(unit)`           | `m³`, `L`, `ft³`, `gal`.                                          |
| `resolveDiameterUnitLabel(unit, locale)` | `mm`, or `"` in Portuguese and `in.` in English for inches.       |

Convert the values themselves with the [unit conversion](/core/guides/unit-conversion) helpers of `@welldot/core`.

## Records

- `formatMeterLabel(meter, { locale, untypedLabel, dateFormat? })`: `Electromagnetic · S/N 123 · 01/02/2024`.
- `permitLabel(permit, fallback?)`: the authority and identifier of a permit.
- `formatWaterQualityResult(result, labels)`: `< 0.001`, `0.004 (est.)`, the not-detected, present or absent label, or the text ([Water quality](/utils/guides/water-quality)).
