Contact Us

May 4, 2026

May 8, 2026 12:49 am

Business-Day-Aware Email Sends in SFMC: The SSJS Counter That Skips Weekends and Holidays

Share with

Salesforce Marketing Cloud Insights by Genetrix Technology
Marketing Cloud · SSJS · Financial Services · Automation

Business-Day-Aware Email Sends in SFMC: The SSJS Counter That Skips Weekends and Holidays

GT

Team Genetrix


April 2026

7 min read

“Send this email exactly 23 business days after the customer’s payment due date.” Sounds like a straightforward automation requirement. A scheduled query, a date filter, done.

Add weekends. Add public holidays. Add the requirement that if today is a non-working day, the automation must not skip those customers — it must compensate on the next working day by catching everyone who was due on the skipped days. Now it’s a different problem entirely.

This was the real challenge a financial services client brought to us. Their payment overdue reminders needed to respect business days — not calendar days — and they needed a solution that was fully automated, required no manual intervention, and adjusted dynamically every time a weekend or holiday cluster changed the effective send date.

Calendar planning — business day logic for financial services email automation

Calendar days and business days are not the same. A payment overdue reminder that ignores weekends and holidays either sends on the wrong day or silently misses customers.

Why SQL Alone Can’t Solve This

The naive SQL approach is: WHERE DaysPastDue = 23. This works on normal weekdays. But on a Tuesday after a 3-day holiday weekend (Saturday + Sunday + Monday public holiday), you need to catch everyone who was at 23 days on Saturday, Sunday, and Monday — not just Tuesday.

That means your query on Tuesday needs: WHERE DaysPastDue IN (23, 24, 25, 26). And the range changes every day, depending on how many non-working days preceded the current send date. Hardcoding this in SQL creates a maintenance nightmare. You’d need to manually update the query every time a holiday cluster occurs.

The better architecture: let SSJS calculate the dynamic range, then pass it to SQL. SSJS has native JavaScript date functions that can detect weekends in real time, and it can query a holiday reference Data Extension to check for public holidays. The SQL stays clean; the intelligence lives in the script.

Important edge case: The compensation must also handle consecutive clusters — three consecutive non-working days means the next working day’s query needs to cover a range of four days. The counter must accumulate correctly across multi-day runs where the automation doesn’t fire at all.

The Three-Part Solution

Part 1: Holiday Reference Data Extension

A simple Data Extension maintained annually by the business team, listing all public holidays for the year. The SSJS script reads this DE on every run — it never hardcodes holidays, it looks them up dynamically.

Data Extension — HolidayCalendar Schema

HolidayDate    Date     (Primary Key)
HolidayName    Text
Region         Text     (optional, for multi-region support)

-- Example records:
2026-01-26  "Republic Day"       "IN"
2026-03-29  "Good Friday"        "IN"
2026-08-15  "Independence Day"   "IN"

Part 2: The SSJS Counter Script

This script runs as the first activity in the daily Marketing Cloud automation. It checks whether today is a weekend or holiday. If it is, it increments a counter and exits — no send runs today. If it’s a working day, it reads the counter, builds the DaysPastDue range dynamically, triggers the SQL query with that range, and resets the counter to zero.

SSJS — Business Day Counter and Dynamic Range Builder

<script runat="server">
Platform.Load("Core", "1");

var today      = new Date();
var dayOfWeek  = today.getDay();        // 0=Sun, 6=Sat
var isWeekend  = (dayOfWeek === 0 || dayOfWeek === 6);

// Check holiday DE
var todayStr   = Platform.Function.FormatDate(today, "yyyy-MM-dd");
var holidays   = Platform.Function.LookupRows("HolidayCalendar",
                                               "HolidayDate", todayStr);
var isHoliday  = (holidays && holidays.length > 0);

// Read current counter
var cRows  = Platform.Function.LookupRows("SkipDayCounter",
                                           "CounterKey", "MAIN");
