Agent Wranglers

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.