# Calendars
URL: https://pro.rv-grid.com/guides/event-scheduler/scheduling-rules/calendars/
Source: src/content/docs/guides/event-scheduler/scheduling-rules/calendars.mdx
Description: Define working days, holidays, working hours, and per-resource calendars for Event Scheduler.
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:

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

## Calendar Shape

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

```ts
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.

  Calendar `workingDays` use ISO weekday numbers: Monday is `1` and Sunday is `7`. The older `nonWorkingTime.workingDays` option uses JavaScript day indexes, where Sunday is `0` and Saturday is `6`.

## Configuring Scheduler Calendars

Add calendars through `eventScheduler.calendars`:

```ts
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.

## Per-Resource Calendars

Each resource can point at its own calendar:

```ts
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:

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

## What Calendars Affect

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:

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

## Working Hours

`workingHours` can be a single range or multiple ranges:

```ts
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 vs Availability

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.

## Demo

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.
