# Durations

> One form, seven units, and two of them are nominal.

Source: https://www.sendlang.com/docs/reference/durations
Section: Reference

A duration is an **amount** and a **unit suffix**, with no space between them.

**Syntax form, not a complete program:**

```sendql
30d    12h    2w    6mo    1y    90s    5m
```

It is written identically everywhere — in a workflow's schedule, in a SendQL
window, in an age comparison. There is one form and there are no alternatives.

**Syntax form, not a complete program:**

```sendflow
wait 2d                                    // a schedule
frequency cap 4 per 7d                     // a schedule
repeat up to 3 every 24h { ... }           // a schedule
if count(open within 7d) >= 1 { ... }      // inside a predicate
if now - attr.signup_date > 30d { ... }    // inside a predicate
```

## The units

| Unit | Means | Exact length |
|---|---|---|
| `s` | seconds | 1 second |
| `m` | **minutes** | 60 seconds |
| `h` | hours | 60 minutes |
| `d` | days | 24 hours |
| `w` | weeks | 7 days |
| `mo` | **months** | **30 days** — nominal |
| `y` | years | **365 days** — nominal |

## m is minutes. Months are mo.

This is the one genuinely dangerous ambiguity in the language, and it is worth
saying loudly:

**Syntax form, not a complete program:**

```sendql
5m     // five minutes
5mo    // five months
```

A `wait 3m` that was meant to be `wait 3mo` does not fail — it succeeds, three
minutes later. The lexer checks for the `mo` suffix first and falls back to the
single trailing character, so both spellings are valid and only one of them is
what you meant. There is no way for the language to catch this for you. Read your
diffs.

## Months and years are nominal

`1mo` is exactly 30 days. `1y` is exactly 365 days. Neither is calendar-aware:
`1mo` from the 31st of January is not the 28th of February, it is the 2nd of
March.

This is deliberate, and the reason is worth understanding because it explains a
lot about the rest of the design. A calendar-exact month would make a literal's
meaning **depend on when it was evaluated** — `1mo` would be 28 days in February
and 31 in March. That would make it impossible to check a `between` window for
reversal statically, and impossible for the engine to fold a schedule at compile
time.

A duration has to be a fixed quantity for the language to be able to reason about
it at all. So it is one.

If you need calendar-exact behaviour — "the first of every month" — that is a
date on the contact, and it belongs in a
[date-relative trigger](/docs/sendflow/settings), not in a duration.

## What is not a duration

| Not this | Why |
|---|---|
| `30 days` | The word form is not a duration. Write `30d`. |
| `30 d` | A duration is one token; no space. |
| `-1d` | Durations are unsigned. Direction comes from the operator. |
| `2.5d` | The amount is an integer. Write `60h`. |
| `0d` | Valid inside a predicate, but a **schedule** duration must be at least 1. |

The word form is the one people reach for first, and it fails with an error that
hands you the fix rather than pointing at a token:

**Rejected — the parser refuses this:**

```sendflow
workflow "t" v1 {
  enter on segment "trial-started"

  wait 2 days
}
```

> `durations are written in the compact form: use 2d, not 2 days (a workflow and a SendQL condition spell a duration the same way now)`

The compact form is the *only* form, everywhere. A workflow's schedule and a
condition inside that workflow spell a duration exactly the same way, so a single
line never carries two spellings of one idea:

**Syntax form, not a complete program:**

```sendflow
wait up to 7d until count(activity.login within 7d) >= 2
```

Get the unit wrong and it tells you that too:

**Rejected — the parser refuses this:**

```sendflow
workflow "t" v1 {
  enter on segment "trial-started"

  wait 2 dayz
}
```

> `expected a duration such as 7d, 24h or 2w — "dayz" is not a unit`

## Rounding and precision

`0d` is rejected as a **schedule** — `wait 0d`, `every 0h` and `frequency cap 4
per 0d` are all errors, because each describes something that would either never
happen or happen infinitely. Inside a predicate, a zero duration is merely
pointless rather than wrong, and is left alone.

A schedule duration over a year draws a warning (`wait up to 400d is a very long
wait window`), on the theory that it is more often a typo than an intention.
