DurationCalculator function isAfter returns incorrect result when a schedule is applied and the start time is outside the schedule working hours.DescriptionDurationCalculator function isAfter returns incorrect result when a schedule is applied and the start time is outside the schedule working hours. This negatively affects any relative duration calculation when that use schedules and the isAfter function such as: "Next business day by 4pm", "2 business days by 4pm" and "3 business days by 4pm".Steps to Reproduce Run following script in scripts - background: var gdt = new GlideDateTime(); gdt.setDisplayValue("2021-03-21 15:00:00"); executeSample(gdt.getValue()); function executeSample(startdate) { var dc = new DurationCalculator(); // Load the "8-5 weekdays excluding holidays" schedule into our duration calculator. dc.setSchedule("08fcd0830a0a0b2600079f56b1adb9ae"); gs.log("Starting on " + startdate); dc.setStartDateTime(startdate); var start = dc.startDateTime; // 2 bus days by 4pm if before 10am dc.calcRelativeDuration("c69ab9b1c0a8016461b7f30134c111dd"); gs.log("Next Business Day end: " + dc.getEndDateTime().getDisplayValue()); }WorkaroundModify script include: DurationCalculator function: isAfter to read as follows: isAfter: function(/*GlideDateTime*/ dt, /*String hh:mm:ss*/ tm) { var startGdt; if (!dt) startGdt = new GlideDateTime(this.startDateTime); else startGdt = new GlideDateTime(dt); if (this.lu.atLevel(GSLog.DEBUG)) this.lu.logDebug('[isAfter]: startGdt: ' + startGdt.getDisplayValueInternal()); // use GlideScheduleDateTime to get the timezone object for the timezone we're using var gsdt = new GlideScheduleDateTime(); gsdt.setTimeZone(this.timezone); // Get the "tm" time string into a GlideDateTime object which will help to parse it for us // and set the date to be the same as that from the start date/time (dt) var timeGdt = new GlideDateTime(); timeGdt.setTZ(gsdt.getTimeZone()); timeGdt.setDisplayValueInternal(startGdt.getLocalDate().getValue() + " " + tm); // if we didn't manage to parse it then getDate() will return null and we'll assume false if (timeGdt.getDate() === null) return false; // if we have a schedule and the supplied GlideDateTime is not in schedule get the next // date/time inside the schedule if (this.schedule && !this.schedule.isInSchedule(startGdt)) { var whenNext = this.schedule.whenNext(startGdt, this.timezone); if (whenNext) { // Retain the actual start datetime this.actualStartGdt = new GlideDateTime(startGdt); startGdt.add(whenNext); if (this.startDateTime) this.startDateTime = startGdt; if (this.lu.atLevel(GSLog.DEBUG)) this.lu.logDebug('[isAfter] Start of next working hours: ' + startGdt.getDisplayValueInternal()); timeGdt.setDisplayValueInternal(startGdt.getLocalDate().getValue() + " " + tm); if (this.lu.atLevel(GSLog.DEBUG)) this.lu.logDebug('[isAfter] start after: ' + tm + " on business date: " + timeGdt.getDisplayValueInternal()); } else { if (this.lu.atLevel(GSLog.INFO)) this.lu.logInfo('[isAfter] No next working hours found in schedule: ' + this.schedule); return false; } } var isAfter = startGdt.getNumericValue() > timeGdt.getNumericValue(); if (this.lu.atLevel(GSLog.DEBUG)) this.lu.logDebug('[isAfter] return: ' + startGdt.getDisplayValueInternal() + " isAfter " + timeGdt.getDisplayValueInternal() + " = " + isAfter); return isAfter; }, // returns 0 if endTime is before startTime _totalSeconds: function(/* GlideDateTime */ startTime, /* GlideDateTime */ endTime) { startTime = this.actualStartGdt ? this.actualStartGdt : startTime; if (this.lu.atLevel(GSLog.DEBUG)) this.lu.logDebug('[_totalSeconds] startTime: ' + startTime.getDisplayValueInternal() + " endTime: " + endTime.getDisplayValueInternal()); var totalSeconds = Math.max(0, (Math.floor(endTime.getNumericValue()/1000) - Math.floor(startTime.getNumericValue()/1000) )); if (this.lu.atLevel(GSLog.DEBUG)) this.lu.logDebug('[_totalSeconds] totalSeconds: ' + totalSeconds); return totalSeconds; }, Related Problem: PRB1485025