Channels and preferences Required
Hold messages during quiet hours
Hold every non-critical push and text until the recipient's quiet hours end, in the recipient's own time zone.
How to do it
Store an IANA time zone name per user, such as Europe/Stockholm, never a fixed offset, because offsets change with daylight saving. Default to 21:00 to 08:00 and let people change it. The US TCPA allows marketing calls and texts only from 8 a.m. to 9 p.m. local time, and some states are stricter. When you lack a time zone, guess from the phone's country code or the last session, and use the stricter window. Only critical types, such as fraud or safety alerts, skip the hold.
Example
import { Temporal } from '@js-temporal/polyfill';
export function nextAllowedSend(now, timeZone, quiet = { start: 21, end: 8 }) {
const local = now.toZonedDateTimeISO(timeZone);
if (local.hour >= quiet.end && local.hour < quiet.start) return now;
const day = local.hour >= quiet.start ? local.add({ days: 1 }) : local;
return day.with({ hour: quiet.end, minute: 0, second: 0, millisecond: 0 }).toInstant();
}
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
def next_allowed_send(now: datetime, time_zone: str, start: int = 21, end: int = 8) -> datetime:
local = now.astimezone(ZoneInfo(time_zone))
if end <= local.hour < start:
return now
day = local + timedelta(days=1) if local.hour >= start else local
return day.replace(hour=end, minute=0, second=0, microsecond=0)
How to check
A unit test sets the clock to 23:30 in the user's zone and expects the next send time to be 08:00 the next morning. A second test covers a daylight saving change night.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers notifications playbook: https://agentwranglers.se/playbook/notifications/channels-and-preferences/quiet-hours.