Skip to content

Migrating from moment-business-days ​

Every feature of moment-business-days has an equivalent. The translations below are checked against the real moment-business-days in test/moment-business-days.test.ts, with thousands of random working weeks and holiday lists, and the examples run both libraries side by side.

1. Install ​

sh
npm install temporal-workdays
npm install temporal-polyfill # only where Temporal isn't built in yet (Safari, Node before 26)

2. Move the configuration ​

moment-business-days reads its settings from a global moment locale. Here the settings are a plain object, passed to createWorkCalendar or as the last argument of any function.

ts
// Before
moment.updateLocale('us', {
  workingWeekdays: [1, 2, 3, 4, 5],
  holidays: ['07-03-2026', '12-25-2026'],
  holidayFormat: 'MM-DD-YYYY',
});

// After
const us = createWorkCalendar({
  weekend: [6, 7],
  holidays: ['2026-07-03', '2026-12-25'],
});

us.isWorkday(Temporal.PlainDate.from('2026-07-03')); // → false
moment locale settingtemporal-workdays option
workingWeekdays (0 is Sunday)weekend, the days that are not working days, numbered 1 (Monday) to 7 (Sunday)
holidays and holidayFormatholidays as 'YYYY-MM-DD' strings, or 'MM-DD' for every year
a holiday(date) function on the localeholidays: (date) => …, which receives a Temporal.PlainDate
nextBusinessDayLimit, prevBusinessDayLimitnot needed, because there is no search limit
one locale per regionone createWorkCalendar(…) per region

To convert an existing configuration:

ts
// workingWeekdays to weekend
const workingWeekdays = [0, 1, 2, 3, 4]; // Sunday to Thursday
const working = workingWeekdays.map((day) => (day === 0 ? 7 : day));
const weekend = [1, 2, 3, 4, 5, 6, 7].filter((day) => !working.includes(day));
weekend; // → [5, 6]

// holidays in any holidayFormat to ISO dates
const holidays = ['07-03-2026', '12-25-2026']; // holidayFormat: 'MM-DD-YYYY'
const isoHolidays = holidays.map((h) => moment(h, 'MM-DD-YYYY').format('YYYY-MM-DD'));
isoHolidays; // → ['2026-07-03', '2026-12-25']

3. Replace the calls ​

In this table m is a moment and d is a Temporal.PlainDate. A PlainDateTime or ZonedDateTime works too and comes back as the same type.

moment-business-daystemporal-workdaysNotes
m.isBusinessDay()isWorkday(d)
m.isHoliday()isHoliday(d)
m.businessAdd(n)addWorkdays(d, n)moment rounds a fractional n, this throws
m.businessSubtract(n)subtractWorkdays(d, n)
m.nextBusinessDay()nextWorkday(d)no 7-day limit, and d is not changed
m.prevBusinessDay()previousWorkday(d)no 7-day limit, and d is not changed
a.businessDiff(b)Math.abs(workdaysBetween(b, a))see businessDiff
a.businessDiff(b, true)workdaysBetween(b, a)see businessDiff
m.monthBusinessDays()workdaysInMonth(d)
m.monthBusinessDays(end)[...eachWorkday(d.with({ day: 1 }), end)]
m.businessDaysIntoMonth()workdayOfMonth(d)0 before the first workday of the month
m.monthBusinessWeeks()workdaysByWeek(d.with({ day: 1 }), d.with({ day: d.daysInMonth }))uses your holidays and weekend
m.monthBusinessWeeks(true)workdaysByWeek(d, d.with({ day: d.daysInMonth }))
a.businessWeeksBetween(b)workdaysByWeek(a, b)
m.monthNaturalDays()plain Temporal, see below
m.monthNaturalWeeks()plain Temporal, see below
m.businessAdd(n, 'hours')addWorkingTime(dateTime, { hours: n }, { hours: [['09:00', '17:00']] })uses real working hours

Side by side:

ts
import moment from 'moment-business-days';
import { addWorkdays, workdaysBetween, workdaysInMonth } from 'temporal-workdays';

moment.updateLocale('en', { holidays: ['2026-11-26'], holidayFormat: 'YYYY-MM-DD', workingWeekdays: [1, 2, 3, 4, 5] });
const options = { holidays: ['2026-11-26'] };

moment('2026-11-25').businessAdd(1).format('YYYY-MM-DD'); // → '2026-11-27'
addWorkdays(Temporal.PlainDate.from('2026-11-25'), 1, options).toString(); // → '2026-11-27'

moment('2026-11-30').businessDiff(moment('2026-11-23')); // → 4
workdaysBetween(Temporal.PlainDate.from('2026-11-23'), Temporal.PlainDate.from('2026-11-30'), options); // → 4

moment('2026-11-10').monthBusinessDays().length; // → 20
workdaysInMonth(Temporal.PlainDate.from('2026-11-10'), options).length; // → 20

To convert between the two while you migrate:

ts
const fromMoment = Temporal.PlainDate.from(moment('2026-11-06').format('YYYY-MM-DD'));
fromMoment.toString(); // → '2026-11-06'
moment(fromMoment.toString()).format('YYYY-MM-DD'); // → '2026-11-06'

Differences ​

businessDiff ​

moment-business-days counts the workdays from the earlier date up to, but not including, the later one. workdaysBetween counts the workdays after the earlier date up to and including the later one, which keeps workdaysBetween(d, addWorkdays(d, n)) equal to n. The results are the same when both dates are workdays. When either can be a weekend day or holiday and you need moment's exact numbers, move both dates back one day:

ts
const saturday = Temporal.PlainDate.from('2026-11-07');
const monday = Temporal.PlainDate.from('2026-11-09');

moment('2026-11-09').businessDiff(moment('2026-11-07')); // → 0
workdaysBetween(saturday, monday); // → 1
workdaysBetween(saturday.subtract({ days: 1 }), monday.subtract({ days: 1 })); // → 0

Other differences ​

  • Dates are never changed in place. m.nextBusinessDay() modifies m, while nextWorkday(d) returns a new date.
  • There are no search limits. nextBusinessDay() stops after 7 days and then returns a day that isn't a business day. nextWorkday always finds the next workday, and throws only if the options contain no workdays at all.
  • monthBusinessWeeks() assumes Monday to Friday, ignores holidays and adds an empty week when a month ends on a weekend. workdaysByWeek uses your weekend and holidays, starts each week after the weekend (on Sunday for a Friday and Saturday weekend) and returns no empty weeks.
  • On a weekend at the start of a month, businessDaysIntoMonth() counts in the previous month. workdayOfMonth returns 0.
  • Invalid input such as a fractional amount, an unknown option or an impossible date throws an error instead of being rounded or ignored.
  • Moments are in local time. Here, use PlainDate for dates and ZonedDateTime for moments in time.

monthNaturalDays and monthNaturalWeeks ​

These list every day of the month, so plain Temporal does it:

ts
const february = Temporal.PlainDate.from('2026-02-10');

// monthNaturalDays()
const days = Array.from({ length: february.daysInMonth }, (_, i) => february.with({ day: i + 1 }));
days.length; // → 28

// monthNaturalWeeks(), with weeks from Sunday to Saturday
const weeks = [];
for (const day of days) {
  if (weeks.length === 0 || day.dayOfWeek === 7) weeks.push([]);
  weeks[weeks.length - 1].push(day);
}
weeks.map((week) => week.length); // → [7, 7, 7, 7]

// moment-business-days adds an empty last week here
const momentWeeks = moment('2026-02-10').monthNaturalWeeks();
momentWeeks.map((week) => week.length); // → [7, 7, 7, 7, 0]

Released under the MIT License.