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.
Installation#
Zero runtime dependencies. Works in Node, browsers, and edge runtimes.
pnpm add @itzsa/bs-dateQuick 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
| Prop | Type | Default | Description |
|---|---|---|---|
| adToBs | (input: AdDateInput) => BsDate | — | AD civil date → BS. Accepts Date, YYYY-MM-DD, or { year, month, day }. |
| bsToAd | (input: BsDateInput) => Date | — | BS → local-midnight Date for the matching AD civil day. |
| bsToAdParts | (input: BsDateInput) => AdDate | — | BS → AD parts without constructing a Date. |
| todayBs | () => BsDate | — | Current local calendar day as BS. |
Arithmetic#
Day/month/year math with clamp semantics; age helpers for HR and forms.
Arithmetic
| Prop | Type | Default | Description |
|---|---|---|---|
| addDays | (input: BsDateInput, n: number) => BsDate | — | Add or subtract whole days in BS space. |
| addMonths | (input: BsDateInput, n: number) => BsDate | — | Add months; day is clamped to the target month length. |
| addYears | (input: BsDateInput, n: number) => BsDate | — | Add years with day clamp (leap-month safe). |
| diffInDays | (a: BsDateInput, b: BsDateInput) => number | — | Signed whole-day difference (a − b). |
| diffInBsYears | (a: BsDateInput, b: BsDateInput) => number | — | Age/tenure style year count (anniversary not yet reached → floor). |
Calendar#
Month bounds, weekdays, and compare.
Calendar
| Prop | Type | Default | Description |
|---|---|---|---|
| daysInBsMonth | (year: number, month: number) => number | — | Length of a BS month (1–12). |
| startOfBsMonth / endOfBsMonth | (input: BsDateInput) => BsDate | — | First / last day of the month. |
| getBsWeekday | (input: BsDateInput) => number | — | 0 = Sunday … 6 = Saturday (UTC civil day). |
| isSaturday | (input: BsDateInput) => boolean | — | Nepal weekend helper. |
| compareBs | (a, b) => -1 | 0 | 1 | — | Chronological compare of two BS dates. |
Format#
Pattern tokens plus Nepali numerals and month names.
Format
| Prop | Type | Default | Description |
|---|---|---|---|
| formatBs | (date, pattern?, { locale?, nepaliDigits? }) => string | "YYYY-MM-DD" | Tokens: YYYY, MM, DD, MMMM, MMM. locale ne uses Devanagari month names. |
| formatBsIso | (input) => string | — | Always YYYY-MM-DD with ASCII digits. |
| toNepaliNumerals | (input: string | number) => string | — | Map 0–9 → ०–९. |
| getBsMonthName | (month: number, locale?: "en" | "ne") => string | — | Baisakh…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
| Prop | Type | Default | Description |
|---|---|---|---|
| isPublicHoliday | (input: BsDateInput) => boolean | — | True if the active calendar marks the date. |
| getHolidayName | (input, locale?) => string | null | — | First matching holiday name, or null. |
| getHolidaysInMonth | (year, month) => HolidayEntry[] | — | All entries matching that year/month (O(1) index). |
| setHolidayCalendar | (calendar: HolidayCalendar) => void | — | Replace the process-global holiday set (module API). |
| extendHolidayCalendar | (entries: HolidayEntry[]) => void | — | Append entries to the active calendar. |
| createHolidayLookup | (calendar) => HolidayLookup | — | Build an indexed lookup without touching global state. |
| mergeHolidayCalendars | (...calendars) => HolidayCalendar | — | Merge multiple calendars (later entries win on same key). |
Scalability
Module setters mutate process state. UsecreateBsDateEngine 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
| Prop | Type | Default | Description |
|---|---|---|---|
| createBsDateEngine | (options?: BsDateEngineOptions) => BsDateEngine | — | Isolated convert / arithmetic / holidays — safe for workers & multi-tenant servers. |
| DEFAULT_CALENDAR_DATA | BsCalendarData | — | Bundled Panchanga tables BS 2000–2100. |
| extendCalendarData | (base, extra, patch?) => BsCalendarData | — | Merge 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
| Prop | Type | Default | Description |
|---|---|---|---|
| isValidBsDate | (input: unknown) => boolean | — | Soft check against the default calendar. |
| assertValidBsDate / requireBsDate | … | — | Throw typed BsRangeError / BsInvalidError / BsParseError. |
| parseDateString | (input: string) => AdDate | — | Parse YYYY-MM-DD (AD or BS string shape). |
Data & limits#
What ships in the box — and what you should override.
| Topic | Detail |
|---|---|
| BS year range | 2000–2100 inclusive (extend via extendCalendarData) |
| Epoch | BS 2000-01-01 ↔ AD 1943-04-14 |
| Timezones | Civil-day math only; bsToAd returns local midnight for that AD day |
| Holidays | Sample data (asOf in package). Always override for production payroll. |