Documentation · itzsa

BS Date

Headless Bikram Sambat date logic — convert, arithmetic, format, and swappable holidays. No React, no CSS, no picker. Pair with @itzsa/nepali-datepicker when you need UI.

@itzsa/bs-dateheadlessBS 2000–2100en / ne

Installation#

Zero runtime dependencies. Works in Node, browsers, and edge runtimes.

pnpm add @itzsa/bs-date

Quick start#

Module-level helpers share one default calendar and a process-global holiday set.

import {
  adToBs,
  bsToAd,
  addDays,
  addMonths,
  diffInBsYears,
  formatBs,
  toNepaliNumerals,
  isPublicHoliday,
  createBsDateEngine,
} from "@itzsa/bs-date";

adToBs("2025-04-14"); // { year: 2082, month: 1, day: 1 }
bsToAd("2080-10-15");

addDays("2082-01-30", 5);
diffInBsYears("2082-01-01", "2070-05-15"); // age-style years

formatBs("2082-01-05", "DD MMMM YYYY", { locale: "ne" });
toNepaliNumerals(2082); // "२०८२"

isPublicHoliday("2082-01-01");

Convert#

AD ↔ BS using community-verified Panchanga month lengths.

Convert

PropTypeDefaultDescription
adToBs(input: AdDateInput) => BsDateAD civil date → BS. Accepts Date, YYYY-MM-DD, or { year, month, day }.
bsToAd(input: BsDateInput) => DateBS → local-midnight Date for the matching AD civil day.
bsToAdParts(input: BsDateInput) => AdDateBS → AD parts without constructing a Date.
todayBs() => BsDateCurrent local calendar day as BS.

Arithmetic#

Day/month/year math with clamp semantics; age helpers for HR and forms.

Arithmetic

PropTypeDefaultDescription
addDays(input: BsDateInput, n: number) => BsDateAdd or subtract whole days in BS space.
addMonths(input: BsDateInput, n: number) => BsDateAdd months; day is clamped to the target month length.
addYears(input: BsDateInput, n: number) => BsDateAdd years with day clamp (leap-month safe).
diffInDays(a: BsDateInput, b: BsDateInput) => numberSigned whole-day difference (a − b).
diffInBsYears(a: BsDateInput, b: BsDateInput) => numberAge/tenure style year count (anniversary not yet reached → floor).

Calendar#

Month bounds, weekdays, and compare.

Calendar

PropTypeDefaultDescription
daysInBsMonth(year: number, month: number) => numberLength of a BS month (1–12).
startOfBsMonth / endOfBsMonth(input: BsDateInput) => BsDateFirst / last day of the month.
getBsWeekday(input: BsDateInput) => number0 = Sunday … 6 = Saturday (UTC civil day).
isSaturday(input: BsDateInput) => booleanNepal weekend helper.
compareBs(a, b) => -1 | 0 | 1Chronological compare of two BS dates.

Format#

Pattern tokens plus Nepali numerals and month names.

Format

PropTypeDefaultDescription
formatBs(date, pattern?, { locale?, nepaliDigits? }) => string"YYYY-MM-DD"Tokens: YYYY, MM, DD, MMMM, MMM. locale ne uses Devanagari month names.
formatBsIso(input) => stringAlways YYYY-MM-DD with ASCII digits.
toNepaliNumerals(input: string | number) => stringMap 0–9 → ०–९.
getBsMonthName(month: number, locale?: "en" | "ne") => stringBaisakh…Chaitra / बैशाख…चैत्र.

Holidays#

Bundled list is sample data — override for payroll or bank calendars. Lookups are indexed O(1).

import {
  setHolidayCalendar,
  mergeHolidayCalendars,
  DEFAULT_HOLIDAY_CALENDAR,
  type HolidayCalendar,
} from "@itzsa/bs-date";

