Jeff Benedict
Tera Contributor

I have run into the same requirement with several clients working around financial close: an activity starts on workday 3, a confirmation is due on workday 7, or an escalation should occur after five business days. The requirement sounds straightforward until weekends, holidays, fiscal periods, and time zones start changing the answer.

The bigger issue is usually not calculating one date. It is preventing every flow, Business Rule, Scheduled Script, and integration from inventing its own definition of a workday. Once that happens, WD5 in one process can quietly mean something different from WD5 in another.

 

The Challenge

Financial-close processes are commonly expressed in workdays rather than calendar dates. WD1 may be the first working day of the period, WD3 may start a reconciliation activity, and WD7 may be the deadline for a balance confirmation. A simple addDays(7) cannot represent that rule because the result depends on the business calendar.

Hard-coding weekends is not enough either. A Monday-through-Friday check still misses company holidays and can duplicate calendar logic that ServiceNow already supports through schedules. It also encourages different applications to maintain different holiday rules.

Treat “workday” as shared application infrastructure, not a one-off date calculation.

 

A Simple Example

Assume the new financial period starts on Thursday, January 1, 2026, and January 1 is a company holiday. With weekends and the holiday excluded, the financial workday sequence looks like this:

Date

Day

Calendar status

Close workday

Jan 1

Thu

Company holiday

-

Jan 2

Fri

Working day

WD1

Jan 3

Sat

Weekend

-

Jan 4

Sun

Weekend

-

Jan 5

Mon

Working day

WD2

Jan 6

Tue

Working day

WD3

Jan 7

Wed

Working day

WD4

Jan 8

Thu

Working day

WD5

Jan 9

Fri

Working day

WD6

Jan 12

Mon

Working day

WD7

 

In this example, “due on WD7” resolves to January 12. Adding seven calendar days to January 1 would return January 8, which is four calendar days early relative to the actual close rule.

 

Centralize the Business Calendar

The pattern we used in a recent solution was to configure a ServiceNow business schedule and reference that schedule through application properties. The application also externalized the business time zone. That keeps the calendar configurable while allowing server-side logic to ask one consistent question: is this candidate date a working date for the financial-close calendar?

PROPERTY: {
    BUSINESS_SCHEDULE:
        'x_company_finance.business_schedule',
    BUSINESS_TIME_ZONE:
        'x_company_finance.business_time_zone'
},

getProperty: function(propertyName, defaultValue) {
    return gs.getProperty(propertyName, defaultValue);
}

 

I prefer the property indirection rather than embedding a schedule sys_id throughout the application. Different instances can point to the appropriate schedule, and changes to the business calendar do not require edits to every script that consumes it.

 

Put the Calculation in One Reusable Utility

The next step is to move the workday calculation into a Script Include. Callers should provide a period start date and the requested workday number. The utility loads the configured GlideSchedule and walks forward until it finds the requested number of dates that fall inside the business schedule.

getNthBusinessDay: function(periodStartValue, workdayNumber) {
    var scheduleId = gs.getProperty(
        'x_company_finance.business_schedule', ''
    );

    if (!scheduleId || !periodStartValue || workdayNumber < 1)
        return '';

    var businessSchedule = new GlideSchedule(scheduleId);
    var candidate = new GlideDateTime(periodStartValue);

    // Evaluate at midday to avoid midnight/DST edge cases.
    var startDate = candidate.getLocalDate().getValue();
    candidate.setDisplayValue(startDate + ' 12:00:00');

    var businessDaysFound = 0;
    var maximumDaysToCheck = 45;

    for (var i = 0; i < maximumDaysToCheck; i++) {
        if (businessSchedule.isInSchedule(candidate)) {
            businessDaysFound++;

            if (businessDaysFound == workdayNumber) {
                return candidate.getLocalDate().getValue();
            }
        }

        candidate.addDaysLocalTime(1);
    }

    return '';
}

 

The 45-day guard is intentional. A bad or empty schedule should fail visibly instead of allowing a loop to continue indefinitely. For normal monthly-close use cases, 45 calendar days is more than enough to resolve a requested workday while still surfacing configuration problems quickly.

 

Keep Fiscal Period Logic Separate

One subtle point is deciding where counting begins. In a close process, WD1 is often tied to a fiscal period, not necessarily to the first day of the calendar month. Our recent implementation resolves the relevant fiscal period first, then passes the period start into the workday utility.

var workdayNumber = this._getWorkdayNumber(
    relationshipGR.getValue('generate_on')
);

var scheduledDate = this._getNthBusinessDay(
    creationPeriodGR.getValue('fiscal_start_date_time'),
    workdayNumber
);

if (!scheduledDate) {
    result.message =
        'Unable to calculate Workday ' + workdayNumber;
    return result;
}

 

That separation is important. The fiscal calendar answers which period and start date apply. The workday calendar answers which dates within that period count as working days.


Use the Same Utility Everywhere

Once the calculation is centralized, the surrounding automations become simpler. A reporting schedule can determine when to create a case, a case can calculate its due date, and reminder logic can reference the same workday construct instead of maintaining its own weekday and holiday rules.

// Create the activity on WD3
var startDate = workdayUtil.getNthBusinessDay(
    periodStart,
    3
);

// Make the activity due on WD7
var dueDate = workdayUtil.getNthBusinessDay(
    periodStart,
    7
);

 

For a Date/Time deadline, resolve the workday first and then apply the business-specific time and time zone. In one implementation, the reporting schedule was WD1 and the confirmation due date was calculated as WD7 at 11:00 PM. Keeping the workday calculation separate from the deadline time made both rules easier to understand.


Why Not Just Count Weekdays?

  • Weekdays are not necessarily workdays. A holiday on Monday must not increment the close-day counter.
  • A ServiceNow schedule gives administrators a supported place to maintain working intervals and exclusions instead of changing code.
  • A centralized Script Include prevents Flow Designer, Scheduled Scripts, Business Rules, and UI Actions from developing competing calendar logic.
  • Externalizing the schedule and time zone through properties makes the pattern portable across instances and clients.
  • Returning an empty result when a valid workday cannot be resolved makes broken calendar configuration visible instead of silently selecting the wrong date.


A Few Practical Considerations

  • Agree on what WD1 means. Some organizations use the first business day of the new period, while others anchor workdays to a separate close calendar.
  • Use a dedicated financial-close schedule when the close calendar differs from the standard corporate business schedule.
  • Be explicit about time zones. The calendar may be global while individual deadlines are evaluated in Central, Eastern, or another business time zone.
  • Evaluate a date at a stable time such as noon when the requirement is date-based. This reduces surprises around midnight and daylight-saving transitions.
  • Test months containing a holiday on the first of the month, consecutive holidays, weekends at period start, year-end close, and daylight-saving changes.
  • If users need to understand the result later, store the calculated date on the business record rather than recalculating historical dates after the schedule changes.


Final Thoughts

The useful pattern here is not the loop that counts workdays. It is making the definition of a workday a shared service for the application. Financial-close solutions tend to accumulate WD1, WD3, WD5, WD7, reminder, escalation, and auto-close rules quickly. If each automation solves the calendar problem independently, inconsistencies are almost guaranteed.

By combining a ServiceNow business schedule, configurable application properties, fiscal-period awareness, and one reusable Script Include, the business can maintain the calendar in one place and every consuming process can calculate dates the same way.

For financial-close applications, that turns “due on business day 5” from an ambiguous requirement into a repeatable platform capability.