TimedSchedule
TimedSchedule(id) :: # (opaque)
Identified appointment spans in source-label order. The window selects
starts; duration may extend beyond it. IDs preserve the series and source.
Start and end interpretation share one zone-work budget per call.
new_with_endings supports source-specific duration or explicit-end overrides.
Example
Use this layer when each recurrence start should become an identified span.
Coordinate measures POSIX microseconds; Calendar applies calendar components,
resolves the end, then adds its coordinate tail. The window selects starts,
not every appointment overlapping that window.
This example supplies a finite UTC rules snapshot. Applications using named zones can pass rules from the optional zone-data package instead. For a complete application with calendar-month durations and an extra booking, see equipment loans.
import time.TimedSchedule
import time.TimedRecurrence
import time.TimedOccurrence
import time.CalendarPattern
import time.GregorianDate
import time.CalendarDate
import time.ClockTime
import time.LocalDateTime
import time.ZoneRules
import time.FixedOffset
import time.PosixSpan
import time.PosixBoundary
import time.PosixDelta
expect {
date = GregorianDate.from_fields({ year: 1970, month: 1, day: 1 })?
end_date = GregorianDate.from_fields({ year: 1970, month: 1, day: 4 })?
clock = ClockTime.from_microseconds_since_midnight(0)?
start = LocalDateTime.new(CalendarDate.from_gregorian(date), clock)
end = LocalDateTime.new(CalendarDate.from_gregorian(end_date), clock)
validity = PosixSpan.from_seconds(-1, 345600, RejectSubmicrosecond)?
rules = ZoneRules.new_bounded(
"UTC", "fixed", validity,
FixedOffset.from_seconds(0), [],
{ minimum: 0, maximum: 0 },
)?
rule = TimedRecurrence.new({ date, clock }, {
calendar: CalendarPattern.defaults(Daily),
clocks: { hours: [], minutes: [], seconds: [] },
termination: Count(2),
by_set_pos: [],
})?
duration = Coordinate(PosixDelta.from_microseconds(3600000000))
schedule = TimedSchedule.new(
42.U64, rule, { start, end }, duration,
{ rules, occurrence: RequireUnique, gap: RejectGap },
)?
work = {
max_steps: 100, max_buffered: 1,
max_zone_segments: 10, max_zone_candidates: 1,
}
result = TimedSchedule.collect(schedule, {
work, max_occurrences: 3,
})
match result {
Err(_) => Bool.False
Ok(batch) => match batch.status {
Complete => batch.occurrences.len() == 2
Limited(_) => Bool.False
}
}
}
When a batch returns Limited, process its partial output and resume the returned
cursor with sufficient budgets. Increasing only the output cap does not fix a
work or buffer limit.
new_with_overrides : id, TimedRecurrence, Window, Duration, List(Override), Context -> Try(TimedSchedule(id), [InvalidDuration, EmptyWindow, ReversedWindow, OutOfRange, TooManyOverrides, ConflictingDuration(LocalDateTime), ..])
Override durations at original source positions, including explicit inclusions. This does not add starts: configure those on the rule. Exclusions still win; overrides do not replenish COUNT or change IDs. At most 4096 inputs, sorted once in O(n log n), then O(log n) lookup. Identical definitions coalesce; conflicting definitions at one position return ConflictingDuration, even outside the query or at excluded starts. Calendar and coordinate definitions remain distinct despite equal widths.
new_with_endings : id, TimedRecurrence, Window, Duration, List(EndOverride), Context -> Try(TimedSchedule(id), [InvalidDuration, EmptyWindow, ReversedWindow, OutOfRange, TooManyOverrides, ConflictingEnding(LocalDateTime), ..])
Override a source with a duration or explicit end, without adding starts. The same 4096-entry limit and positional lookup apply. Equal definitions coalesce; different definitions at one position return ConflictingEnding. Endpoint order is checked after the start and end are interpreted.
At most one start advancement and one end advancement. A pending end resumes before consuming another start. Steps count source-candidate work; constructing a duration adds only bounded calendar arithmetic. Start buffers and the pending end's zone candidates have separate caps.
Output capacity returns Limited without looking ahead. Candidate and zone budgets are totals across every start and end in this batch.
End, errors and Limited are terminal items; resume a limited cursor explicitly with new budgets. Each advancement uses the supplied limits.
to_inspect : TimedSchedule(id) -> Str
EndOverride : { source : LocalDateTime, ending : Ending }
Override : { source : LocalDateTime, duration : Duration }
Limits : Limits
Limit : [StartWorkLimit, StartBufferLimit, StartZoneWorkLimit, StartZoneBufferLimit, EndZoneWorkLimit, EndZoneBufferLimit, OutputLimit]
Error : [InvalidDuration, OutOfRange, OutsideValidity, InvalidDestination(Fields), Gap, Ambiguous, AmbiguousGap, OffsetConflict, UnsynchronizedStart]