Quiet hours design
How Meridian enforces user-defined quiet windows so alerts never fire when you're sleeping, streaming, or in focus mode.
The problem
A detection fires at 3:12 AM. The user is asleep. The alert is correct but the timing is wrong — it erodes trust and trains people to ignore notifications. We needed a scheduling layer that is simple to configure, impossible to accidentally bypass, and cheap to evaluate at scale.
Data model
Each user profile stores an ordered list of quiet-hour windows. Every window has a start time, end time, days-of-week bitmask, and a priority. Windows can overlap — the highest-priority window wins. The default window (priority 0) is always-active, so deleting all custom windows restores 24/7 alerting.
{
"windows": [
{
"id": "sleep",
"start": "22:00",
"end": "07:00",
"days": 127,
"priority": 10
},
{
"id": "focus-block",
"start": "09:00",
"end": "11:30",
"days": 31,
"priority": 20
}
]
}Evaluation path
When an alert is ready to dispatch, the notifier calls a single pure function: isQuiet(now, windows). It converts the current UTC time into the user's configured timezone, checks the day-of-week bit, and walks the window list in priority-descending order. The first window whose interval contains the local time short-circuits the check. If no window matches, the default window returns false — alert proceeds.
Edge cases we handle
- Midnight wraparound: windows like 22:00–07:00 are split into two intervals internally so the comparison stays correct.
- DST transitions: we evaluate against the user's IANA timezone, not a fixed offset, so spring-forward and fall-back gaps are handled by the platform.
- Overlapping windows: priority ordering ensures deterministic behavior. A high-priority focus block inside a sleep window suppresses alerts during the focus block even though sleep would also match.
- Empty window list: treated as always-active. New accounts ship with zero windows and get full alerting by default.
Why it works at scale
The check is O(n) on a list that rarely exceeds five windows. No database queries, no cron jobs, no state machines. The notifier calls isQuiet inline before every dispatch. If the user is in a quiet window, the alert is queued to a deferred bucket and re-evaluated when the window closes. This keeps the hot path under 200 µs and avoids building a separate scheduler service.
Related: Alert deduplication recipe · Rate limiting design