Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Workflow

ServiceHours (SLA-accrual windows)

entity ServiceHours { string Name; Zone Zone; ServiceWindow[] Windows; ServiceException[] Exceptions; }

A schedule the SLA clock accrues within — the platform WALKS its weekly windows (and holiday exceptions) to advance ticks and compute deadlines. The platform defines the shape; the app fills the rows (BusinessHours, AroundTheClock) and assigns which one governs an instance. No windows ⇒ the identity schedule (ticks == wall-clock).

stable1 example compiled by CIworkflowtype

Summary#

ServiceHours is the window an SLA clock accrues within. The platform's tick-accrual engine walks a schedule's weekly Windows (and any holiday Exceptions) to advance SLA time and compute deadlines — so a ticket on a business-hours schedule simply doesn't burn its clock at night, while one on a 24/7 schedule accrues around the clock. The shape is platform (the engine must walk it), the rows are the app's (it seeds BusinessHours / AroundTheClock, assigns which one governs an instance, and owns everything about the SLA's meaning).

Signature#

public entity ServiceHours {
  string Name;
  Zone   Zone;                    // REQUIRED — the zone the windows' local times are read in (DST-correct)
  [ForeignKey(ServiceHours)] ServiceWindow[]    Windows;     // recurring weekly open windows — Mon 09:00–17:00, …
  [ForeignKey(ServiceHours)] ServiceException[] Exceptions;  // holiday / special-hours date ranges
}

public entity ServiceWindow {
  ServiceHours ServiceHours;
  DayOfWeek Day;                  // the C# built-in enum (Sunday = 0 … Saturday = 6)
  TimeSpan  Start;  TimeSpan End; // local time-of-day, as a TimeSpan from midnight
}

public entity ServiceException {
  ServiceHours ServiceHours;
  DateTime From;  DateTime To;
  bool     IsClosed;              // the clock does not accrue across From..To
  string   Reason;
}

Description#

ServiceHours is ordinary app data (a data-DB entity), editable through the app's own UI — not a declaration block. An app seeds the schedules it needs and points an instance at one (typically snapshotted in the workflow's Start { } from a contract/severity matrix). The platform reads the rows and walks them; it never defines them.

  • Windows are recurring weekly intervals in local time. The parent's Zone gives them a DST-correct instant (a 09:00 Stockholm window is a different UTC instant in summer and winter). A schedule with no windows is the identity schedule — ticks equal wall-clock — which is the 24/7 / "AroundTheClock" case.
  • Zone is required. An SLA schedule must always state the zone its hours are measured in, so a row can never be saved ambiguous — the platform rejects it at commit (UI edit or seed), not just at compile. A 24/7 schedule names a zone too (Zone = "UTC"); it's inert there (with no windows the walk never reads it) but keeps every schedule self-documenting.
  • Exceptions are date-range overrides applied on top of the windows. An IsClosed exception (a public holiday) removes accrual across its From..To; the Reason is for humans.
  • DayOfWeek is the C# built-in enum, so w.Day == DayOfWeek.Monday reads and stores exactly as in C#.

Binding it to a workflow. A workflow names the schedule its clocks accrue within with a ServiceHours = <expr>; setting (a value over this.Item resolving to a ServiceHours ref, typically snapshotted onto the entity in Start). Every SLA clock on the run — a state's Expire, a milestone's Within, a reminder — then advances only inside that schedule's windows, so a deadline computed from a 4-hour budget lands after the intervening nights and weekends, not 4 wall-clock hours later. With no ServiceHours binding a run uses the identity schedule (24/7).

workflow SupportTicket {
  ServiceHours = this.Item.ServiceHours;   // the schedule this run's clocks walk
  Accrues      = [Open, Working];          // and the states in which they run at all
  ...
}

Two schedule-shaped things, deliberately split. ServiceHours is when the SLA clock accrues — the customer's promise window. It is not availability (who is on shift): rosters, leave, and follow-the-sun live entirely in the app and the platform never consults them. The promise (ServiceHours) and the people (availability) are different concerns with different owners.

Examples#

enum OrderState { Open, Done }

[Principal]
entity Person {
  [Required, MaxLength(200)] string Email;
  security { allow read, create when IsAuthenticated; }
}

entity Order {
  [Required, MaxLength(60)] string Reference;
  OrderState Status;                       // no default: the workflow owns this field
  security { allow read, create, update when IsAuthenticated; }
}

// The schedule entities ship with the platform; an app that reaches them says who may.
partial entity ServiceHours   { security { allow read, create when IsAuthenticated; } }
partial entity ServiceWindow  { security { allow read, create when IsAuthenticated; } }

// Two schedules: one that accrues around the clock, one that only counts business hours.
void SeedSchedules() {
  new ServiceHours { Name = "AroundTheClock", Zone = Zone.Of("UTC") };          // no windows ⇒ 24/7

  var biz = new ServiceHours { Name = "BusinessHours", Zone = Zone.Of("Europe/Stockholm") };
  new ServiceWindow { ServiceHours = biz, Day = DayOfWeek.Monday,
                      Start = TimeSpan.FromHours(9), End = TimeSpan.FromHours(17) };
}

Seed a 24/7 schedule and a Mon–Fri business-hours one with a holiday:

var aroundTheClock = new ServiceHours { Name = "AroundTheClock", Zone = "UTC" };   // no windows ⇒ accrues 24/7
var bizHours = new ServiceHours { Name = "BusinessHours", Zone = "Europe/Stockholm" };
foreach (var d in [DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday]) {
  new ServiceWindow { ServiceHours = bizHours, Day = d, Start = TimeSpan.FromHours(9), End = TimeSpan.FromHours(17) };
}
new ServiceException { ServiceHours = bizHours, From = new DateTime(2026, 6, 19), To = new DateTime(2026, 6, 19),
                       IsClosed = true, Reason = "Midsommarafton" };

See also#

  • Assigned / Finished (milestones) — the SLA clock whose ticks accrue within these windows
  • <span class="planned" title="this page is planned and not written yet">workflow-state</span> — Accrues names the states in which the clock runs
  • subscribe — the slot whose promise the clock measures

Related

Assigned / Finished (milestones)

A milestone puts an SLA on a slot's progress — Assigned (someone must PICK IT UP within Within) and Finished (it must…

subscribe

Declares that a workflow state waits on an event, and configures the wait — who may hold it, who may hand it on…