All Docs
FeaturesagentOS Direct DebitUpdated March 12, 2026

BACS Working Day Engine

BACS Working Day Engine

The BACS working day engine ensures that all collection and receipt dates in the Direct Debit service are valid UK banking working days. It powers both the tenant-facing mandate form and the internal collection scheduler.

Overview

BACS payments are only processed on UK banking working days — that is, Monday to Friday, excluding UK public holidays. Submitting a collection on a non-working day (weekend or bank holiday) would cause it to be rejected or delayed. The working day engine resolves all dates before they are displayed to tenants or submitted to Modulr.

Bank Holiday Data

The service maintains a bank_holidays table seeded with official UK bank holiday data fetched from the GOV.UK Bank Holidays API:

https://www.gov.uk/bank-holidays.json

This covers bank holidays in:

  • England & Wales
  • Scotland
  • Northern Ireland

The table is used by the working day functions to skip public holidays when advancing dates.

Core Functions

calculateCollectionDate(requestedDate)

Resolves a requested collection date to the nearest valid BACS working day on or after the requested date.

Logic:

  1. If the requested date falls on a Saturday, advance to Monday.
  2. If the requested date falls on a Sunday, advance to Monday.
  3. If the resulting date is a UK bank holiday, advance to the next calendar day and repeat from step 1.
  4. Return the first date that is both a weekday and not a bank holiday.

Used by: The mandate form (Payment Details step) and the collection scheduler.


calculateReceiptDate(collectionDate)

Derives the expected receipt date from a confirmed collection date, following the standard BACS settlement cycle and skipping non-working days.

Used by: The mandate form to display the date pair to the tenant — i.e. "funds leave your account on [collection date] and will be received on [receipt date]".


Edge Cases

February 28/29 Boundary

When a mandate specifies a monthly collection day of 29, 30, or 31, the engine applies the following rules for February:

Configured dayNon-leap yearLeap year
29Feb 28Feb 29
30Feb 28Feb 29
31Feb 28Feb 29

In the following month (March), the collection reverts to the originally configured day.

Sunday-to-Monday Push

If a calculated date lands on a Sunday, it is pushed forward to Monday before any further bank holiday checks are applied. This means a Sunday that precedes a bank holiday Monday will be pushed to Tuesday.

Month-End Roll

If a mandate's configured collection day does not exist in a given month (e.g. day 31 in April, June, September, or November), the date is rolled back to the last valid day of that month.

Mandate Form Integration

On the Payment Details step of the mandate form, tenants are shown a clear date pair:

  • Collection date — the working day on which funds will be debited from their account.
  • Receipt date — the working day on which the agent/landlord will receive the funds.

Both dates are computed in real time using the working day engine as the tenant selects their preferred collection day.

Collection Scheduler Integration

The collection scheduler calls calculateCollectionDate(requestedDate) before submitting each collection to Modulr. This ensures that BACS submission dates are always valid and prevents rejections due to non-working-day submissions.