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

nepali-date-kit

v2.0.0

Published

Complete Nepali (Bikram Sambat) date toolkit — a moment-style immutable date object, AD/BS conversion, formatting, and React date pickers and calendar.

Readme

🇳🇵 nepali-date-kit

The complete Bikram Sambat toolkit — a moment-style date object, AD ⇄ BS conversion, and React pickers and calendar, in one dependency-free package.

npm license

import nepaliDate from 'nepali-date-kit';

nepaliDate('2082-05-30').format('dddd, DD MMMM YYYY'); // "Monday, 30 Bhadra 2082"
nepaliDate('2082-05-30').toAd(); // "2025-09-15"
nepaliDate().add(1, 'month').fromNow(); // "in a month"

Contents


Install

npm install nepali-date-kit

React and React DOM are optional peer dependencies — install them only if you use the components. The core has no dependencies at all.


Why v2

v2 fixes three problems that affected every user of v1:

| Problem | What happened | Now | | --- | --- | --- | | The epoch was one day off | The converter anchored 1 Baisakh 2000 BS to 13 April 1943 instead of 14 April. Every single conversion was a day early, and the calendar grid patched over it with a +1 weekday offset — so dates and weekdays disagreed. | Anchored to 14 April 1943, verified against published calendars and round-tripped over all 33,247 supported days. | | Conversions changed with your timezone | Dates were built with Date.UTC(...) and read back with local getters. Outside Nepal the result could land on the wrong day. | All calendar maths runs on integer day numbers. Verified byte-identical across 8 timezones from UTC−11 to UTC+14. | | The pickers needed your Tailwind config | Components rendered Tailwind utility classes. If your app didn't have Tailwind scanning node_modules/nepali-date-kit, the popover lost its positioning and fell behind antd/MUI modals. | The stylesheet ships inside the bundle and is injected automatically. The panel is position: fixed with an explicit z-index. |

