Skip to content

Periods

Find the stretch of working time at, after or before an instant, with next_period and previous_period.

A period is a maximal stretch of working time, written [start, end): it includes start but not end. next_period and previous_period return the period at, after or before an instant; JavaScript names them nextPeriod and previousPeriod. This page covers Rust, Python and JavaScript.

Which period is returned

  • next_period(t) returns the period that contains t, starting at t. If t is outside working time, it returns the first period that starts after t. A period that ends at t does not contain it.
  • previous_period(t) returns the period that contains the instant just before t, ending at t. Otherwise it returns the last period that ends before t. A period that starts at t is skipped.
  • A period is one stretch of working time, whichever rules produce it. An overnight shift is one period across midnight, and shifts that touch or overlap form one period. Breaks and holidays split working time into separate periods.

The London support desk from the README works 09:00–17:00 Monday to Friday in Europe/London, with a break from 12:00 to 13:00, and is closed on 25 and 28 December 2026. Each [====) is one period, in London time:

              09:00            12:00   13:00            17:00
Thu 24 Dec    [================)       [================)
Fri 25 Dec    holiday
Sat 26 Dec    weekend
Sun 27 Dec    weekend
Mon 28 Dec    holiday
Tue 29 Dec    [================)       [================)

From 11:00 on Tuesday 29 December, next_period returns 11:00–12:00: the morning period, starting at 11:00. From 09:00 on Tuesday, previous_period skips the period that starts at 09:00 and returns 13:00–17:00 on Thursday 24 December, past both holidays and the weekend.

Return values

LanguageMethodReturns
Rustworkclock::Schedule::next_period, previous_periodResult<std::ops::Range<jiff::Timestamp>, workclock::Error>
Pythonworkclock.Schedule.next_period, previous_periodA (start, end) tuple of datetimes in the tzinfo of the datetime passed in
JavaScriptSchedule.nextPeriod, previousPeriodA Period, { start, end }, of two Dates

Find the next and previous period

use jiff::civil::{date, time};
use jiff::Zoned;
use workclock::{Break, Holidays, Schedule, Weekdays, WorkDay};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let days = Weekdays::MON_TO_FRI;
    let support = Schedule::builder()
        .time_zone("Europe/London")
        .rule(WorkDay::new(time(9, 0, 0, 0), time(17, 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 now: Zoned = "2026-12-29T11:00[Europe/London]".parse()?;
    let next = support.next_period(&now)?;
    assert_eq!(next.start.to_string(), "2026-12-29T11:00:00Z");
    assert_eq!(next.end.to_string(), "2026-12-29T12:00:00Z");

    let morning: Zoned = "2026-12-29T09:00[Europe/London]".parse()?;
    let previous = support.previous_period(&morning)?;
    assert_eq!(previous.start.to_string(), "2026-12-24T13:00:00Z");
    assert_eq!(previous.end.to_string(), "2026-12-24T17:00:00Z");
    Ok(())
}

London's offset from UTC is zero in December, so the UTC instants in the Rust and JavaScript tabs are also London wall-clock times.

Long periods and errors

Every search stops at the search bound: 366 days, plus seven days per holiday date in the schedule.

  • Round-the-clock schedules. A period is capped at the search bound. next_period returns at most the bound from the period's start, and previous_period at most the bound up to its end. Only schedules that work round the clock have periods that long (P19–P21).
  • No working time. next_period fails with the no-working-time error if no period starts within the search bound after t, and previous_period if none ends within the bound before t (P22, P23). That is workclock::Error::NoWorkingTime in Rust and NoWorkingTimeError in Python and JavaScript.
  • End of the supported range. If the search reaches the end of the supported range (the start, for previous_period) before finding working time, the result is the out-of-range error: workclock::Error::OutOfRange, or OutOfRangeError in Python and JavaScript (P24). A period that runs into either end of the range stops there.

Errors lists every error class in each language.

Edge cases

These are the period examples from DESIGN.md. Each ID is a case in tests/fixtures/design_examples.json, which the Rust, Python and JavaScript test suites all run. Unless noted, dates are in the week of Monday 5 January 2026, times are UTC, and 9–17 Mon–Fri is a WorkDay from 09:00 to 17:00, Monday to Friday.

IDScheduleQueryResultShows
P19–17 Mon–Frinext_period(Mon 10:00)Mon 10:00–17:00Inside a period, the result starts at t
P2sameprevious_period(Mon 10:00)Mon 09:00–10:00Inside a period, the result ends at t
P3samenext_period(Sat 10:00)Mon 2026-01-12 09:00–17:00Outside working time, the first period after t
P4sameprevious_period(Sat 10:00)Fri 09:00–17:00Outside working time, the last period before t
P5samenext_period(Mon 09:00)Mon 09:00–17:00A period that starts at t contains it
P6samenext_period(Mon 17:00)Tue 09:00–17:00A period that ends at t does not contain it
P7sameprevious_period(Mon 17:00)Mon 09:00–17:00A period that ends at t is returned whole
P8sameprevious_period(Tue 09:00)Mon 09:00–17:00A period that starts at t is skipped
P922–06 Mon–Frinext_period(Mon 23:00)Mon 23:00–Tue 06:00An overnight shift is one period across midnight
P10sameprevious_period(Tue 02:00)Mon 22:00–Tue 02:00The same, searching back
P1100–00 Mon–Frinext_period(Sat 10:00)Mon 2026-01-12 00:00–Sat 2026-01-17 00:0024-hour shifts that touch form one period
P129–17 Mon–Fri and 13–20 Mon–Frinext_period(Mon 10:00)Mon 10:00–20:00Overlapping rules form one period
P139–17 Mon–Fri, break 12–13next_period(Mon 12:30)Mon 13:00–17:00From inside a break, the period after it
P14sameprevious_period(Mon 12:30)Mon 09:00–12:00From inside a break, the period before it
P159–17 Mon–Fri, holiday Wed 2026-01-07next_period(Tue 17:00)Thu 09:00–17:00A holiday is skipped
P16sameprevious_period(Thu 09:00)Tue 09:00–17:00The same, searching back
P1722–06 Mon–Fri, holiday Tue 2026-01-06next_period(Mon 22:00)Mon 22:00–Tue 00:00A holiday cuts an overnight shift at midnight
P18samenext_period(Tue 00:00)Wed 00:00–06:00The part of a holiday's shift after the next midnight is kept
P1900–00 Mon–Sunnext_period(Mon 10:00)Mon 10:00–Wed 2027-01-06 10:00 (366 days)A period is capped at the search bound
P2000–00 Mon–Sun, holiday Thu 2027-01-14next_period(Mon 00:00)Mon 00:00–Wed 2027-01-13 00:00 (373 days)One holiday date adds seven days to the bound
P21sameprevious_period(Mon 2028-01-24 00:00)Sat 2027-01-16 00:00–Mon 2028-01-24 00:00 (373 days)The cap applies searching back too
P229–10 every day Etc/GMT+3, break 9–10 every day America/Sao_Paulonext_period(Fri 2021-01-01 00:00)No-working-time errorNo period starts within the search bound
P23sameprevious_period(Fri 2021-01-01 00:00)No-working-time errorNo period ends within the search bound
P249–17 Mon–Frinext_period(Thu 9999-12-30 18:00)Out-of-range errorThe search reaches the end of the supported range

In P20 and P21 each period would otherwise run one day past the bound. P22's schedule builds because the break missed the shift while São Paulo kept summer time; since Brazil dropped it in 2019, the break covers the whole shift.

See also

Report a problem with this pageAsk a question in Discussions

On this page