# `Timecode`

A frame's SMPTE ST 12-1 address: hours, minutes, seconds and frames at `rate`, counted drop-frame or not. It has no single text form, since `01:00:00;00` doesn't say its rate, and is shown that way.

[Media and visual](types.md#media-and-visual); not ordered; since Jadpo 0.1.0.

## Make one

Build one from its fields, `Timecode { hours: ...  minutes: ...  seconds: ... }`.

Its parts keep these rules; a value that breaks one fails with the rule's name:

- `frames_within_rate`: `frames is below the rate's whole frames a second`
- `drop_frame_rate`: `drop_frame only at 30000/1001 or 60000/1001`
- `frame_not_dropped`: `a drop-frame timecode is never a label drop-frame counting skips`

## Fields

| Field | Type |
|---|---|
| `hours` | `Int` |
| `minutes` | `Int` |
| `seconds` | `Int` |
| `frames` | `Int` |
| `rate` | [`FrameRate`](type-frame-rate.md) |
| `drop_frame` | `Bool` |

## Text, JSON and storage

It has no single text form, so `text.from` and `text.parse` don't take it. JSON sends an object of its parts.

| JSON |
|---|
| `{"hours":1,"minutes":0,"seconds":0,"frames":0,"rate":"25/1","drop_frame":false}` |
| `{"hours":0,"minutes":10,"seconds":0,"frames":0,"rate":"30000/1001","drop_frame":true}` |
| `{"hours":0,"minutes":1,"seconds":0,"frames":2,"rate":"30000/1001","drop_frame":true}` |
| `{"hours":23,"minutes":59,"seconds":59,"frames":59,"rate":"60000/1001","drop_frame":false}` |

It is stored in 7 columns, each named for the field, `_` and its suffix. This is `{"hours":0,"minutes":1,"seconds":0,"frames":2,"rate":"30000/1001","drop_frame":true}`:

| Column | Type | Holds |
|---|---|---|
| `<field>_hours` | `smallint` | `0` |
| `<field>_minutes` | `smallint` | `1` |
| `<field>_seconds` | `smallint` | `0` |
| `<field>_frames` | `smallint` | `2` |
| `<field>_rate_numerator` | `integer` | `30000` |
| `<field>_rate_denominator` | `integer` | `1001` |
| `<field>_drop_frame` | `boolean` | `TRUE` |

## Shown to people

`text.format(value, locale:)` shows it to people in their locale.

With `locale: Locale.en_gb`:

| Value | Shown |
|---|---|
| `{"hours":1,"minutes":0,"seconds":0,"frames":0,"rate":"25/1","drop_frame":false}` | `01:00:00:00` |
| `{"hours":0,"minutes":10,"seconds":0,"frames":0,"rate":"30000/1001","drop_frame":true}` | `00:10:00;00` |
| `{"hours":0,"minutes":1,"seconds":0,"frames":2,"rate":"30000/1001","drop_frame":true}` | `00:01:00;02` |
| `{"hours":23,"minutes":59,"seconds":59,"frames":59,"rate":"60000/1001","drop_frame":false}` | `23:59:59:59` |

## Operations

It has the contract's own operations: `text.format`, `json.read`, `json.write` and `==`. It isn't ordered, so `<` and sorting don't take it. [Every standard type meets one contract](../topics/every-standard-type-meets-one-contract.md) says how they work.
