How AI scheduling handles overnight shifts (and why most don't)
A shift from 8pm to 4am crosses midnight. That sentence breaks more scheduling tools than any other. Here's what goes wrong, and what we do differently.
A bartender clocks in at 8pm Friday and clocks out at 4am Saturday. To you that's one shift — a normal Friday night. To most scheduling tools that's a parsing exception, a validation error, or worse: two phantom shifts that look right on the calendar and break later in payroll.
We hit this early at Weekwright and decided to fix it properly. Here's what overnight shifts do to the data model and what we changed.
The naive model: endTime > startTime
Most schedulers store shifts as a date plus two times — say, 2026-04-22, 08:00, 16:00. Validation: the end time must be after the start time. Worked for centuries.
Then the bar shift comes in: 2026-04-22, 20:00, 04:00. End time is before start time. Three things tend to happen:
- The form rejects it. Manager has to split it into
20:00–23:59and a new shift00:00–04:00. Now you have two database rows for one shift, plus a Saturday-morning shift that nobody actually works. - The form accepts it but stores it wrong. Some tools store it literally and the duration calculation comes out as "-16 hours." Compliance checks fail. Payroll reads negative hours.
- The form silently rolls the date forward. The UI shows it on Friday but the database row has Saturday. Reports based on database dates disagree with what the calendar shows.
What we did instead
We treat endTime ≤ startTime as a valid signal that the shift wraps midnight. The shift stays as a single row tied to its start date; duration is computed correctly; the calendar renders it as one block that visually crosses the midnight line.
Internally, every shift has a wallclock start and end, plus an IANA timezone (the location's timezone, since multi-location orgs span them). When we need actual UTC instants — for compliance checks, payroll exports, calendar overlap detection — we compute them at read time using a helper that handles the midnight wrap:
function wallclockRangeToUtc(
date: string, // "2026-04-22"
startTime: string, // "20:00"
endTime: string, // "04:00"
timezone: string, // "America/New_York"
): { startUtc: Date; endUtc: Date } {
// ... endUtc gets +1 day when endTime <= startTime
}Single function, single source of truth. Conflict detection, compliance rules, weekly hour totals — they all consume the same UTC range and never have to special-case midnight.
Why the AI matters here
AI scheduling has to understand overnight shifts to draft them correctly. When a manager types "cover Friday and Saturday nights with bartenders," the agent needs to know:
- Saturday night = Saturday evening to Sunday early morning, not Friday night to Saturday morning.
- A bartender who closes Friday at 4am Saturday hasn't had 11 hours of rest by 9am Saturday — so don't schedule them on Saturday morning prep.
- Hours worked between midnight and 6am may trigger different pay rules in some jurisdictions (NYC fast-food "clopening" premium, for example).
Our agent reasons about all of these because the underlying model preserves the actual shift structure. Tools that flatten overnight shifts into two rows lose that structure — and then their AI features start drifting toward fiction.
What this means for you as a manager
If you're evaluating scheduling tools and you have any overnight component to your business — restaurants, healthcare, manufacturing, hospitality — test the overnight case explicitly:
- Create a shift from 8pm to 4am the next day.
- Check the duration the tool computes (should be 8 hours).
- Check the rest-period rule against a 9am next-day shift (should warn or block).
- Export to CSV / payroll and verify the hours total correctly.
If any of those break, it's a tell. The tool doesn't understand your shift structure, and bigger problems are downstream.