Plus: the calendar component is now actually Nepali (v1's rendered January–December), the react-native entry is no longer an empty file, and the published .d.ts no longer references types that were never shipped.

See Migrating from v1 for the breaking changes.


Quick start

import nepaliDate, { toAd, toBs, isAfter, format } from 'nepali-date-kit';

// Today, in BS
nepaliDate().format(); // e.g. "2082-05-30"

// Convert
toAd('2082-05-30'); // "2025-09-15"
toBs('2025-09-15'); // "2082-05-30"
toBs(new Date()); // today, in BS

// Compare — leave the second date out to compare with today
isAfter('2082-06-15'); // true
isAfter('2082-06-15', '2082-05-30'); // true

// Format, in English or Nepali
format('2082-05-30', 'DD MMMM YYYY'); // "30 Bhadra 2082"
format('2082-05-30', 'DD MMMM YYYY', 'bs', 'np'); // "३० भाद्र २०८२"

The nepaliDate object

nepaliDate(input?) returns an immutable BS date. Every method that changes something returns a new instance, so chains never mutate what you started with.

const joined = nepaliDate('2082-05-30');

joined.add(1, 'year'); // a new instance
joined.format(); // "2082-05-30" — unchanged

Reading input

| Call | Reads the input as | | --- | --- | | nepaliDate('2082-05-30') | BS | | nepaliDate({ year: 2082, month: 5, day: 30 }) | BS | | nepaliDate.fromAd('2025-09-15') | AD | | nepaliDate(new Date()) | AD (an absolute instant) | | nepaliDate(1757900000000) | AD (epoch milliseconds) | | nepaliDate(dayjsValue) / nepaliDate(momentValue) | AD (via $d / _d / toDate()) |

Bare strings are read as BS, because that is what this library is for. Date objects, epoch numbers and dayjs/moment values are absolute instants, so they are always AD. Separators -, / and . all work, a time component is optional, and Nepali digits (२०८२-०५-३०) are accepted.

Anything unreadable produces an invalid instance rather than an exception:

nepaliDate('nonsense').isValid(); // false
nepaliDate('nonsense').format(); // "Invalid Date"

Getters and setters

Call with no argument to read, with an argument to get a new instance back.

nepaliDate('2082-05-30').year(); // 2082
nepaliDate('2082-05-30').month(); // 5  — 1 = Baisakh
nepaliDate('2082-05-30').date(); // 30
nepaliDate('2082-05-30').day(); // 1  — 0 = Sunday

nepaliDate('2082-05-30').year(2083).format(); // "2083-05-30"
nepaliDate('2082-05-30').month(1).format(); // "2082-01-30"
nepaliDate('2082-05-30').day(0).format(); // "2082-05-29" — the Sunday of that week

Months are 1-indexed (1 = Baisakh … 12 = Chaitra), unlike moment's 0-indexed months. This keeps .month() equal to what YYYY-MM-DD prints.

Also available: hour(), minute(), second(), millisecond(), and the generic get(unit) / set(unit, value).

Arithmetic

nepaliDate('2082-12-30').add(1, 'day').format(); // "2083-01-01"
nepaliDate('2082-05-15').add(3, 'months').format(); // "2082-08-15"
nepaliDate('2082-05-15').subtract(1, 'year').format(); // "2081-05-15"
nepaliDate('2082-05-30 23:30').add(45, 'minutes').format('YYYY-MM-DD HH:mm'); // "2082-05-31 00:15"
nepaliDate('2082-05-15').add(1, 'quarter').format(); // "2082-08-15"

Units accept every spelling: year/years/y, quarter/Q, month/months/M, week/w, day/days/d, hour/h, minute/m, second/s, millisecond/ms. As in moment, capital M is month and lowercase m is minute.

Adding months clamps the day when the target month is shorter — the 32nd of Asar plus a month is the last day of Shrawan, never a spill into Bhadra.

Boundaries

nepaliDate('2082-05-15').startOf('month').format(); // "2082-05-01"
nepaliDate('2082-05-15').endOf('month').format(); // "2082-05-31"
nepaliDate('2082-05-15').startOf('week').format(); // "2082-05-15" — that Sunday
nepaliDate('2082-05-15').endOf('day').format('HH:mm:ss.SSS'); // "23:59:59.999"

startOf / endOf accept year, quarter, month, week, day, hour, minute, second.

Comparison

Every comparator defaults its second argument to now, and compares whole days unless you pass a unit.

nepaliDate('2082-06-15').isAfter(); // after today?
nepaliDate('2082-06-15').isAfter('2082-05-30'); // after that date?
nepaliDate('2082-06-15').isAfter('2082-05-30', 'month'); // a whole month later?

nepaliDate('2082-05-30').isBefore('2082-06-01');
nepaliDate('2082-05-30').isSame('2082-05-30');
nepaliDate('2082-05-30').isSameOrBefore('2082-05-30');
nepaliDate('2082-05-15').isBetween('2082-05-01', '2082-05-30'); // true
nepaliDate('2082-05-01').isBetween('2082-05-01', '2082-05-30'); // true — bounds included
nepaliDate('2082-05-01').isBetween('2082-05-01', '2082-05-30', 'day', '()'); // false — both excluded

isBetween includes both bounds by default ('[]'), which differs from moment's exclusive '()'; pass the notation explicitly to choose. The standalone isBetween(date, from, to, unit?, inclusivity?) behaves identically.

Convenience predicates: isToday(), isTomorrow(), isYesterday(), isWeekend() (Saturday), isLeapYear() (a 366-day BS year), isValid().

Differences and relative time

nepaliDate('2082-06-01').diff('2082-05-01', 'day'); // 31
nepaliDate('2082-06-01').diff('2082-05-01', 'month'); // 1
nepaliDate('2082-06-01').diff('2082-05-01', 'day', true); // 31 (unrounded float)

nepaliDate().subtract(2, 'day').fromNow(); // "2 days ago"
nepaliDate().add(3, 'hour').fromNow(); // "in 3 hours"
nepaliDate().subtract(2, 'day').fromNow(false, 'np'); // "२ दिन अगाडि"

nepaliDate().calendar(); // "Today"
nepaliDate().add(1, 'day').calendar(); // "Tomorrow"
nepaliDate().subtract(3, 'day').calendar(); // "Last Friday" (whichever weekday that was)

diff truncates toward zero, matching moment; pass true as the third argument for the exact fraction.

Output

const d = nepaliDate('2082-05-30 15:04:05');

d.format('YYYY-MM-DD'); // "2082-05-30"
d.toAd('DD MMMM YYYY'); // "15 September 2025"
d.toDate(); // native Date
d.toObject(); // { year: 2082, month: 5, day: 30, hour: 15, ... }
d.toArray(); // [2082, 5, 30, 15, 4, 5, 0]
d.toISOString(); // "2025-09-15T09:19:05.000Z" — from local time, so offset-dependent
d.valueOf(); // epoch ms
d.unix(); // epoch seconds
d.value; // "2082-05-30"
`${d}`; // "2082-05-30 15:04:05"

Calendar queries

nepaliDate('2082-05-30').daysInMonth(); // 31
nepaliDate('2082-05-30').daysInYear(); // 365
nepaliDate('2082-05-30').dayOfYear(); // 155
nepaliDate('2082-05-30').weekOfYear(); // 23
nepaliDate('2082-05-30').quarter(); // 2

Statics

nepaliDate.now(); // now
nepaliDate.today(); // today at 00:00
nepaliDate.fromAd('2025-09-15'); // read an AD value
nepaliDate.unix(1757900000); // from epoch seconds
nepaliDate.min('2082-05-30', '2081-01-01'); // earliest
nepaliDate.max(['2082-05-30', '2083-01-01']); // latest (array also works)
nepaliDate.isNepaliDate(value); // type guard
nepaliDate.minYear; // 2000
nepaliDate.maxYear; // 2090

Durations

import { duration } from 'nepali-date-kit';

duration(90, 'minutes').asHours(); // 1.5
duration({ days: 2, hours: 6 }).asHours(); // 54
duration(90, 'minutes').humanize(); // "2 hours"
duration({ days: 1, hours: 2 }).toISOString(); // "P1DT2H"

Formatting

format(template, lang?) on an instance, or format(input, template, calendar?, lang?) standalone. Templates use moment's tokens.

| Token | Output | Example | | --- | --- | --- | | YYYY YY | Year | 2082, 82 | | M MM | Month number | 5, 05 | | MMM MMMM | Month name | Bhd, Bhadra | | D DD Do | Day of month | 30, 30, 30th | | DDD DDDD | Day of year | 155, 155 | | d dd ddd dddd | Weekday | 1, Mo, Mon, Monday | | w ww | Week of year | 23, 23 | | Q | Quarter | 2 | | H HH | Hour, 24h | 15, 15 | | h hh | Hour, 12h | 3, 03 | | k kk | Hour, 1–24 | 15, 15 | | m mm | Minute | 4, 04 | | s ss | Second | 5, 05 | | S SS SSS | Fractional second | 0, 07, 078 | | A a | Meridiem | PM, pm | | Z ZZ | UTC offset | +05:45, +0545 | | X x | Unix seconds / ms | 1757… | | [...] | Literal text | [on] YYYY → on 2082 |

Anything not a token passes through, so YYYY-MM-DD, DD/MM/YYYY and YYYY年MM月 all work. Wrap letters you want kept verbatim in square brackets.

Nepali output — pass 'np' as the language. Digits become Devanagari and names are translated; bracketed literals are left alone.

format('2082-05-30', 'DD MMMM YYYY, dddd', 'bs', 'np'); // "३० भाद्र २०८२, सोमबार"
format('2082-05-30', 'DD MMMM YYYY', 'ad', 'np'); // "१५ सेप्टेम्बर २०२५"

Cross-calendar — the third argument picks which calendar to print. The input is always read as BS.

format('2082-05-30', 'YYYY-MM-DD', 'ad'); // "2025-09-15"

Standalone functions

Every instance method has a function form, for when you're working with plain strings.

Conversion — toAd(input, template?, lang?), toBs(input, template?, lang?), toAdDate(input), format(input, template?, calendar?, lang?), parse(input, calendar?)

Comparison — isAfter, isBefore, isSame (alias isEqual), isSameOrAfter, isSameOrBefore, isBetween, isToday, isTomorrow, isYesterday, isWeekend, isLeapYear, isValidDate

isAfter(date1, date2?, precision?); // date2 defaults to today, precision to 'day'

Arithmetic — add, subtract, diff, startOf, endOf, setDate, setMonth, setYear, addDaysFromToday, subtractDaysFromToday

Boundaries — startOfDay, endOfDay, getMonthStartDate, getMonthEndDate, getYearStartDate, getYearEndDate, getWeekStartDate, getWeekEndDate

Ranges and calendar data — getDatesBetween, daysBetween, getMonthDates, getSupportedYears, getWeekDay, getDaysInMonth, getDaysInYear, getCalendarMatrix

Relative time — fromNow, from, calendarTime, relativeTime, duration

getDatesBetween('2082-05-01', '2082-05-03'); // ["2082-05-01", "2082-05-02", "2082-05-03"]
daysBetween('2082-05-01', '2082-05-11'); // 10
getSupportedYears(); // [2000, 2001, ..., 2090]

getCalendarMatrix

Build your own grid, with the same data the components use.

import { getCalendarMatrix } from 'nepali-date-kit';

const { weeks, cells, weekdays, monthName } = getCalendarMatrix({ year: 2082, month: 5 });
// weeks: 6 rows of 7 cells
// each cell: { date, year, month, day, weekday, ad, adDay, isCurrentMonth, isToday, isWeekend }

Options: year, month, showOutsideDays (default true), weekStartsOn (default 0 = Sunday), fixedWeeks (default true, always 6 rows so the height never jumps).


React components

import { DatePicker, RangePicker, NepaliCalendar } from 'nepali-date-kit/react';

Styles are injected automatically — no CSS import and no Tailwind config required. If you'd rather control it (server rendering, CSP), import nepali-date-kit/style.css instead.

<DatePicker />

const [date, setDate] = useState('');

<DatePicker value={date} onChange={setDate} color="#2563eb" theme="dark" allowAd />;

onChange gives you a BS YYYY-MM-DD string, or '' when cleared.

<RangePicker />

const [range, setRange] = useState([]);

<RangePicker value={range} onChange={setRange} minDate="2082-01-01" />;

onChange gives you [start, end], always in order even if you click the end first, or [] when cleared.

Picker props

| Prop | Type | Default | Notes | | --- | --- | --- | --- | | value | string / string[] | — | BS YYYY-MM-DD. | | onChange | (value) => void | — | | | color | string | theme accent | Selected-day colour. Readable text and the range fill are derived from it. | | theme | 'light' \| 'dark' \| 'auto' | 'light' | auto follows the OS. | | tokens | Partial<TThemeTokens> | — | Override individual colours, radius or shadow. | | className / style | | | Applied to the trigger. | | panelClassName / panelStyle | | | Applied to the popover. | | size | 'small' \| 'medium' \| 'large' | 'medium' | Trigger height. | | minDate / maxDate | TInputDate | — | | | disabledDate | (cell) => boolean | — | Reject individual dates. | | allowAd | boolean | false | Show the AD day in each cell. | | lang | 'en' \| 'np' | 'en' | | | displayFormat | string | 'YYYY-MM-DD' | Any format template. | | placeholder | string | 'Select date' | | | allowClear | boolean | true | | | disabled | boolean | false | | | weekStartsOn | 0–6 | 0 | | | placement | 'auto' \| 'top' \| 'bottom' | 'auto' | | | zIndex | number | 9999 | | | getPopupContainer | () => HTMLElement | document.body | | | inputRender | (text, open) => ReactNode | — | Replace the trigger. | | onOpenChange | (open) => void | — | | | name | string | — | Emits a hidden input for plain HTML forms. |

Positioning

The panel is portalled to document.body, positioned fixed, flipped above the trigger when there isn't room below, and clamped to the viewport. zIndex defaults to 9999, which clears antd's modal (1000) and MUI's (1300). Raise it if your own overlay sits higher:

<DatePicker zIndex={20000} />

Styling

Every colour is a CSS custom property, so className can override anything:

.my-picker {
	--ndk-accent: #16a34a;
	--ndk-radius: 4px;
	--ndk-border: #cbd5e1;
}
<DatePicker className="my-picker" />

Available: --ndk-bg, --ndk-fg, --ndk-muted, --ndk-border, --ndk-hover, --ndk-accent, --ndk-accent-fg, --ndk-range-bg, --ndk-disabled, --ndk-weekend, --ndk-today-ring, --ndk-radius, --ndk-shadow.

<NepaliCalendar />

A full-month BS calendar with events.

<NepaliCalendar
	size="large"
	allowAd
	events={[
		{ date: '2082-05-12', title: 'Launch', color: '#22c55e' },
		{ date: '2025-09-15', calendar: 'ad', title: 'From an AD date' },
	]}
	onClick={(cell) => console.log(cell.date, cell.ad)}
	onEventClick={(event) => console.log(event.title)}
/>

Sizes

| Size | Shows | | --- | --- | | small | The date, plus one coloured dot per event | | medium | The above, plus 1 titled event chip | | large | The above, plus up to 3 titled event chips |

Any events beyond the limit are summarised as +N more. Dots appear at every size, so a busy day is visible even in small.

Calendar props

| Prop | Type | Default | Notes | | --- | --- | --- | --- | | events | TCalendarEvent[] | [] | { date, title?, color?, calendar?, isAd? }. date is BS unless calendar: 'ad'. | | size | 'small' \| 'medium' \| 'large' | 'medium' | | | allowAd | boolean | true | AD day in each cell, AD month span in the header. Set false to also hide events marked isAd. | | onClick | (cell, event) => void | — | Any date cell click. | | onEventClick | (event, cell) => void | — | Doesn't also fire onClick. | | onSelect | (date) => void | — | | | onMonthChange | ({ year, month }) => void | — | | | value / defaultValue | string | — | Controlled / uncontrolled selection. | | year / month | number | — | Controlled visible month. | | renderCell | (cell, events) => ReactNode | — | Replace a cell's contents. | | disabledDate | (cell) => boolean | — | | | showHeader | boolean | true | | | className / style / cellClassName | | | | | color / theme / tokens / lang / weekStartsOn | | | As per the pickers. |

Works with your form library

The pickers are plain controlled components, so they drop into anything:

// react-hook-form
<Controller name="joined" control={control} render={({ field }) => <DatePicker {...field} />} />;

// Formik
<DatePicker value={values.joined} onChange={(v) => setFieldValue('joined', v)} />;

// antd Form
<Form.Item name="joined"><DatePicker /></Form.Item>;

React Native

React Native has no DOM, and a calendar there should use your own design system. So this entry ships headless hooks — they do the calendar maths and state, you render with View / Text / Pressable.

import { useNepaliCalendar } from 'nepali-date-kit/react-native';

const cal = useNepaliCalendar({ value, onChange: setValue, lang: 'np' });

<View>
	<Text>{cal.monthName} {cal.localizeDigits(cal.year)}</Text>

	{cal.matrix.weeks.map((week, i) => (
		<View key={i} style={{ flexDirection: 'row' }}>
			{week.map((cell) => (
				<Pressable key={cell.date} disabled={cal.isDisabled(cell)} onPress={() => cal.select(cell.date)}>
					<Text style={cell.isToday && styles.today}>{cal.localizeDigits(cell.day)}</Text>
				</Pressable>
			))}
		</View>
	))}

	<Button title="Next" onPress={cal.nextMonth} disabled={!cal.canGoNext} />
</View>;

useNepaliCalendar returns matrix, year, month, monthName, weekdayNames, selected, today, nextMonth, prevMonth, goToToday, setMonth, select, isDisabled, localizeDigits, canGoNext, canGoPrev.

There's also useNepaliDate(initial?, displayFormat?, lang?) for when you just need a value holder, and the entire core API is re-exported from this entry.


Browser / CDN

<script src="https://cdn.jsdelivr.net/npm/nepali-date-kit@2"></script>
<script>
	NepaliDateKit.toBs(new Date()); // "2082-05-30"
	NepaliDateKit.nepaliDate().format('DD MMMM YYYY'); // "30 Bhadra 2082"
</script>

The CDN build is the core utilities only — no React, no DOM requirement.


Accuracy

The calendar covers 2000–2090 BS (1943–2034 AD). Bikram Sambat month lengths are set by astronomical observation and published year by year, so they can't be computed; the table lists every year explicitly.

The epoch is 1 Baisakh 2000 BS = 14 April 1943 AD, a Wednesday, cross-checked against Hamro Patro, Ashesh and nepalicalendar. The test suite verifies:

  • Every published New Year anchor from 2000 to 2083 BS, with its weekday.
  • A BS → AD → BS round trip for all 33,247 supported days, confirming the AD date advances by exactly one day per BS day.
  • Byte-identical output across 8 timezones from Pacific/Midway (UTC−11) to Pacific/Kiritimati (UTC+14), including both directions of a US DST transition.

Dates outside the range throw a descriptive RangeError from the converter functions, and produce an invalid instance from nepaliDate() — never a silently wrong answer.


Migrating from v1

v2 corrects a one-day error in every conversion. If you have BS dates stored in a database that were produced by v1, they are one day early and need shifting.

Otherwise, the breaking changes:

1. nepaliDate() returns an object, not a string.

nepaliDate(); // v1: "2082-05-30"   v2: a chainable instance

nepaliDate().format(); // "2082-05-30"
nepaliDate().value; // "2082-05-30"
`${nepaliDate()}`; // "2082-05-30" — template literals still work
toBs(); // "2082-05-30" — still a plain string

2. Format tokens now match moment.

| v1 | v2 | | --- | --- | | M → 05 | M → 5, MM → 05 | | MM → Bhd | MMM → Bhd | | MMM → Bhadra | MMMM → Bhadra | | dd → Mon | dd → Mo, ddd → Mon | | ddd → Monday | dddd → Monday |

So 'YYYY-M-DD' becomes 'YYYY-MM-DD', and 'MMM YYYY' becomes 'MMMM YYYY'.

3. Comparators take whole days by default and default to today.

isAfter('2082-06-15'); // v1: threw    v2: compares with today
isAfter(a, b); // v2: ignores time-of-day
isAfter(a, b, true); // v2: exact, to the millisecond (the v1 `strict` flag still works)

4. startOfDay / endOfDay return an instance, not a Date. Call .toDate() for the old value. nepaliDateFormat() still returns a Date.

5. Component renames. BsCalendar → NepaliCalendar (the old name is still exported). The calendar's props changed substantially — it renders a BS month now.

6. Types are real exports. v1's .d.ts referenced TInputDate and friends without shipping them, so consumers saw Cannot find name. Import them instead:

import type { TInputDate, TNepaliDate, TCalendarCell } from 'nepali-date-kit';

7. No Tailwind. Remove nepali-date-kit from your Tailwind content globs and drop any dist/output.css import — styles ship with the components.


License

MIT © Nischal Adhikari