@namlo/nepali-calendar
v0.1.3
Published
Offline Bikram Sambat (Nepali) calendar for JS/TS: BS↔AD conversion, astronomically computed panchang (tithi, nakshatra, yoga, karana, sunrise/sunset), and curated festivals/holidays. Works in React, React Native and Node.
Maintainers
Readme
@namlo/nepali-calendar
Offline Bikram Sambat (BS) calendar for JavaScript & TypeScript — accurate date conversion, astronomically computed panchang, and curated festivals/holidays. Zero network calls, zero native dependencies, works everywhere JavaScript runs.
Why this exists
Most JavaScript Nepali-calendar libraries only do date conversion (BS ↔ AD) and stop there. The few that include panchang (tithi, nakshatra, yoga, karana) reuse static almanac tables where the tithi/paksha is frequently mislabeled, and festival lists are hardcoded once and never re-verified.
This package instead:
- Computes panchang from real ephemeris (via
astronomy-engine) using the Lahiri (Chitrapaksha) ayanamsa — the same math used by professional Nepali panchang publishers — instead of a lookup table. - Verifies festival placement programmatically. Curated festival data is built against the computed panchang with assertions (e.g. "Ekadashi must fall on tithi 11 or 26"), catching the kind of month-shift errors (adhik māsa / leap-month handling) that silently break hand-transcribed data.
- Ships as one offline package for React, React Native, and Node — no API, no rate limits, no stale cache.
Features
| | | |---|---| | 🔁 | BS ↔ AD conversion, validated against per-day Gregorian anchors | | 🌙 | Computed panchang — tithi, paksha, nakshatra, yoga, karana | | ☀️ | Sunrise / sunset / moonrise / moonset, computed for any location | | 🎉 | Curated festivals & public holidays, keyed by BS year/month/day | | 📱 | Works in React, React Native (Expo), and Node — no network, no native modules | | 🌐 | Nepali (Devanagari) + English month/weekday names and numeral conversion | | 📦 | Dual ESM + CJS build with full TypeScript types, tree-shakeable |
Installation
npm install @namlo/nepali-calendaryarn add @namlo/nepali-calendar
# or
pnpm add @namlo/nepali-calendarRequires Node.js 18+ (or any modern bundler/RN runtime — the package has no Node-only dependencies at runtime).
Quick start
A step-by-step tour of the API, from simplest to most complete.
1. What's today's date in BS?
import { getToday, formatBs } from "@namlo/nepali-calendar";
const today = getToday(); // { year: 2083, month: 3, day: 26 }
formatBs(today, "np"); // "असार २६, २०८३"
formatBs(today, "en"); // "Ashadh 26, 2083"2. Convert between BS and AD
import { adToBs, bsToAd } from "@namlo/nepali-calendar";
adToBs(new Date("2026-04-14")); // { year: 2083, month: 1, day: 1 }
bsToAd({ year: 2083, month: 1, day: 1 }); // Date → 2026-04-14T00:00:00.000Z3. Get a full month grid (for rendering a patro)
import { getBsMonth } from "@namlo/nepali-calendar";
const month = getBsMonth(2083, 1); // Baishakh 2083
month.totalDays; // 31
month.startWeekday; // 2 (0=Sun..6=Sat — Baishakh 1 falls on a Tuesday)
month.days[17].ad; // "2026-05-01" (Baishakh 18)4. Get one fully-resolved day — conversion + panchang + festivals
import { getBsDay } from "@namlo/nepali-calendar";
const day = getBsDay(2083, 1, 18);
day.ad; // "2026-05-01"
day.weekday; // 5 (Friday)
day.paksha; // "Shukla"
day.panchang.tithiName; // "Purnima"
day.panchang.nakshatraName; // "Swati"
day.sunrise; // Date (Kathmandu sunrise, ~05:10 local)
day.isHoliday; // true
day.holidays; // ["बुद्ध जयन्ती", "मजदूर दिवस"]That's the whole surface you need for a typical calendar screen. Everything below is the full reference for going deeper (custom locations, raw panchang for any instant, festival listings, number formatting).
Framework example: React patro grid
A minimal component using only this package — no other date library needed.
import { getBsMonth, BS_MONTHS_NP, WEEKDAYS_NP, toDevanagari } from "@namlo/nepali-calendar";
function PatroGrid({ year, month }: { year: number; month: number }) {
const { days, startWeekday, totalDays } = getBsMonth(year, month);
const leadingBlanks = Array.from({ length: startWeekday });
return (
<div>
<h2>{BS_MONTHS_NP[month - 1]} {toDevanagari(year)}</h2>
<div className="grid grid-cols-7">
{WEEKDAYS_NP.map((w) => <div key={w}>{w}</div>)}
{leadingBlanks.map((_, i) => <div key={`b${i}`} />)}
{days.map((day) => (
<div key={day.gatey} className={day.isHoliday ? "text-red-600" : ""}>
<strong>{toDevanagari(day.gatey)}</strong>
{day.holidays.map((h) => <div key={h}>{h}</div>)}
</div>
))}
</div>
<p>{totalDays} days this month</p>
</div>
);
}This is exactly the pattern used to render the screenshot above.
API reference
Types
All exported as named TypeScript types — import type { BsDate, BsDay, ... } from "@namlo/nepali-calendar".
| Type | Shape |
|---|---|
| BsDate | { year: number; month: number /* 1-12 */; day: number } |
| Paksha | "Shukla" \| "Krishna" |
| GeoLocation | { latitude: number; longitude: number } |
| Panchang | { tithi, tithiName, tithiNameNp, paksha, nakshatra, nakshatraName, nakshatraNameNp, yoga, yogaName, yogaNameNp, karana, karanaName, karanaNameNp, sunLongitude, moonLongitude } |
| SunMoonTimes | { sunrise, sunset, moonrise, moonset } — each Date \| null |
| CalendarEvent | { title: string; isHoliday: boolean } |
| BsDay | conversion + panchang + events for one day — see below |
| BsMonth | { year, month, totalDays, startWeekday, days: BsDay[] } |
Conversion
adToBs(date: Date): BsDate
bsToAd(bs: BsDate): Date
getToday(now?: Date): BsDate
bsDaysInMonth(year: number, month: number): number
bsDaysInYear(year: number): number
monthLengths(year: number): number[] // the 12 month lengths for a BS year
toISODate(date: Date): string // "YYYY-MM-DD"
MIN_BS_YEAR // 2000
MAX_BS_YEAR // 2090adToBs/bsToAd are exact round-trips within [MIN_BS_YEAR, MAX_BS_YEAR], derived from a single validated anchor (BS 2083-01-01 = 2026-04-14 AD) by walking an embedded per-year month-length table. Throws a RangeError outside that range.
Calendar — getBsDay / getBsMonth
The high-level functions most apps want: conversion + computed panchang + curated events, merged into one object.
getBsDay(year: number, month: number, gatey: number, location?: GeoLocation): BsDay
getBsMonth(year: number, month: number, location?: GeoLocation): BsMonthlocation defaults to Kathmandu (27.7172°N, 85.3240°E) and affects sunrise/sunset (and therefore which tithi is considered to prevail "at sunrise" for that day's panchang).
BsDay
interface BsDay {
year: number; month: number; gatey: number; // BS date
ad: string; // "2026-05-01" (ISO)
adDate: Date; // same, as a Date
weekday: number; // 0=Sun..6=Sat
paksha: "Shukla" | "Krishna";
tithi: number; // 1..30
panchang: Panchang; // full computed panchang at sunrise
sunrise: Date | null;
sunset: Date | null;
isHoliday: boolean;
holidays: string[]; // curated public-holiday titles (np)
events: string[]; // curated non-holiday event titles (np)
}Panchang (astronomy)
For when you need panchang at an arbitrary instant rather than a calendar day (e.g. "what's the tithi right now?").
computePanchang(date: Date): Panchang
getSunMoonTimes(date: Date, location?: GeoLocation): SunMoonTimes
lahiriAyanamsa(date: Date): number // degrees
KATHMANDU: GeoLocationcomputePanchang returns sidereal (nirayana) tithi/nakshatra/yoga/karana computed from the Sun and Moon's geocentric ecliptic longitude at that exact instant — it does not apply any day-boundary or sunrise convention itself (that's what getBsDay does on top of it).
Festivals & holidays
getDayEvents(year: number, month: number, gatey: number): DayEvents
listFestivals(year: number, opts?: { month?: number }): DatedEvents[]
getHolidays(year: number): DatedEvents[] // listFestivals(year).filter(d => d.isHoliday)
hasEventData(year: number): boolean
AVAILABLE_EVENT_YEARS: number[]interface DayEvents {
holidays?: string[];
events?: string[];
isHoliday?: boolean;
}
interface DatedEvents extends DayEvents {
month: number;
gatey: number;
}import { listFestivals } from "@namlo/nepali-calendar";
listFestivals(2083, { month: 7 });
// [{ month: 7, gatey: 4, holidays: ["विजया दशमी (दशैँ टीका)"], isHoliday: true }, ...]Formatting
toDevanagari(n: number): string // 2083 -> "२०८३"
fromDevanagari(s: string): number // "२०८३" -> 2083
formatBs(bs: BsDate, locale: "en" | "np"): string
BS_MONTHS_EN: string[] // ["Baishakh", "Jestha", ..., "Chaitra"]
BS_MONTHS_NP: string[] // ["वैशाख", "जेठ", ..., "चैत्र"]
WEEKDAYS_EN: string[] // ["Sunday", ..., "Saturday"]
WEEKDAYS_NP: string[] // ["आइतबार", ..., "शनिबार"]Data coverage & accuracy
- Conversion covers BS 2000–2090. BS 2083 is validated exactly against authoritative per-day Gregorian anchors published by the Nepal Panchanga Nirnayak Samiti (Rāṣṭriya Pañchāṅg). Other years rely on a community-sourced month-length table (cross-checked with
nepali-date-converter, MIT) and may differ by ±1 day in rare edge years around a month boundary. - Panchang is computed at each day's Kathmandu sunrise, following the traditional Nepali day-naming convention (the tithi prevailing at sunrise names the day). Pass a
locationtogetBsDay/getBsMonthto compute for elsewhere. - Festivals are curated, editorial data and currently ship for BS 2083 (2026–2027 AD). They're built from the official Rāṣṭriya Pañchāṅg and checked with automated assertions against the computed panchang (e.g. every Ekādaśī-named festival is asserted to land on tithi 11 or 26) so lunar leap-month (adhik māsa) shifts can't silently corrupt placement.
- Event/holiday titles are Nepali (Devanagari). English romanization is on the roadmap — contributions welcome.
Contributing festival data for a new year
- Add a raw source file at
scripts/raw/<YYYY>.json(seescripts/clean-source.tsfor the exact schema:{ year, months: { <MonthName>: { month_index, total_days, days: [{ gatey, date_ad, is_public_holiday, holidays_np, events_np }] } } }). - Run:
This normalizes the raw file intonpm run clean:datasrc/data/events/<YYYY>.jsonand regenerates the year index. - Add round-trip assertions for the new year's anchors in
test/convert.test.ts, and runnpm test. - Open a PR. Please cite the source (official panchang publisher, page/date) in the PR description.
Development
git clone <this-repo>
cd nepali-calendar
npm install
npm test # vitest — conversion round-trips, panchang sanity, festival lookups
npm run typecheck # tsc --noEmit
npm run build # tsup -> dist/ (ESM + CJS + .d.ts)Project layout:
src/
convert.ts BS <-> AD conversion
calendar.ts getBsDay / getBsMonth (merges conversion + panchang + events)
panchang.ts astronomy-engine based panchang + sun/moon rise-set
festivals.ts festival/holiday lookup over bundled data
format.ts Devanagari numerals, month/weekday names
data/
months.ts BS month-length tables
events/<YYYY>.json compiled festival data (generated — do not edit by hand)
scripts/
build-2083.ts authoritative BS 2083 festival builder (source of truth)
clean-source.ts raw/<YYYY>.json -> src/data/events/<YYYY>.json
test/ vitest suiteLicense
MIT © HamroAstrology.
- Month-length data cross-checked against
nepali-date-converter(MIT). - Panchang math powered by
astronomy-engine(MIT).
