npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

temporal-business-days

v0.1.1

Published

Business-day math (skip weekends and holidays) built natively on the Temporal API — no legacy Date wrapping.

Readme

temporal-business-days

npm version bundle size license downloads

Business-day math — skipping weekends and holidays — built natively on the Temporal API. Works with Temporal.PlainDate directly instead of wrapping the legacy Date object.

  • Configurable weekends (default Saturday/Sunday)
  • Holidays as either an explicit date list or a predicate function
  • Immutable: every function returns a new Temporal.PlainDate, nothing is mutated
  • No runtime dependencies; ships ESM and CommonJS with full type declarations

Requirements

This package does not bundle a Temporal implementation. It expects a global Temporal object to already exist — either natively or from a polyfill you set up yourself.

If your environment doesn't have native Temporal yet (most do not at the time of writing — see support below), install the polyfill and install it onto the global scope:

npm install temporal-business-days
npm install @js-temporal/polyfill
import { Temporal } from '@js-temporal/polyfill'
globalThis.Temporal = Temporal

Importing the polyfill for its side effects alone (import '@js-temporal/polyfill') does not register the global — you have to assign globalThis.Temporal yourself, as shown above. Do this once, early in your application's startup, before calling into this library. If Temporal is missing when a function runs, it throws a ReferenceError explaining how to fix it rather than failing with a cryptic error.

The polyfill is a (types) peer dependency

@js-temporal/polyfill is declared as an optional peer dependency. Even though nothing from it ends up in the runtime bundle, its TypeScript types are required at compile time — the published type declarations import Temporal from @js-temporal/polyfill directly, so that the Temporal.PlainDate you pass in and the one this library expects are guaranteed to be the same type.

The practical consequence: TypeScript users need @js-temporal/polyfill installed for the types to resolve, even if you are on an environment with native Temporal and never load the polyfill at runtime. In that case it's a types-only install:

npm install -D @js-temporal/polyfill

JavaScript users without TypeScript can ignore this entirely.

Usage

import {
  isBusinessDay,
  addBusinessDays,
  businessDaysBetween,
  nextBusinessDay,
  previousBusinessDay,
} from 'temporal-business-days'

const today = Temporal.Now.plainDateISO()

isBusinessDay(today)
// true on weekdays, false on weekends — default Mon–Fri week, no holidays

addBusinessDays(today, 5)
// a Temporal.PlainDate five business days from now

businessDaysBetween(
  Temporal.PlainDate.from('2026-06-22'),
  Temporal.PlainDate.from('2026-06-26'),
)
// 4

Options

Every function takes the same optional BusinessDayOptions as its last argument, so behavior is consistent across the whole API.

interface BusinessDayOptions {
  // Days of the week that are non-working, using ISO numbering
  // (1 = Monday … 7 = Sunday). Defaults to [6, 7] (Saturday, Sunday).
  weekend?: WeekendDay[]

  // Extra non-working days on top of weekends — either an explicit list of
  // dates, or a predicate that returns true for a holiday. Defaults to none.
  holidays?: Temporal.PlainDate[] | ((date: Temporal.PlainDate) => boolean)
}
// A Friday–Saturday weekend, as used in parts of the Middle East.
isBusinessDay(date, { weekend: [5, 6] })

// An explicit holiday list.
isBusinessDay(date, { holidays: [Temporal.PlainDate.from('2026-12-25')] })

// Or any predicate.
isBusinessDay(date, { holidays: (d) => d.month === 12 && d.day === 25 })

API

isBusinessDay(date, options?) => boolean

True when date is neither a weekend day nor a holiday under the given options.

addBusinessDays(date, days, options?) => Temporal.PlainDate

Returns the date days business days away, skipping weekends and holidays. Negative days moves backwards. The starting date is never counted, whether or not it is itself a business day, so addBusinessDays(friday, 1) lands on the following Monday. days of 0 returns the date unchanged; a non-integer days throws a RangeError.

businessDaysBetween(start, end, options?) => number

Counts business days from the day after start through end, inclusive. Returns 0 when start and end are equal, and a negative number when end is before start. It is the inverse of addBusinessDays: when end is itself a business day, addBusinessDays(start, businessDaysBetween(start, end)) lands back on end.

nextBusinessDay(date, options?) => Temporal.PlainDate

The nearest business day strictly after date.

previousBusinessDay(date, options?) => Temporal.PlainDate

The nearest business day strictly before date.

Plugging in a holiday source

The holidays predicate is the integration point for a region-aware holiday library. For example, with dach-holidays providing German/Austrian/Swiss holidays, wire its check straight in:

import { isHoliday } from 'dach-holidays'
import { addBusinessDays, isBusinessDay } from 'temporal-business-days'

// Holidays for Berlin (DE-BE). dach-holidays' isHoliday takes a string | Date,
// so convert the PlainDate to its ISO string (e.g. "2026-12-25") first.
const options = {
  holidays: (date: Temporal.PlainDate) => isHoliday(date.toString(), 'DE-BE'),
}

isBusinessDay(Temporal.PlainDate.from('2026-12-25'), options)
// false — Christmas Day is a public holiday in Berlin

addBusinessDays(Temporal.PlainDate.from('2026-12-23'), 2, options)
// skips the 24th/25th if they are holidays as well as the weekend

Because the predicate is just a function, the same pattern works for any source — a database lookup, a hand-written rule, or another holiday package.

Temporal support

Temporal is a recent addition to JavaScript, so native availability is still uneven:

  • Firefox — shipped (2025)
  • Chrome — shipped (early 2026)
  • Safari — in progress; check MDN or caniuse for current status
  • Node.js — no native support yet

Until your target environments ship it, use the polyfill as described in Requirements. The browser/runtime picture is changing quickly, so the links above are the source of truth rather than this list.

License

MIT