Skip to content

Calendars

Calendars describe when work is normally allowed. Event Scheduler uses them to mark closed cells, validate drag/create targets, skip non-working days during rollover, create outside-availability conflicts, and resolve the calendar that applies to a resource.

Use calendars for repeating business rules such as weekday schedules, weekend crews, holiday closures, and resource-specific operating hours. Use availability entries for dated working windows or exceptions such as one-off maintenance, PTO, blocked rooms, or breaks.

For detailed calendar-vs-availability precedence, including how dated working windows override calendar fallback checks, see the Scheduling Rules overview.

Calendars do not automatically reject every edit outside working hours. They provide the working-hours model and produce outside-availability conflicts. To make those conflicts block create, move, resize, and edit mutations, configure the conflict rule as an error:

grid.eventScheduler = {
view: 'week',
weekStartDate: '2026-06-08',
calendars: {
primaryCalendarId: 'weekday',
calendars: [weekdayCalendar],
},
conflicts: {
enabled: true,
rules: { 'outside-availability': 'error' },
},
};

Event Scheduler uses the same CalendarEntity shape as Gantt, so calendar definitions can be shared between planning views:

import {
DEFAULT_CALENDAR,
EventSchedulerPlugin,
type CalendarEntity,
} from '@revolist/revogrid-enterprise';
const weekdayCalendar: CalendarEntity = {
...DEFAULT_CALENDAR,
id: 'weekday',
name: 'Weekday coverage',
timeZone: 'UTC',
workingDays: [1, 2, 3, 4, 5],
holidays: ['2026-06-19'],
hoursPerDay: 8,
workingHours: { start: '08:00', end: '18:00' },
};
grid.plugins = [EventSchedulerPlugin];
grid.eventScheduler = {
view: 'week',
weekStartDate: '2026-06-08',
calendars: {
primaryCalendarId: 'weekday',
calendars: [weekdayCalendar],
},
};

DEFAULT_CALENDAR is a Monday-Friday UTC calendar with no holidays and hoursPerDay: 8. Spread it when you want the standard defaults and only need to change the id, label, hours, or holidays.

Add calendars through eventScheduler.calendars:

grid.eventScheduler = {
view: 'resourceTimeline',
weekStartDate: '2026-06-08',
timeRange: { start: '06:00', end: '22:00' },
calendars: {
enabled: true,
primaryCalendarId: 1001,
className: 'scheduler-closed-cell',
calendars: [
{
...DEFAULT_CALENDAR,
id: 1001,
name: 'Hospital weekday calendar',
workingHours: { start: '08:00', end: '18:00' },
},
{
...DEFAULT_CALENDAR,
id: 1002,
name: 'Weekend coverage',
workingDays: [5, 6, 7],
workingHours: { start: '10:00', end: '20:00' },
},
],
},
};

Calendar ids can be strings or numbers. Internally the scheduler normalizes ids for lookup, so primaryCalendarId, resource calendarId, and entries in the calendars array should refer to the same logical id.

Each resource can point at its own calendar:

grid.eventSchedulerResources = [
{ id: 'alex', name: 'Alex Kim', role: 'RN', calendarId: 1001 },
{ id: 'weekend-team', name: 'Weekend team', role: 'RN', calendarId: 1002 },
];

The resolver order is:

  1. resource.calendarId
  2. calendars.resourceCalendarId(resource)
  3. calendars.primaryCalendarId

Use resourceCalendarId when the calendar comes from metadata or an external rules table:

grid.eventScheduler = {
view: 'resourceTimeline',
weekStartDate: '2026-06-08',
calendars: {
primaryCalendarId: 'weekday',
calendars,
resourceCalendarId: (resource) =>
resource.metadata?.employmentType === 'weekend' ? 'weekend' : undefined,
},
};

Calendars are applied during projection and interaction:

  • Closed days and holidays receive the calendar closed state.
  • Slots outside workingHours are marked as outside working time.
  • Events placed outside the resolved calendar produce an outside-availability conflict.
  • Drag, resize, and create previews use the configured calendar when finding valid target dates.
  • Moving an event past the bottom of a day can roll to the next schedulable day instead of stopping on a closed weekend or holiday.
  • Conflict and permission hooks still run after calendar checks; calendars do not bypass app-level validation.
  • Blocking is controlled by eventScheduler.conflicts, usually with rules: { 'outside-availability': 'error' } or policy: 'prevent'.

Calendar cells expose the same availability-style state used by the scheduler renderer. Use className on the calendars config for product-specific closed-cell styling:

.scheduler-closed-cell {
background: #f4f6f8;
color: #8b929c;
}

workingHours can be a single range or multiple ranges:

const splitShiftCalendar: CalendarEntity = {
...DEFAULT_CALENDAR,
id: 'split-shift',
name: 'Split shift',
workingHours: [
{ start: '07:00', end: '11:00' },
{ start: '15:00', end: '21:00' },
],
};

The scheduler treats a slot as working when it overlaps at least one configured working-hours range. If workingHours is omitted, every visible time slot on a working day is treated as working.

An event is considered outside availability when its visible segment is not covered by a working calendar or by matching eventSchedulerAvailability entries. With the default conflict policy, that state is shown as a warning. With rules: { 'outside-availability': 'error' }, the scheduler rejects the mutation before it updates eventSchedulerEvents.

nonWorkingTime.workingHours is different: it is a visual fallback for closed cells. Use it for simple background styling when you do not need calendar/resource rules. Use calendars or eventSchedulerAvailability when outside-working-hours edits must participate in conflict detection.

Calendars are recurring operating rules. Availability is dated operational data. They are not mutually exclusive: the scheduler can use a calendar as the baseline and dated availability as the more specific rule for a particular resource/date.

Use calendars for:

  • Standard weekday business hours
  • Weekend-only teams
  • Region-specific public holidays
  • Resource calendars copied from workforce or Gantt planning data

Use availability for:

  • Extra dated working windows
  • One-time room maintenance
  • PTO or sick leave
  • Lunch breaks that vary by date
  • Temporary blocked equipment

When both are present, availability is evaluated first for visible slot state. Unavailable entries win in this order: kind: 'holiday', then kind: 'blocked', then kind: 'break'. If any matching kind: 'working' availability exists for the resource/date, the slot is open only when it overlaps one of those dated working intervals; otherwise it is treated as closed for that date. If no matching dated working availability exists, the scheduler falls back to the resolved calendar.

The same precedence applies to conflicts. If dated availability entries define kind: 'working' for a resource/date and an event is not fully covered by those working intervals, the scheduler raises outside-availability from availability instead of checking the calendar. If no dated working availability applies, the calendar can raise outside-availability. Availability entries with kind: 'blocked', kind: 'holiday', or kind: 'break' raise blocked-time when events overlap them. Both conflict types can be made blocking from the conflicts configuration.

The event-scheduler-shift-week demo includes a calendar selector with weekday, open, and training calendars. Switch between them to see closed hours, closed days, and holiday behavior update without changing the event source.