# PersianDate wraps Date instead of patching it

> PersianDate is a JavaScript library for parsing, validating, manipulating and formatting Persian (Jalali) dates, with a Gregorian mode and a switchable leap year rule. It leaves the native Date object alone and wraps it, which keeps global state out of your app but means every conversion is explicit.

**babakhani/PersianDate** — javascript date library for parsing, validating, manipulating, and formatting persian dates System.

- Repository: https://github.com/babakhani/PersianDate
- Website: http://babakhani.github.io/PersianWebToolkit/doc/persian-date/
- Stars: 607 · Forks: 110
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/babakhani-persiandate

## A wrapper object rather than a patched Date.prototype

Most date libraries of this type either patch the native Date object or replace it outright. PersianDate refuses both. The documentation states that instead of modifying Date.prototype, persianDate creates a wrapper for the Date object, and you get that wrapper by calling persianDate() with one of the supported input types.

The supported inputs cover the cases a real application hits. Calling with no arguments gives the current moment, and the docs note this is essentially the same as passing new Date(). A millisecond count since the Unix epoch works like the native constructor, a Unix timestamp in seconds goes through persianDate.unix(Number), an existing native Date instance is accepted directly, and an array of numbers mirrors the parameter list of new Date() in Persian year, month, day, hour, minute, second and millisecond order.

That array form is where the design gets opinionated. Passing [1393, 1, 14, 15, 25, 50, 125] yields a Persian date, and the docs point out that from version 1.0 you can hand it a Gregorian array as well, but only after switching the calendar type with toCalendar('gregorian'). The array is positional and Persian by default, so an application that mixes calendars needs to be deliberate about which mode is active when a value is constructed.

## Global switches for calendar, leap year rule and locale

Three global settings shape the output, and each one has a per-instance override. The first is toCalendar, whose default is persian and whose alternative is gregorian. Switching globally and then constructing an object looks like this:

```javascript
persianDate.toCalendar("gregorian");
new persianDate([2017]).year(); // 2017
```

The second is toLeapYearMode, with algorithmic as the default and astronomical as the alternative. The documentation explains the difference: the astronomical rule counts the days elapsed between equinoxes at the reference meridian, while the algorithmic rule follows the Behrooz-Birashk algorithm. You can set it globally or on one instance:

```javascript
persianDate.toLeapYearMode("algorithmic");
```

The documentation also states that toLeapYearMode only has an effect when the calendar type is persian, and does nothing for the gregorian calendar. That is a sensible guard, but it also means the setting is inert unless it is set after a calendar switch, which is an easy ordering mistake to make.

The third is toLocale, defaulting to fa with en as the other option. The locale controls digit rendering and month names, and after version 1.0 you use toLocale instead of the older formatPersian when you want Persian or English digits.

## Locale decides the digits, the month names and the meridiem

The English locale produces ASCII output, which is the practical reason to reach for it in an English interface:

```javascript
persianDate.toLocale("en");
new persianDate([1396, 6, 17]).format(); // "1396-06-17 00:00:00 AM"
new persianDate([1396, 6, 17]).format("dddd"); // "Friday"
new persianDate([1396, 6, 17]).format("MMMM"); // "Shahrivar"
```

Those are the documented English results for a Persian date of 1396-06-17. Under the fa locale the same input renders with Persian digits and a Persian month name, and the format() output carries a Persian meridiem marker. The month names come from the locale, not from the calendar type, so switching to gregorian while keeping fa gives Gregorian month names in Persian script.

The documented examples use moment-style formatting tokens such as dddd for the weekday name and MMMM for the full month name. The library is described as inspired by momentjs, and the moment resemblance is deliberate enough that a developer who has used moment will find the API familiar.

One documentation quirk is worth knowing about. The example under the English locale combines a Gregorian calendar switch with a Persian input array and shows a Gregorian-style output, which is the clearest illustration that calendar type and input interpretation are linked rather than independent.

## Partial arrays default to the first day of the period

The array constructor is forgiving about missing components, and the documented behaviour is that any value past the year is optional and defaults to the lowest possible number:

```javascript
new persianDate([1392]); // Farvardin 1st
new persianDate([1392, 6]); // Shahrivar 1st
new persianDate([1392, 6, 10]); // Shahrivar 10th
```

So a year alone means the first day of Farvardin, and a year and month means the first day of that month. This is convenient for form defaults and for a date picker that only knows the month.

There is a companion behaviour on the reading side, documented in the same section. Instead of modifying the native Date.prototype, persianDate creates a wrapper for the Date object, so getting a Persian date out of an existing native Date means constructing a new wrapper around it, and the docs describe that as the fastest way to get a persianDate wrapper.

The practical constraint follows from the design. Because the native Date is never touched, there is no PersianDate.parse to collide with, and no global method to override. Instead, the API surface stays small and predictable, and each conversion is an explicit new persianDate(...) call in your code.

## Installing from npm, then loading it in a browser or a bundler

Installation is a single npm command, and the documentation recommends saving it as a dev dependency:

