Skip to content

Calendars and Duration

Source code
Astroastro
---
import GanttScheduling from '@revolist/revogrid-examples/components/gantt/GanttScheduling.vue';
---

<GanttScheduling client:only="vue" />
TypeScriptts
// src/components/gantt/GanttScheduling.ts
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();

import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import type { GanttTaskSourceRow, DependencyEntity, CalendarEntity } from '@revolist/gantt';
import { currentTheme } from '../composables/useRandomData';

const { isDark } = currentTheme();

const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';

// Calendar-aware scheduling: durations are in working days (Mon–Fri, excluding holidays)
const ganttConfig = {
  id: PROJECT_ID,
  name: 'Website Redesign',
  version: '1',
  currency: 'USD',
  timeZone: 'America/New_York',
  primaryCalendarId: CALENDAR_ID,
  updatedAt: '2026-04-06T00:00:00Z',
  statusDate: '2026-04-06',
  zoomPreset: 'week' as const,
  scheduling: {
    excludeHolidaysFromDuration: true, // durations skip weekends and holidays
  },
  visuals: {
    shadeNonWorkingTime: true, // shade weekend columns on the timeline
    projectLineDate: '2026-04-06', // vertical status-date line
  },
};

// US calendar with public holidays
const calendars: CalendarEntity[] = [
  {
    id: CALENDAR_ID,
    name: 'US Standard',
    timeZone: 'America/New_York',
    workingDays: [1, 2, 3, 4, 5], // Mon–Fri
    holidays: [
      '2026-05-25', // Memorial Day
      '2026-07-04', // Independence Day
    ],
    hoursPerDay: 8,
  },
];

const tasks: GanttTaskSourceRow[] = [
  {
    id: 't1', parentId: null,
    name: 'Design', type: 'summary', workflowStatus: 'in-progress',
    startDate: '2026-04-06', endDate: '2026-04-24', duration: 15,
    percentDone: 60, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't2', parentId: 't1',
    name: 'Wireframes', type: 'task', workflowStatus: 'done',
    startDate: '2026-04-06', endDate: '2026-04-10', duration: 5,
    percentDone: 100, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't3', parentId: 't1',
    name: 'Design Review', type: 'milestone', workflowStatus: 'done',
    startDate: '2026-04-10', endDate: '2026-04-10', duration: 0,
    percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'],
  },
  {
    id: 't4', parentId: 't1',
    name: 'Visual Design', type: 'task', workflowStatus: 'in-progress',
    startDate: '2026-04-13', endDate: '2026-04-24', duration: 10,
    percentDone: 40, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't5', parentId: null,
    name: 'Development', type: 'summary', workflowStatus: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
    percentDone: 0, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't6', parentId: 't5',
    name: 'Frontend', type: 'task', workflowStatus: 'not-started',
    // constraint: cannot start before May 4 (waiting on external API)
    startDate: '2026-05-04', endDate: '2026-05-20', duration: 13,
    percentDone: 0, calendarId: CALENDAR_ID, tags: [],
    constraintType: 'start-no-earlier-than',
    constraintDate: '2026-05-04',
  },
  {
    id: 't7', parentId: 't5',
    name: 'Backend', type: 'task', workflowStatus: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
    percentDone: 0, calendarId: CALENDAR_ID, tags: [],
    deadlineDate: '2026-05-22', // deadline marker — project expects early delivery
  },
  {
    id: 't8', parentId: null,
    name: 'Launch', type: 'milestone', workflowStatus: 'not-started',
    startDate: '2026-05-28', endDate: '2026-05-28', duration: 0,
    percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'],
  },
];

