Dev.to WebDev 🛠 Dev 👁 0 📖 1 min read

Your due-date logic is probably wrong (and a tiny library to fix it)

"The invoice is due on the 20th." Simple, right? Then reality shows up. Bug 1: the deadline moves a day when the server changes time zone new Date('2026-11-20') // midnight UTC = Nov 19 in New York J

"The invoice is due on the 20th." Simple, right? Then reality shows up.

Bug 1: the deadline moves a day when the server changes time zone

new Date('2026-11-20') // midnight UTC = Nov 19 in New York

JavaScript Date is a moment in time, not a calendar day. If you store deadlines as
Date, a time zone or daylight-saving change can silently move them by a day.

Fix: treat deadlines as plain YYYY-MM-DD strings and do integer day arithmetic.

Bug 2: "every 31st" in February

Does a monthly deadline on the 31st skip February? Most businesses expect it on
the last day of the month instead — Feb 28, or 29 in leap years.

Bug 3: the weekend adjustment that jumps to the next month

"If it's a weekend, move to the next business day" works — until Saturday,
January 31st becomes Monday, February 2nd, and lands in the wrong accounting period.
Finance solved this long ago with modified following: move forward, unless that
changes the month; then move back.

Bug 4: business days that aren't Monday to Friday

In many countries the weekend is Friday and Saturday. And every country, state and
company has its own holidays.

A small library for all of it

I packaged these rules in duedates, a
zero-dependency TypeScript library:

import { createCalendar, nextDue, occurrences } from '@contalert/duedates';

const calendar = createCalendar({
  weekend: [6, 7],          // ISO weekdays: Saturday, Sunday
  holidays: ['2026-12-25'],
});

// Last day of each quarter, modified following
occurrences(
  { type: 'dayOfMonth', day: 'last', months: [3, 6, 9, 12], adjust: 'modifiedFollowing' },
  { from: '2026-11-01', count: 4, calendar },
);

// 5th business day of the month
nextDue({ type: 'businessDayOfMonth', n: 5 }, { from: '2026-12-01', calendar });
// { nominal: '2026-12-07', due: '2026-12-07' }

It doesn't ship holiday data on purpose: holiday rules change and you know which
ones apply to you. Pass a list, or plug in a holiday library.

npm install @contalert/duedates

If you've handled deadline logic in production, I'd love to hear which edge cases
bit you — issues and PRs are welcome.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.