```shell
npm install persian-date --save-dev
```

For a plain browser page without a build step, the library ships an IIFE bundle that you can point a script tag at from node_modules:

```html
<script src="node_modules/persian-date/dist/persian-date.iife.js" type="text/javascript"></script>
```

In a Node or bundler context the module is imported by name:

```javascript
require('persian-date');
```

The package manifest backs up the range of entry points. The main field points at dist/persian-date.js, the module field at dist/persian-date.esm.mjs, the browser field at dist/persian-date.iife.js, and unpkg and jsdelivr both point at the minified IIFE bundle. An exports map exposes the package root with separate import and require conditions, plus a package.json subpath, so both ESM and CommonJS bundlers resolve without path guessing.

The package is version 1.2.1 and its keywords include calendar, date, jalali-date, khayyam and persian-date. The author field names Reza Babakhani, and the repository points at git@github.com:babakhani/PersianDate.git.

## A custom licence next to an MIT field in package.json

The licence story needs care. The package.json lists the licence as MIT, which is the field npm and most tooling will read. The repository itself is classified by GitHub as Other, described as a custom licence that GitHub cannot classify, with a pointer to the LICENSE file.

That combination is unusual enough that you should open the LICENSE file before shipping anything commercial, since the MIT string in the manifest and the actual terms in the file may not say the same thing. This is not legal advice, it is a practical instruction: the manifest field is what a dependency scanner will show you, and the file is what the maintainer actually chose.

The repository also carries the usual supporting files. There is a CHANGELOG.md, a src directory with the implementation, a test directory, a typings directory for TypeScript declarations, a doc directory, and a dist directory with the built bundles. The development tooling in package.json uses Vite for both the watch build and the production build, mocha with nyc for coverage, oxlint for linting, and jsdoc2md to generate doc/document.md from src/pDate.js.

A detail worth flagging is the doctest script. The package can run markdown-doctest against README.md and doc/leapyear.md, plus markdown-link-check on the same files, which means the code examples in the documentation are executed as part of the project's own checks. That is a good sign for the accuracy of the snippets you are reading here.

## Release history: one recent tag after a long gap

The tag history explains both the current state and the risk. The latest release is v1.2.1, dated 2026-05-23. Before that sit v1.1.0 from 2019-08-10 and v1.0.5 from 2018-02-10, so the gap between the second and third releases is roughly seven years while the most recent release arrived after a long dormant period. The last push was on 2026-08-20.

The version numbering also tells you where the breaking changes landed. The documentation repeatedly notes behaviour that changed from version 1.0 onward, most importantly that support for the Gregorian calendar arrived in 1.0, that both leap year modes became selectable in 1.0, and that toLocale replaced formatPersian for controlling Persian or English digits. A project upgrading from a pre-1.0 release has real work to do, and the CHANGELOG.md file is where that history is recorded.

Two supporting signals are visible in the repository. There is a .travis.yml, the older continuous integration configuration, alongside a .github directory, which suggests the newer workflow is now the one in use. The typing support also goes beyond a single d.ts file: the repository has a typings directory and a vite.config.ts, so the package was set up with TypeScript in mind rather than having types bolted on later.

## Conclusion

PersianDate suits an application that renders Persian dates for end users and needs a moment style API without pulling in moment itself, particularly a TypeScript or bundler project that can consume the ESM build at dist/persian-date.esm.mjs. It is the wrong choice if you need a maintained, fully typed date library with a large plugin ecosystem, or if your team cannot accept a library whose global toCalendar and toLocale calls leak across unrelated modules. Before adopting it, install the package, call persianDate.toCalendar("gregorian") in one test and check the output, then read the LICENSE file, because the package manifest says MIT while the repository carries a custom licence that GitHub could not classify.

## FAQ

### How do I install PersianDate and format a Persian date in JavaScript?

The documented install is npm install persian-date --save-dev, then require('persian-date') in Node or a bundler. Constructing with new persianDate() and calling format() gives the current date and time in the Persian calendar.

### How do I switch PersianDate to the Gregorian calendar and English output?

Call persianDate.toCalendar("gregorian") and persianDate.toLocale("en"), either globally or on a single instance with new persianDate(...).toCalendar("gregorian").toLocale("en"). The documented English format output for 1396-06-17 is 1396-06-17 00:00:00 AM.

### Which leap year rule does PersianDate use and can I change it?

The default mode is algorithmic, based on the Behrooz-Birashk algorithm, and the alternative is astronomical, which counts days between equinoxes. Set it globally with persianDate.toLeapYearMode("algorithmic") or per instance, and it has no effect while the calendar type is gregorian.

## Sources

- [babakhani/PersianDate on GitHub](https://github.com/babakhani/PersianDate)
- [Issues](https://github.com/babakhani/PersianDate/issues)
- [Project website](http://babakhani.github.io/PersianWebToolkit/doc/persian-date/)
- [README](https://github.com/babakhani/PersianDate/blob/master/README.md)
- [Releases](https://github.com/babakhani/PersianDate/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/babakhani-persiandate
