# `Timecode`

An address of one frame in a video: hours, minutes, seconds and frames.

Use it for video editing. It holds its frame rate, so a time on its own isn't enough. For a plain amount of time, use [`Duration`](/docs/types/type-duration.md).

## Shape

| Field | Type | Required? |
|---|---|---|
| `hours` | `Int` | Yes |
| `minutes` | `Int` | Yes |
| `seconds` | `Int` | Yes |
| `frames` | `Int` | Yes |
| `rate` | [`FrameRate`](/docs/types/type-frame-rate.md) | Yes |
| `drop_frame` | `Bool` | Yes |

Its parts keep these rules, and 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

## Example

```jadpo
Timecode {
    hours: 1
    minutes: 0
    seconds: 0
    frames: 0
    rate: FrameRate { numerator: 25 denominator: 1 }
    drop_frame: false
}
```

## Good to know

- `drop_frame` works only at 30000/1001 or 60000/1001.
- It has no single text form, so `text.parse` won't take it.
- `==` tells you whether two are the same. `<` and `list.sort_by` don't take it, so sort by one of its parts instead.

## In JSON

An object of its parts, in a request, a response or `json.write`:

```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}
```

## Details

<details>
<summary>Text form, storage, how it is shown, and operations</summary>

`Timecode` is in the group Media and visual, and has been in Jadpo since 0.1.0.

### Text form

It has no single text form, so `text.from` and `text.parse` don't take it.

### How it is stored

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` |

### How it is 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 also takes `text.format`, `json.read`, `json.write` and `==`. [How every standard type behaves](/docs/every-standard-type-meets-one-contract.md) says how they work.

</details>