const org: HolidayCalendar = {
  asOf: "2082 HR",
  yearRange: { min: 2082, max: 2082 },
  entries: [
    { year: 2082, month: 1, day: 1, nameEn: "New Year", nameNe: "नयाँ वर्ष" },
  ],
};

// Module API — process-global (fine for single-tenant apps)
setHolidayCalendar(mergeHolidayCalendars(DEFAULT_HOLIDAY_CALENDAR, org));

// Prefer createBsDateEngine({ holidays }) in servers / multi-tenant.

Holidays

PropTypeDefaultDescription
isPublicHoliday(input: BsDateInput) => booleanTrue if the active calendar marks the date.
getHolidayName(input, locale?) => string | nullFirst matching holiday name, or null.
getHolidaysInMonth(year, month) => HolidayEntry[]All entries matching that year/month (O(1) index).
setHolidayCalendar(calendar: HolidayCalendar) => voidReplace the process-global holiday set (module API).
extendHolidayCalendar(entries: HolidayEntry[]) => voidAppend entries to the active calendar.
createHolidayLookup(calendar) => HolidayLookupBuild an indexed lookup without touching global state.
mergeHolidayCalendars(...calendars) => HolidayCalendarMerge multiple calendars (later entries win on same key).

Scalability

Module setters mutate process state. Use createBsDateEngine when multiple holiday sets must coexist (multi-tenant APIs, workers).

Engine & scale#

Pluggable calendar tables and isolated engines keep the library robust beyond a single global config.

Isolated engines#

Own calendar + holiday index — no cross-request leakage.

import {
  createBsDateEngine,
  extendCalendarData,
  DEFAULT_CALENDAR_DATA,
} from "@itzsa/bs-date";

// Isolated: no shared holiday mutation across tenants / workers
const payroll = createBsDateEngine({
  holidays: {
    asOf: "org-2082",
    yearRange: { min: 2082, max: 2082 },
    entries: [
      { year: 2082, month: 6, day: 12, nameEn: "Dashain", nameNe: "दशैं" },
    ],
  },
});

payroll.isPublicHoliday("2082-06-12"); // true
payroll.adToBs("2025-04-14");

// Extend month-length tables past the bundled max year
const wider = extendCalendarData(DEFAULT_CALENDAR_DATA, {
  2101: [31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
});
const future = createBsDateEngine({ calendar: wider });

Extend calendar#

Ship extra year rows without forking. Epoch must stay consistent if you change minYear.

Engine / registry

PropTypeDefaultDescription
createBsDateEngine(options?: BsDateEngineOptions) => BsDateEngineIsolated convert / arithmetic / holidays — safe for workers & multi-tenant servers.
DEFAULT_CALENDAR_DATABsCalendarDataBundled Panchanga tables BS 2000–2100.
extendCalendarData(base, extra, patch?) => BsCalendarDataMerge extra year→month-length rows (e.g. past 2100).
getCalendarMeta(calendar?) => { minYear, maxYear, yearCount, … }Inspect range / version / epoch without converting.

Examples#

Live against the workspace package. Toggle Preview / Code; copy from the upper right.

Convert live#

AD → BS

2082-01-01

०१ बैशाख २०८२

BS → AD

2025-04-14

Today BS: 2083-04-06 · २०८३-०४-०६

Age / tenure#

diffInBsYears (age / tenure)

Completed years: -12

API reference#

Validation and error types.

Validation

PropTypeDefaultDescription
isValidBsDate(input: unknown) => booleanSoft check against the default calendar.
assertValidBsDate / requireBsDateThrow typed BsRangeError / BsInvalidError / BsParseError.
parseDateString(input: string) => AdDateParse YYYY-MM-DD (AD or BS string shape).

Data & limits#

What ships in the box — and what you should override.

TopicDetail
BS year range2000–2100 inclusive (extend via extendCalendarData)
EpochBS 2000-01-01 ↔ AD 1943-04-14
TimezonesCivil-day math only; bsToAd returns local midnight for that AD day
HolidaysSample data (asOf in package). Always override for production payroll.