var count  = (cRows && cRows.length > 0)
             ? parseInt(Platform.Function.Field(cRows[0], "CounterValue"))
             : 0;

if (isWeekend || isHoliday) {
    // Skip today — increment counter
    Platform.Function.UpsertDE("SkipDayCounter",
        ["CounterKey"], ["MAIN"],
        ["CounterValue"], [count + 1]);
} else {
    // Working day — build range and trigger send
    var daysRange = [];
    for (var i = 0; i <= count; i++) { daysRange.push(23 + i); }

    // Write range to QueryParam DE for SQL to pick up
    Platform.Function.UpsertDE("QueryParams",
        ["ParamKey"], ["DAYS_PAST_DUE"],
        ["ParamValue"], [daysRange.join(",")]);

    // Reset counter
    Platform.Function.UpsertDE("SkipDayCounter",
        ["CounterKey"], ["MAIN"],
        ["CounterValue"], [0]);
}
</script>

Part 3: The Dynamic SQL Query

The SQL query reads the DaysPastDue range written by the SSJS script and filters contacts accordingly. On a normal day, it reads 23. After a 3-day holiday weekend, it reads 23,24,25,26. The SQL itself never changes — only the parameter does.

Financial services — payment reminder system ensuring no customer is missed

No customer is missed because of a holiday. The counter ensures Monday compensates for Saturday, Sunday, and any public holidays in between.

Walking Through a Real Scenario

Let’s make the counter logic concrete with a real example spanning a 4-day holiday cluster (Thursday national holiday + Friday national holiday + Saturday + Sunday).

Wednesday (normal working day): Counter = 0. Query runs for DaysPastDue = 23. Counter resets to 0.

Thursday (national holiday): Counter increments to 1. No send.

Friday (national holiday): Counter increments to 2. No send.

Saturday (weekend): Counter increments to 3. No send.

Sunday (weekend): Counter increments to 4. No send.

Monday (working day): Counter = 4. Query runs for DaysPastDue IN (23, 24, 25, 26, 27). Monday’s single run catches everyone due over the entire 5-day period. Counter resets to 0.

Key Design Decisions

  • Counter accumulates across consecutive non-working days — a 4-day cluster produces a range of 5 days on the next working day.
  • Counter resets only after a successful send — wrap the reset in error handling to prevent silent data loss if the query fails.
  • Weekends are detected in real time — no manual updates needed when the calendar year changes.
  • Holidays require an annual update — the business team maintains the HolidayCalendar DE; no code changes needed.
  • Multi-region support — add a Region column to the holiday DE and filter by region in the SSJS script for global implementations.

Frequently Asked Questions

What if the automation itself fails on a working day?

This is a critical edge case. If the automation fails on a working day, the counter is not reset — so the next successful run will include an extra day in the range, as if today was also a non-working day. This may or may not be acceptable depending on your business rules. For high-stakes use cases, add a separate monitoring alert when the automation fails to run on an expected working day.

Can this pattern handle different “due day” thresholds (e.g., 30 days instead of 23)?

Yes — the base threshold (23 in our example) is just a variable in the SSJS script and SQL. Change it to 30 or any other value. The counter logic and range-building remain identical.

Why use SSJS instead of a more complex SQL with date functions?

SFMC SQL has limited date manipulation capabilities and no access to external DEs for holiday lookups within the same query. SSJS provides JavaScript’s full date API, direct DE read/write access, and the ability to construct dynamic values before the SQL runs. For this type of conditional, pre-execution logic, SSJS is the right tool.

Genetrix Technology · Salesforce Marketing Cloud Partner

Need business-day logic in your Marketing Cloud automations?

Genetrix builds custom SSJS + SQL automation solutions for financial services, insurance, and any regulated industry where calendar precision directly affects compliance and customer experience. Let’s scope your use case.

Get in Touch with Genetrix →

Blogs for the

Business-Savvy!​

Let’s Connect

A 30 min no cost strategy session
with cloud support expert

Let’s Connect

A 30 min no cost strategy session
with cloud support expert