const dependencies: DependencyEntity[] = [
  { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
  { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
  { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
  { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
  { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
  { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
];

export function load(parentSelector: string) {
  const grid = document.createElement('revo-grid');

  grid.theme = isDark() ? 'darkCompact' : 'compact';
  grid.hideAttribution = true;
  grid.plugins = [GanttPlugin];
  grid.gantt = ganttConfig;
  grid.ganttCalendars = calendars;
  grid.ganttDependencies = dependencies;
  grid.source = tasks;
  grid.columns = [
    createDefaultTaskTableColumn('wbs'),
    createDefaultTaskTableColumn('name'),
  ];

  document.querySelector(parentSelector)?.appendChild(grid);
}
Vuevue
<template>
  <RevoGrid
    hide-attribution
    style="height: 500px"
    :theme="isDark ? 'darkCompact' : 'compact'"
    :plugins="plugins"
    :source="tasks"
    :columns="columns"
    :gantt.prop="ganttConfig"
    :gantt-dependencies.prop="dependencies"
    :gantt-calendars.prop="calendars"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import type { GanttTaskSourceRow, DependencyEntity, CalendarEntity } from '@revolist/gantt';
import { currentThemeVue } from '../composables/useRandomData';

const { isDark } = currentThemeVue();

const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';

const plugins = ref([GanttPlugin]);

const ganttConfig = ref({
  id: PROJECT_ID,
  name: 'Website Redesign',
  version: '1',
  currency: 'USD',
  timeZone: 'America/New_York',
  primaryCalendarId: CALENDAR_ID,
  updatedAt: '2026-04-06T00:00:00Z',
  statusDate: '2026-04-06',
  zoomPreset: 'week' as const,
  scheduling: {
    excludeHolidaysFromDuration: true,
  },
  visuals: {
    shadeNonWorkingTime: true,
    projectLineDate: '2026-04-06',
  },
});

const calendars = ref<CalendarEntity[]>([
  {
    id: CALENDAR_ID,
    name: 'US Standard',
    timeZone: 'America/New_York',
    workingDays: [1, 2, 3, 4, 5],
    holidays: ['2026-05-25', '2026-07-04'],
    hoursPerDay: 8,
  },
]);

const tasks = ref<GanttTaskSourceRow[]>([
  {
    id: 't1', parentId: null,
    name: 'Design', type: 'summary', workflowStatus: 'in-progress',
    startDate: '2026-04-06', endDate: '2026-04-24', duration: 15,
    percentDone: 60, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't2', parentId: 't1',
    name: 'Wireframes', type: 'task', workflowStatus: 'done',
    startDate: '2026-04-06', endDate: '2026-04-10', duration: 5,
    percentDone: 100, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't3', parentId: 't1',
    name: 'Design Review', type: 'milestone', workflowStatus: 'done',
    startDate: '2026-04-10', endDate: '2026-04-10', duration: 0,
    percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'],
  },
  {
    id: 't4', parentId: 't1',
    name: 'Visual Design', type: 'task', workflowStatus: 'in-progress',
    startDate: '2026-04-13', endDate: '2026-04-24', duration: 10,
    percentDone: 40, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't5', parentId: null,
    name: 'Development', type: 'summary', workflowStatus: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
    percentDone: 0, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't6', parentId: 't5',
    name: 'Frontend', type: 'task', workflowStatus: 'not-started',
    startDate: '2026-05-04', endDate: '2026-05-20', duration: 13,
    percentDone: 0, calendarId: CALENDAR_ID, tags: [],
    constraintType: 'start-no-earlier-than',
    constraintDate: '2026-05-04',
  },
  {
    id: 't7', parentId: 't5',
    name: 'Backend', type: 'task', workflowStatus: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
    percentDone: 0, calendarId: CALENDAR_ID, tags: [],
    deadlineDate: '2026-05-22',
  },
  {
    id: 't8', parentId: null,
    name: 'Launch', type: 'milestone', workflowStatus: 'not-started',
    startDate: '2026-05-28', endDate: '2026-05-28', duration: 0,
    percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'],
  },
]);

const dependencies = ref<DependencyEntity[]>([
  { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
  { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
  { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
  { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
  { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
  { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
]);

const columns = ref([
  createDefaultTaskTableColumn('wbs'),
  createDefaultTaskTableColumn('name'),
]);
</script>
Reacttsx
import React, { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import type { GanttTaskSourceRow, DependencyEntity, CalendarEntity } from '@revolist/gantt';
import { currentTheme } from '../composables/useRandomData';

const { isDark } = currentTheme();

const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';

const tasks: GanttTaskSourceRow[] = [
  {
    id: 't1', parentId: null,
    name: 'Design', type: 'summary', workflowStatus: 'in-progress',
    startDate: '2026-04-06', endDate: '2026-04-24', duration: 15,
    percentDone: 60, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't2', parentId: 't1',
    name: 'Wireframes', type: 'task', workflowStatus: 'done',
    startDate: '2026-04-06', endDate: '2026-04-10', duration: 5,
    percentDone: 100, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't3', parentId: 't1',
    name: 'Design Review', type: 'milestone', workflowStatus: 'done',
    startDate: '2026-04-10', endDate: '2026-04-10', duration: 0,
    percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'],
  },
  {
    id: 't4', parentId: 't1',
    name: 'Visual Design', type: 'task', workflowStatus: 'in-progress',
    startDate: '2026-04-13', endDate: '2026-04-24', duration: 10,
    percentDone: 40, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't5', parentId: null,
    name: 'Development', type: 'summary', workflowStatus: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
    percentDone: 0, calendarId: CALENDAR_ID, tags: [],
  },
  {
    id: 't6', parentId: 't5',
    name: 'Frontend', type: 'task', workflowStatus: 'not-started',
    startDate: '2026-05-04', endDate: '2026-05-20', duration: 13,
    percentDone: 0, calendarId: CALENDAR_ID, tags: [],
    constraintType: 'start-no-earlier-than',
    constraintDate: '2026-05-04',
  },
  {
    id: 't7', parentId: 't5',
    name: 'Backend', type: 'task', workflowStatus: 'not-started',
    startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
    percentDone: 0, calendarId: CALENDAR_ID, tags: [],
    deadlineDate: '2026-05-22',
  },
  {
    id: 't8', parentId: null,
    name: 'Launch', type: 'milestone', workflowStatus: 'not-started',
    startDate: '2026-05-28', endDate: '2026-05-28', duration: 0,
    percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'],
  },
];

const dependencies: DependencyEntity[] = [
  { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
  { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
  { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
  { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
  { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
  { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
];

const calendars: CalendarEntity[] = [
  {
    id: CALENDAR_ID,
    name: 'US Standard',
    timeZone: 'America/New_York',
    workingDays: [1, 2, 3, 4, 5],
    holidays: ['2026-05-25', '2026-07-04'],
    hoursPerDay: 8,
  },
];

function GanttScheduling() {
  const ganttConfig = useMemo(() => ({
    id: PROJECT_ID,
    name: 'Website Redesign',
    version: '1',
    currency: 'USD',
    timeZone: 'America/New_York',
    primaryCalendarId: CALENDAR_ID,
    updatedAt: '2026-04-06T00:00:00Z',
    statusDate: '2026-04-06',
    zoomPreset: 'week' as const,
    scheduling: {
      excludeHolidaysFromDuration: true,
    },
    visuals: {
      shadeNonWorkingTime: true,
      projectLineDate: '2026-04-06',
    },
  }), []);

  const columns = useMemo(() => [
    createDefaultTaskTableColumn('wbs'),
    createDefaultTaskTableColumn('name'),
  ], []);

  return (
    <RevoGrid
      style={{ height: '500px' }}
      theme={isDark() ? 'darkCompact' : 'compact'}
      hideAttribution
      plugins={[GanttPlugin]}
      source={tasks}
      columns={columns}
      gantt={ganttConfig}
      ganttDependencies={dependencies}
      ganttCalendars={calendars}
    />
  );
}

export default GanttScheduling;
Angularts
import { Component, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';
import type { GanttTaskSourceRow, DependencyEntity, CalendarEntity } from '@revolist/gantt';
import { currentTheme } from '../composables/useRandomData';

const PROJECT_ID = 'project-web-redesign';
const CALENDAR_ID = 'cal-us';

@Component({
  selector: 'gantt-scheduling-grid',
  standalone: true,
  imports: [RevoGrid],
  // Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
  schemas: [NO_ERRORS_SCHEMA],
  template: `
    <revo-grid
      style="min-height: 500px"
      [theme]="theme"
      [hideAttribution]="true"
      [plugins]="plugins"
      [source]="tasks"
      [columns]="columns"
      [gantt]="ganttConfig"
      [ganttDependencies]="dependencies"
      [ganttCalendars]="calendars"
    ></revo-grid>
  `,
  encapsulation: ViewEncapsulation.None,
})
export class GanttSchedulingGridComponent {
  theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
  plugins = [GanttPlugin];

  ganttConfig = {
    id: PROJECT_ID,
    name: 'Website Redesign',
    version: '1',
    currency: 'USD',
    timeZone: 'America/New_York',
    primaryCalendarId: CALENDAR_ID,
    updatedAt: '2026-04-06T00:00:00Z',
    statusDate: '2026-04-06',
    zoomPreset: 'week' as const,
    scheduling: {
      excludeHolidaysFromDuration: true,
    },
    visuals: {
      shadeNonWorkingTime: true,
      projectLineDate: '2026-04-06',
    },
  };

  calendars: CalendarEntity[] = [
    {
      id: CALENDAR_ID,
      name: 'US Standard',
      timeZone: 'America/New_York',
      workingDays: [1, 2, 3, 4, 5],
      holidays: ['2026-05-25', '2026-07-04'],
      hoursPerDay: 8,
    },
  ];

  tasks: GanttTaskSourceRow[] = [
    {
      id: 't1', parentId: null,
      name: 'Design', type: 'summary', workflowStatus: 'in-progress',
      startDate: '2026-04-06', endDate: '2026-04-24', duration: 15,
      percentDone: 60, calendarId: CALENDAR_ID, tags: [],
    },
    {
      id: 't2', parentId: 't1',
      name: 'Wireframes', type: 'task', workflowStatus: 'done',
      startDate: '2026-04-06', endDate: '2026-04-10', duration: 5,
      percentDone: 100, calendarId: CALENDAR_ID, tags: [],
    },
    {
      id: 't3', parentId: 't1',
      name: 'Design Review', type: 'milestone', workflowStatus: 'done',
      startDate: '2026-04-10', endDate: '2026-04-10', duration: 0,
      percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'],
    },
    {
      id: 't4', parentId: 't1',
      name: 'Visual Design', type: 'task', workflowStatus: 'in-progress',
      startDate: '2026-04-13', endDate: '2026-04-24', duration: 10,
      percentDone: 40, calendarId: CALENDAR_ID, tags: [],
    },
    {
      id: 't5', parentId: null,
      name: 'Development', type: 'summary', workflowStatus: 'not-started',
      startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
      percentDone: 0, calendarId: CALENDAR_ID, tags: [],
    },
    {
      id: 't6', parentId: 't5',
      name: 'Frontend', type: 'task', workflowStatus: 'not-started',
      startDate: '2026-05-04', endDate: '2026-05-20', duration: 13,
      percentDone: 0, calendarId: CALENDAR_ID, tags: [],
      constraintType: 'start-no-earlier-than',
      constraintDate: '2026-05-04',
    },
    {
      id: 't7', parentId: 't5',
      name: 'Backend', type: 'task', workflowStatus: 'not-started',
      startDate: '2026-04-27', endDate: '2026-05-28', duration: 24,
      percentDone: 0, calendarId: CALENDAR_ID, tags: [],
      deadlineDate: '2026-05-22',
    },
    {
      id: 't8', parentId: null,
      name: 'Launch', type: 'milestone', workflowStatus: 'not-started',
      startDate: '2026-05-28', endDate: '2026-05-28', duration: 0,
      percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'],
    },
  ];

  dependencies: DependencyEntity[] = [
    { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 },
    { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 },
    { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 },
    { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 },
    { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
    { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },
  ];

  columns = [
    createDefaultTaskTableColumn('wbs'),
    createDefaultTaskTableColumn('name'),
  ];
}

duration is stored in hours and can be displayed or entered as minutes, hours, days, weeks, months, quarters, or years. Unit conversion uses durationSettings and the effective calendar; see Duration Units and Conversion. Enable calendar exclusion when weekends, holidays, and closed hours should be skipped:

grid.gantt = {
scheduling: {
excludeHolidaysFromDuration: true,
},
};

Calendars use ISO weekday numbers where Monday is 1 and Sunday is 7:

grid.ganttCalendars = [{
id: 'cal-standard',
name: 'Standard',
timeZone: 'UTC',
workingDays: [1, 2, 3, 4, 5],
holidays: ['2026-05-25'],
hoursPerDay: 8,
}];

Each task references a calendar through calendarId. Resource capacity checks use the resource calendar when a resource is assigned.

CalendarEntity.hoursPerDay is the default conversion for task durations on that calendar. Project durationSettings.hoursPerDay can override it globally. Conversion settings do not change workingDays, holidays, or workingHours automatically.

Use working-day lag when dependency offsets should skip weekends and holidays:

grid.gantt = {
scheduling: {
lagCalendar: 'working-days',
},
};

Example: A one-day lag after a Friday skips the weekend and a Monday holiday, landing on Tuesday.