Quiet hours and timezones
Quiet hours are local HH:MM times in the user’s own timezone and may cross midnight:
$prefs = Notifications::preferences($user);
$prefs->quietHours('22:00', '07:00'); // local HH:MM, may cross midnight; equal times are refused
$prefs->quietHours(null); // clears them
$prefs->quietHoursPreference(); // the user's, else notifications.quiet_hours.default, else null
$prefs->timezone('Europe/Bratislava'); // an IANA id; null clears it
$prefs->resolvedTimezone();// config/notifications.php — for users without their own
'quiet_hours' => [
'enabled' => true,
'default' => ['start' => '22:00', 'end' => '07:00'],
],- A non-urgent mail created while quiet is held until the window ends (NotificationMailHeld fires).
- Urgent types are never held. Mandatory means “cannot mute”, not “wakes you up”: a mandatory non-urgent type is held.
- A held or trailing mail is checked again at delivery and held again if quiet hours started meanwhile.
- A digest slot inside quiet hours moves to their end.
- DST: a local end that does not exist (spring forward) moves forward (02:30 → 03:30); an ambiguous one takes the first occurrence.
- notifications.quiet_hours.enabled is the global switch.
Timezone resolution
The default resolver (notifications.timezone.resolver) takes the first valid IANA id of:
- the recipient’s HasNotificationTimezone::notificationTimezone();
- the stored preference ($prefs->timezone('Europe/Bratislava'));
- the attribute notifications.timezone.attribute (timezone; blank disables it), read only when present in the attributes — safe on strict-mode hosts;
- config('app.timezone').
An invalid id is skipped and logged once per recipient per process; it never throws at delivery. Bind your own ResolvesTimezone to read it elsewhere:
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Notifications\Contracts\ResolvesTimezone;
final class WorkspaceTimezoneResolver implements ResolvesTimezone
{
public function timezone(Model $recipient): string
{
return $recipient->workspace?->timezone ?? config('app.timezone');
}
}
// config/notifications.php
'timezone' => ['resolver' => App\Notifications\WorkspaceTimezoneResolver::class, 'attribute' => 'timezone'],Locale
Mail renders in the recipient’s preferredLocale(); inbox titles render in the viewer’s request locale, with a snapshot in the recipient’s locale. There is no separate locale preference.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.