> Section: [4. Configuration](https://jaque.sh/docs/config/cue-in-practice.md)
> Next: config/hot-reload
> Index: https://jaque.sh/llms.txt


A timeperiod is a named schedule: a set of weekly ranges, a handful of
date exceptions for the days that do not follow the weekly pattern, and
an optional list of other periods to exclude from it. Four things in a
config can reference one by name: when a check runs, when a policy may
notify, when an escalation level may notify, and when a contact is
reachable.

## 1. What a timeperiod is

A timeperiod carries a name, a timezone, a map of ranges, and an
`exclude` list.

```cue
timezone: "Europe/Madrid"

timeperiods: {
	office: {
		ranges: {
			monday:       "09:00-17:00"
			tuesday:      "09:00-17:00"
			wednesday:    "09:00-17:00"
			thursday:     "09:00-17:00"
			friday:       "09:00-17:00"
			"2026-12-25": "00:00-00:00"
			"january 1":  "00:00-00:00"
		}
	}
	maintenance: {
		timezone: "UTC"
		ranges: {"day 1": "02:00-04:00"}
	}
	office_no_maint: {
		ranges:  office.ranges
		exclude: ["maintenance"]
	}
}
```

Each key under `ranges` is a date expression; its value is a time list
for the days that expression selects. `exclude` names other timeperiods
whose open time is carved out of this one, the way `maintenance` above
closes `office_no_maint` for two hours on the first of every month even
though `office`'s weekly ranges say nothing about it.

## 2. The range syntax

Every key is one of six forms, written the way an operator coming from
Nagios already writes them.

| Form | Example | Selects |
|---|---|---|
| a weekday | `monday` | that weekday, every week |
| a calendar date | `2026-12-25`, `2026-01-01 - 2026-01-10 / 3` | one date, or a date span, with an optional stride |
| a month day | `july 4`, `july 4 - july 10 / 2` | a day of a fixed month, or a span across months with a stride |
| a day of the month | `day 21`, `day 1 - 15 / 2`, `day -1` | a day number counted from the start or, negative, from the end of the month |
| a nth weekday of a month | `monday 3 july`, `monday 1 july - friday 2 july` | the nth occurrence of a weekday in a month |
| a nth weekday, any month | `monday 3`, `monday 1 - friday 3 / 2`, `monday -1` | the nth occurrence of a weekday in every month |

Each key's value is a time list: comma-separated `start-end` pairs in
24-hour `HH:MM`, unpadded hours allowed, `end` may be `24:00`. A pair does
not wrap past midnight, so an overnight shift is two pairs on consecutive
days: `22:00-24:00` under one date or weekday key and `00:00-06:00` under
the next. An empty value or `00:00-00:00` closes the day, which is how a
single date in the middle of an otherwise open week is turned into a
holiday. `/ n` after a
span, where a form allows one, keeps the span's first day and then every
n-th day after it; negative day numbers and negative occurrence counts
count from the end.

## 3. How a day resolves

For a given civil date, the dated forms are checked before the weekly
one, in a fixed order: calendar date, then month day, then day of the
month, then nth weekday of a month, then nth weekday of any month. The
first form whose key matches the date wins, and its time list is the
day's ranges; within one kind of form, the rules are consulted in the
lexical order of the `ranges` keys, since a CUE map has no declaration
order. If nothing dated matches, the weekday rule for that day applies.
If nothing matches at all, the day is closed. A date inside an excluded
timeperiod is closed regardless of what its own ranges say.

## 4. Zones and daylight saving

Every timeperiod reads its ranges in an IANA zone. The top-level
`timezone` sets the default every timeperiod inherits; a timeperiod's
own `timezone` overrides it for that one period. The default is
`"Local"`, the process's own zone, because that is the value Nagios
used and a config carried over should keep behaving as it did. A
cluster whose engines run in different zones should set the top-level
`timezone` explicitly rather than rely on each engine's own clock to
agree.

Times inside a timeperiod are wall-clock, so a spring-forward gap and a
fall-back repeat both need a rule. A range whose start falls inside a
gap that daylight saving removes begins at the first instant after the
gap. A range whose start falls in an hour that daylight saving repeats
begins at the first occurrence of that wall-clock time.

## 5. The four references

- `check_period` on a check. Outside it, the next check is deferred to
  the period's next open instant rather than run: nothing runs and
  nothing wakes up to find the period still closed. A forced recheck
  ignores it and runs anyway.
- `period` on a notification policy. Outside it, the policy is
  suppressed with `OUTSIDE_PERIOD`.
- `period` on an escalation level. Outside it, that level is suppressed
  with `OUTSIDE_PERIOD`.
- `notification_period` on a contact. Outside it, the contact is
  dropped from whichever level would have notified it.

Each of the four takes a timeperiod name and defaults to empty, which
means always: a check with no `check_period` runs on its own schedule
regardless of the calendar, and a policy, level or contact with no
period assigned is never gated by one.
