There might be events that span over a weekend or another time period marked as non-business. In such cases, you may want to exclude the non-business hours from the total event duration.

The following features can assist you in implementing this scenario:

  • You can highlight the segments of the event that fall during non-business hours.

  • When moving the event, the Scheduler can automatically extend its duration to skip the non-business hours.

  • You can also adjust the event duration during creation and resizing using custom logic.

Highlighting the Non-Business Segments

javascript scheduler skip weekends highlight css

You can highlight the non-business segments using the onBeforeEventRender event handler (see also event active areas). This example highlights Saturday and Sunday, preserving any existing active areas. It uses the default eventEndSpec: "DateTime" (exclusive end) and does not determine business hours from the row or cell settings:

onBeforeEventRender: (args) => {
    const weekends = app.getWeekendsInRange(args.data.start, args.data.end);
    const weekendAreas = weekends.map((w) => {
        return {
            start: w.start,
            end: w.end,
            top: 4,
            bottom: 1,
            cssClass: "weekend"
        };
    });
    args.data.areas = [...(args.data.areas || []), ...weekendAreas];
},

The app.getWeekendsInRange() function returns the portions of weekends that overlap the supplied range. It handles events starting on Sunday, clips both boundaries to the event, and excludes a weekend that starts exactly at the event end:

const app = {
    getWeekendsInRange(start, end) {
        const rangeStart = new DayPilot.Date(start);
        const rangeEnd = new DayPilot.Date(end);
        const weekends = [];
        if (rangeEnd <= rangeStart) {
            return weekends;
        }

        let current = rangeStart.getDatePart();
        if (current.getDayOfWeek() === 0) {
            current = current.addDays(-1);  // Include the weekend already in progress.
        }

        while (current < rangeEnd) {
            if (current.getDayOfWeek() === 6) {  // Saturday
                const weekendEnd = current.addDays(2);
                const clippedStart = current < rangeStart ? rangeStart : current;
                const clippedEnd = weekendEnd > rangeEnd ? rangeEnd : weekendEnd;
                if (clippedStart < clippedEnd) {
                    weekends.push({start: clippedStart, end: clippedEnd});
                }
                current = current.addDays(7);
            } else {
                current = current.addDays(1);
            }
        }
        return weekends;
    }
};

And the weekend CSS class that adds semi-transparent stripes:

.weekend {
    background-image: repeating-linear-gradient(45deg, #00000033, #00000033 10px, transparent 10px, transparent 20px);
}

Skipping Non-Business Hours during Event Moving

The Scheduler can preserve an event's duration measured in business hours while it is moved. Enable this behavior using the eventMoveSkipNonBusiness property:

{
  // ...
  eventMoveSkipNonBusiness: true,
}

In this case, the Scheduler will calculate the event duration based on business hours and will automatically adjust (either prolong or shorten) the event as necessary during drag and drop.

The following time segments will not be included in the new duration:

Skipping Non-Business Hours during Event Creation

During event creation, skipping non-business hours is not enforced automatically.

There are multiple ways to handle a new time range selection that starts or ends during a non-business segment:

  1. Shorten the selection to end at the previous business cell.

  2. Extend the selection to end at the next business cell.

  3. Mark the selection as invalid.

You can implement your own rule using the onTimeRangeSelecting event handler. The following example demonstrates how to extend the selection to end on the next business day:

{
  // ...
  onTimeRangeSelecting: (args) => {
    const cells = args.row.cells.forRange(args.start, args.end);
    let first = cells[0];
    let last = cells.last();

    while (first && !first.properties.business) {
      const minusDuration = new DayPilot.Duration(first.end, first.start);
      args.start = args.start.addTime(minusDuration);
      first = DayPilot.list(args.row.cells.forRange(args.start, args.start.addSeconds(1))).first();
    }
    if (!first) {
      args.start = dp.visibleStart();
      args.allowed = false;
      args.right.enabled = true;
      args.right.html = "Out of range";
    }

    while (last && !last.properties.business) {
      const duration = new DayPilot.Duration(last.start, last.end);
      args.end = args.end.addTime(duration);
      last = DayPilot.list(args.row.cells.forRange(args.end.addSeconds(-1), args.end)).first();
    }
    if (!last) {
      args.end = dp.visibleEnd();
      args.allowed = false;
      args.left.enabled = true;
      args.left.html = "Out of range";
    }
  },
}

For an example project, see the JavaScript Scheduler: Skip Non-Business Cells during Drag and Drop tutorial.

Skipping Non-Business Hours during Event Resizing

The same logic can be applied during event resizing. An example onEventResizing implementation that extends the target duration automatically could look like this (see also event resizing customization):

{
  // ...
  onEventResizing: function(args) {
    const cells = args.row.cells.forRange(args.start, args.end);
    let first = cells[0];
    let last = cells.last();

    while (first && !first.properties.business) {
      const minusDuration = new DayPilot.Duration(first.end, first.start);
      args.start = args.start.addTime(minusDuration);
      first = DayPilot.list(args.row.cells.forRange(args.start, args.start.addSeconds(1))).first();
    }
    if (!first) {
      args.start = dp.visibleStart();
      args.allowed = false;
      args.right.enabled = true;
      args.right.html = "Out of range";
    }

    while (last && !last.properties.business) {
      const duration = new DayPilot.Duration(last.start, last.end);
      args.end = args.end.addTime(duration);
      last = DayPilot.list(args.row.cells.forRange(args.end.addSeconds(-1), args.end)).first();
    }
    if (!last) {
      args.end = dp.visibleEnd();
      args.allowed = false;
      args.left.enabled = true;
      args.left.html = "Out of range";
    }
  },
}

For an example project, see the JavaScript Scheduler: Skip Non-Business Cells during Drag and Drop tutorial.

Availability

Availability of this feature in DayPilot editions:

ProductLitePro
DayPilot for JavaScript
DayPilot for ASP.NET WebForms
DayPilot for ASP.NET MVC
DayPilot for Java