Sending a campaign at nine in the morning sounds like a one-line feature and turns into a queue design problem the first time a customer in a different hemisphere asks why their mail arrived at three in the morning.
The schedule is per recipient
A campaign is not one scheduled job, it is one job per recipient, each with its own target instant. Storing a single send_at and filtering at delivery means every recipient gets the same instant. Storing the local time plus a timezone is the only shape that survives daylight saving changes.
Timezones change under you
If you convert to UTC at creation, a campaign scheduled for the week after a DST transition lands an hour off. Store the IANA timezone name and the local wall-clock time, and resolve to UTC at delivery time when the rules are current.
What about people who did not tell you
Their timezone is a guess from the IP at last activity or from the country on the account. Guessing wrong by a few hours is usually fine; guessing wrong by twelve is not. Cap the window: only deliver between eight and twenty in local time, and if the target falls outside, move to the nearest edge.
The detail that saves a support ticket
Record the resolved UTC instant and the zone used, per recipient. When a customer says the mail came at the wrong hour, you can show the exact conversion instead of arguing about it.
Volanea stores schedules as local wall-clock plus IANA zone and resolves at delivery, which is the only version that survives a DST change.
Top comments (0)