Delivery ETAs
Find when an order is dispatched by adding a handling time to the time it was placed, counting only the warehouse's operating hours, with add.
A warehouse dispatches each order 6 working hours after it is placed. When does an order placed now leave the building? This guide answers it with add, in Rust, Python and JavaScript.
The schedule
A warehouse in London is open 08:00–18:00 on weekdays, stops for lunch, and is closed on 25 and 28 December 2026. As rules, in the Europe/London time zone:
WorkDay: 08:00–18:00, Monday to Friday.Break: 12:00–13:00, Monday to Friday.Holidays: Friday 25 and Monday 28 December 2026.
Handling takes 6 hours of operating time. One order is placed at 15:00 on Thursday 24 December 2026, London time, and another at 10:00 on Saturday 26 December, when the warehouse is closed.
Find the dispatch time
use jiff::civil::{date, time};
use jiff::{SignedDuration, Zoned};
use workclock::{Break, Holidays, Schedule, Weekdays, WorkDay};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let days = Weekdays::MON_TO_FRI;
let warehouse = Schedule::builder()
.time_zone("Europe/London")
.rule(WorkDay::new(time(8, 0, 0, 0), time(18, 0, 0, 0), days))
.rule(Break::new(time(12, 0, 0, 0), time(13, 0, 0, 0), days))
.rule(Holidays::new([date(2026, 12, 25), date(2026, 12, 28)]))
.build()?;
let handling = SignedDuration::from_hours(6);
let placed: Zoned = "2026-12-24T15:00[Europe/London]".parse()?;
let dispatch = warehouse.add(&placed, handling)?;
assert_eq!(dispatch.to_string(), "2026-12-29T11:00:00Z");
let saturday: Zoned = "2026-12-26T10:00[Europe/London]".parse()?;
let dispatch = warehouse.add(&saturday, handling)?;
assert_eq!(dispatch.to_string(), "2026-12-29T15:00:00Z");
Ok(())
}The result
The order placed at 15:00 on Thursday 24 December is dispatched at 11:00 on Tuesday 29 December 2026, London time:
Date, Europe/London | Operating time used |
|---|---|
| Thursday 24 December, 15:00–18:00 | 3 hours |
| Friday 25 December | None: a holiday |
| Saturday 26 and Sunday 27 December | None: not working days |
| Monday 28 December | None: a holiday |
| Tuesday 29 December, 08:00–11:00 | 3 hours |
- Thu 24
- Fri 25
- Sat–Sun
- Mon 28
- Tue 29
- Wed 30
The order placed at 10:00 on Saturday 26 December starts counting when the warehouse next opens, at 08:00 on Tuesday 29 December. It uses 08:00–12:00 and 13:00–15:00, and is dispatched at 15:00 that day.
For a delivery leg with different hours, such as a courier that works on Saturdays, build a second schedule for the courier and add its time to the dispatch instant.
Edge cases
- An order that uses up the morning is dispatched at 12:00, when the lunch break starts, not at 13:00: 4 hours from 08:00 on Tuesday end at 12:00 (DESIGN.md A2, Adding time: Where the result lands).
- An order placed during the break starts counting at 13:00: 1 hour from 12:30 ends at 14:00 (DESIGN.md K4).
- Different hours on Saturdays. Add a second
WorkDayrule for Saturday with its own hours; working time is the union of allWorkDayrules (Schedules). - Holidays. Each date in
Holidaysremoves 00:00 to the next 00:00 in the schedule's time zone (Rules: Holidays). - Daylight saving time (DST). Operating hours are wall-clock times, so 08:00 stays 08:00 London time after the clocks change. An order placed at 15:00 on Friday 27 March 2026 is dispatched at 11:00 on Monday 30 March, London time (BST), which is 10:00 UTC (Time zones and DST).
What 0.1.1 does not do (DESIGN.md: Scope):
- Lead times in business days, such as "delivered in 2 business days". workclock adds working time, not days.
- Different hours on one date, such as closing early on Christmas Eve. Operating hours repeat by weekday, with no per-date overrides. A whole closed day is a holiday.
See also
- Adding time: what
addandsubreturn. - Support SLAs: the same
add, withbetweenfor the time left. - Batch operations: dispatch times for many orders in one call.
- Errors: what
addraises when there is no working time within the search bound.
Report a problem with this pageAsk a question in Discussions