jalali-astro
v1.6.1
Published
Astronomical Gregorian and Jalali date conversion based on the Iranian calendar's equinox rule.
Readme
jalali-astro
Accurate Gregorian and Jalali date conversion for JavaScript and TypeScript, with leap years derived from astronomical Nowruz calculations instead of fixed arithmetic cycles.
jalali-astro implements the equinox-based rule of the Jalali calendar, also known as the Iranian or Persian calendar. It computes Nowruz from the vernal equinox in Iran Standard Time and derives leap years from the distance between consecutive Nowruz dates.
The library provides a complete API for working with Jalali dates, including conversion, validation, formatting, parsing, date arithmetic, week calculations, derived dates, and immutable date manipulation.
Why jalali-astro?
Many Jalali date libraries use fixed arithmetic leap-year cycles. These rules are simple and fast, but they are approximations of the calendar. In some years, cycle-based methods can place Nowruz on the wrong Gregorian day.
jalali-astro does not use a fixed leap-year cycle. Instead, it follows the astronomical rule of the Jalali calendar: Nowruz is determined from the vernal equinox in Iran civil time, and leap years are derived from the number of days between consecutive Nowruz dates.
As a result, jalali-astro follows the astronomical definition of the Jalali calendar rather than approximating it with a repeating arithmetic cycle.
تفاوت اصلی این بسته با بسیاری از کتابخانههای رایج جلالی این است که برای تشخیص سال کبیسه از چرخههای عددی ثابت استفاده نمیکند.
این بسته زمان نوروز را بر پایهٔ محاسبهٔ نجومی اعتدال بهاری در زمان رسمی ایران بهدست میآورد و کبیسه بودن هر سال را از فاصلهٔ میان دو نوروز پیاپی تعیین میکند.
در نتیجه، در سالهایی که روشهای چرخهای ممکن است روز آغاز سال جلالی را اشتباه محاسبه کنند، این بسته همچنان از قاعدهٔ مبتنی بر اعتدال بهاری پیروی میکند.
Astronomical calendar rule
jalali-astro calculates Jalali calendar dates from astronomical estimates of the vernal equinox. It uses the equinox algorithm described by Jean Meeus in Astronomical Algorithms, 2nd edition, Chapter 27, and is intended to provide high-accuracy results, especially for modern dates.
In the Jalali calendar, the first day of the year, Nowruz, is determined by the time of the vernal equinox in Iran civil time (UTC+03:30). If the equinox occurs before local noon, Nowruz falls on that day. If it occurs at or after local noon, Nowruz falls on the following day.
Because this rule depends on which side of local noon the equinox falls, dates near that boundary require particular care. When the calculated equinox time is very close to noon, small differences in astronomical models, time-scale conventions, or rounding methods can place the event on either side of the boundary. In these rare cases, the resulting Nowruz date may differ by one day.
Supported date range
The astronomical calculation is supported from 1001 BCE through 3000 CE in the proleptic Gregorian calendar, corresponding approximately to Jalali years -1622 through 2378.
jalali-astro enforces this range for date conversions that depend on the equinox calculation.
Features
Astronomical conversion
- Gregorian to Jalali and Jalali to Gregorian conversion
- Leap years derived from vernal equinox calculations, not fixed cycles
- Nowruz calculation based on Iran civil time
- Access to the vernal equinox used for a Jalali year
Date creation and validation
- Create dates from Jalali year, month, and day
- Create dates from Gregorian dates, ISO date strings, JavaScript
Dateobjects, Unix timestamps, and Julian Day Numbers - Create dates from day of year, week of year, or nth weekday of a month
Date arithmetic and derived dates
- Add or subtract days, months, and years
- Get the start or end of a year, quarter, month, or week
- Calculate differences in days, months, and years
- Calculate age in complete years
- Weeks start on Saturday and end on Friday
Immutable date object
- Immutable
JalaliDatevalue object - Rich date properties, including year, quarter, month, day, day of year, week of year, week of month, and Julian Day Number
- Fluent helpers for changing year, month, or day without mutating the original date
- Comparison helpers such as
equals,isBefore,isAfter,isBetween,isSameWeek, andisSameQuarter
Formatting and parsing
- Format dates with Persian month names, weekday names, quarter names, and Persian-Indic digits
- Include corresponding Gregorian date components with scoped calendar blocks such as
[gregorian:YYYY/MM/DD] - Parse dates with Persian or Latin digits
- Pattern-based parsing with validation of month names, weekday names, quarter names, and scoped Gregorian components
- Optional Right-to-Left Mark handling for correct display of Persian text
Quality and compatibility
- Full TypeScript type definitions
- ES module and CommonJS builds
- Direct browser usage via IIFE build
- Zero runtime dependencies
- Comprehensive test suite
Installation
npm install jalali-astroQuick start
import { JalaliDate } from 'jalali-astro';
// Convert from Gregorian to Jalali
const nowruz1404 = JalaliDate.fromGregorian(2025, 3, 21);
console.log(nowruz1404.toString()); // "1404/01/01"
console.log(nowruz1404.isLeapYear); // false
// Convert to Gregorian
const g = nowruz1404.toGregorian(); // { year: 2025, month: 3, day: 21 }
// Today's Jalali date in Tehran civil time
const today = JalaliDate.today();
const yesterday = today.addDays(-1);
const nextMonth = today.addMonths(1);
// Comparison
if (today.isAfter(nowruz1404)) {
console.log('Today is after Nowruz 1404');
}
// Create a fixed date for demonstration
const date = new JalaliDate(1405, 2, 14);
console.log(date.toString()); // "1405/02/14"
console.log(date.monthName); // "اردیبهشت"
console.log(date.dayOfWeekName); // "دوشنبه"
console.log(date.weekOfYear); // 7
// Formatting with Persian numerals and names
console.log(date.format('DDDD D MMMM YYYY'));
// "دوشنبه ۱۴ اردیبهشت ۱۴۰۵"
// Include the corresponding Gregorian date with a scoped calendar block
console.log(date.format('DDDD، YYYY/MM/DD برابر با [gregorian:YYYY/MM/DD میلادی]'));
// "دوشنبه ۱۴۰۵/۰۲/۱۴ برابر با ۲۰۲۶/۰۵/۰۴ میلادی"
// Parsing (supports Persian and Latin digits)
const parsed1 = JalaliDate.parse('1405/02/14');
const parsed2 = JalaliDate.parse('۱۴۰۵/۰۲/۱۴');
const parsed3 = JalaliDate.parse('۱۴ اردیبهشت ۱۴۰۵', 'D MMMM YYYY');Module formats
The package ships with ES module, CommonJS, and browser (IIFE) builds. Full TypeScript declarations are included.
Node.js and bundlers
// ES module, TypeScript, and modern bundlers
import { JalaliDate } from 'jalali-astro';// CommonJS
const { JalaliDate } = require('jalali-astro');Browser usage
Modern browsers (ES modules):
<script type="module">
import { JalaliDate } from 'https://esm.sh/jalali-astro';
const today = JalaliDate.today();
console.log(today.format('DDDD، D MMMM YYYY'));
</script>All browsers (IIFE global):
A minified browser bundle is available for CDN/production use (dist/index.min.global.js). The unminified IIFE build (dist/index.global.js) is also available for debugging or local inclusion. Source maps are published alongside the minified bundle to aid debugging.
<!-- Production (minified) -->
<script src="https://unpkg.com/jalali-astro/dist/index.min.global.js"></script>
<!-- Or the unminified IIFE for debugging -->
<script src="https://unpkg.com/jalali-astro/dist/index.global.js"></script>
<script>
const { JalaliDate } = JalaliAstro;
const today = JalaliDate.today();
document.getElementById('current-date').textContent =
today.format('DDDD، D MMMM YYYY', { rlm: 'auto' });
</script>Building and testing
npm install
npm run build
npm testAPI Reference
JalaliDate
Immutable value object representing a Jalali calendar date.
import { JalaliDate, DayOfWeek, Occurrence, Quarter } from 'jalali-astro';Constructor
new JalaliDate(year, month, day)
Creates a Jalali date from explicit Jalali year, month, and day.
const date = new JalaliDate(1404, 1, 1);
console.log(date.toString()); // "1404/01/01"Throws RangeError if the year, month, or day is outside the supported range.
Static factories
JalaliDate.today()
Returns today's Jalali date in Tehran civil time (UTC+03:30), based on the current system time.
const today = JalaliDate.today();JalaliDate.yesterday()
Returns yesterday's Jalali date in Tehran civil time (UTC+03:30), based on the current system time.
const yesterday = JalaliDate.yesterday();JalaliDate.tomorrow()
Returns tomorrow's Jalali date in Tehran civil time (UTC+03:30), based on the current system time.
const tomorrow = JalaliDate.tomorrow();JalaliDate.fromUnixTime(unixTime)
Creates a JalaliDate from a Unix timestamp (milliseconds since 1970-01-01T00:00:00Z).
The timestamp is interpreted in Tehran civil time (UTC+03:30) to determine the corresponding Jalali date.
const today = JalaliDate.fromUnixTime(Date.now());
const specificDate = JalaliDate.fromUnixTime(1609459200000); // 2021-01-01 00:00:00 UTCJalaliDate.fromDate(date)
Creates a Jalali date from a JavaScript Date.
The Date instant is interpreted in Tehran civil time (UTC+03:30) to determine the corresponding Jalali date.
const date = JalaliDate.fromDate(new Date());JalaliDate.fromGregorian(gYear, gMonth, gDay)
Creates a Jalali date from a proleptic Gregorian date.
const date = JalaliDate.fromGregorian(2025, 3, 21);
console.log(date.toString()); // "1404/01/01"Throws RangeError if the Gregorian date is invalid or outside the supported range.
JalaliDate.fromIsoDateString(isoString)
Creates a JalaliDate from an ISO 8601 date string in the format "YYYY-MM-DD".
const date = JalaliDate.fromIsoDateString("2025-03-21");
console.log(date.toString()); // "1404/01/01"Throws Error if the input string is not a valid ISO date.
Throws RangeError if the Gregorian date is invalid or out of supported range.
JalaliDate.fromDayOfYear(year, dayOfYear)
Creates a Jalali date from a Jalali year and 1-based day of year.
const nowruz = JalaliDate.fromDayOfYear(1404, 1);
console.log(nowruz.toString()); // "1404/01/01"Throws RangeError if dayOfYear is outside the valid range for the given year.
JalaliDate.fromWeekOfYear(year, weekNumber, dayOfWeek)
Creates a Jalali date from a year, week number, and day of week.
Week 1 is the week containing 1 Farvardin. Weeks start on Saturday (6) and end on Friday (5).
const saturday = JalaliDate.fromWeekOfYear(1404, 1, DayOfWeek.Saturday);
const friday = JalaliDate.fromWeekOfYear(1404, 1, DayOfWeek.Friday);Throws RangeError if the parameters are out of range or the resulting date is invalid.
JalaliDate.fromNthWeekdayOfMonth(year, month, nth, dayOfWeek)
Creates a Jalali date for the nth occurrence of a weekday within a specific month.
Positive nth values count from the beginning of the month (1 = first occurrence, 2 = second, etc.).
Negative nth values count from the end of the month (-1 = last occurrence, -2 = second-to-last, etc.).
// Second Tuesday of Ordibehesht 1405
const date = JalaliDate.fromNthWeekdayOfMonth(1405, 2, Occurrence.Second, DayOfWeek.Tuesday);
// Last Friday of Esfand 1404
const lastFriday = JalaliDate.fromNthWeekdayOfMonth(1404, 12, Occurrence.Last, DayOfWeek.Friday);
// First Saturday of Farvardin 1404
const firstSat = JalaliDate.fromNthWeekdayOfMonth(1404, 1, Occurrence.First, DayOfWeek.Saturday);Throws RangeError if the parameters are out of range or the nth occurrence doesn't exist in the month.
JalaliDate.fromJDN(jdn)
Creates a Jalali date from a Julian Day Number.
const date = JalaliDate.fromJDN(2460756);Throws RangeError if the Julian Day Number is outside the supported conversion range.
JalaliDate.parse(str, pattern?, options?)
Parses a Jalali date string using a format pattern. The default pattern is YYYY/M/D.
Format tokens outside a scoped calendar block use the Jalali calendar. The same is true inside an explicit [jalali:...] block. Gregorian components can be included with a [gregorian:...] block and are validated against the parsed Jalali date.
Pattern tokens
| Token | Jalali meaning | Gregorian block meaning |
| ------ | ------------------------------------ | ------------------------------------------------------ |
| YY | 2-digit Jalali year | 2-digit Gregorian year |
| YYYY | Full Jalali year | Full Gregorian year |
| M | Jalali month number | Gregorian month number |
| MM | Zero-padded Jalali month number | Zero-padded Gregorian month number |
| MMMM | Persian Jalali month name | Persian Gregorian month name |
| D | Jalali day of month | Gregorian day of month |
| DD | Zero-padded Jalali day of month | Zero-padded Gregorian day of month |
| DDDD | Persian weekday name | Persian weekday name |
| Q | Persian Jalali quarter (season) name | Literal Q; Gregorian quarter names are not supported |
Jalali year/month/day tokens are required to construct a JalaliDate.
Pattern syntax
Scoped calendar blocks are defined with square brackets and a calendar name prefix followed by a colon. Nested blocks are not supported.
| Syntax | Meaning |
| ------------------------ | -----------------------------------------|
| YYYY/MM/DD | Implied Jalali calendar block (default) |
| [jalali:YYYY/MM/DD] | Explicit Jalali calendar block |
| [gregorian:YYYY/MM/DD] | Gregorian calendar block |
| 'text' or "text" | Quoted literal text |
The parser follows these rules:
- Persian-Indic and Latin digits are accepted in numeric fields.
- By default, bidirectional control characters (RLM, LRM, ALM, embedding/override marks, etc.) are stripped from both the input string and the pattern. If the
preserveBidiControlsoption is enabled, they are preserved instead. - By default, leading/trailing whitespace is ignored and consecutive whitespace is collapsed to a single space in both the input string and the pattern. If the
preserveWhitespaceoption is enabled, whitespace is preserved exactly as-is instead. - Quoted text in single or double quotes is matched literally after the selected normalization rules have been applied.
- Day-of-week names, quarter names, and scoped Gregorian components are validated by default against the parsed date. If the
skipValidationoption is enabled, they are not validated, but they must still match the expected pattern shape.
Parse options
interface ParseOptions {
preserveBidiControls?: boolean;
preserveWhitespace?: boolean;
skipValidation?: boolean;
}| Option | Default | Description |
| ---------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| preserveBidiControls | false | Preserve bidirectional control characters instead of stripping them. |
| preserveWhitespace | false | Preserve whitespace exactly instead of trimming and collapsing it. |
| skipValidation | false | Skip validation of day-of-week names, quarter names, and scoped Gregorian tokens. The values still must match the pattern shape. |
Examples
const date = JalaliDate.parse('جمعه ۱ فروردین ۱۴۰۴', 'DDDD D MMMM YYYY');
console.log(date.toString()); // "1404/01/01"
const mixed = JalaliDate.parse(
'۱۴۰۴/۰۱/۰۱ برابر با ۲۰۲۵/۰۳/۲۱ میلادی',
'YYYY/MM/DD برابر با [gregorian:YYYY/MM/DD] میلادی'
);
console.log(mixed.toString()); // "1404/01/01"
const withLiteralBrackets = JalaliDate.parse('تاریخ: [۱۴۰۴/۰۱/۰۱]', '"تاریخ:" "["YYYY/MM/DD"]"');
console.log(withLiteralBrackets.toString()); // "1404/01/01"
// Default parsing is forgiving about repeated whitespace.
const forgiving = JalaliDate.parse('۱۴۰۴ / ۱ / ۱', 'YYYY / M / D');
console.log(forgiving.toString()); // "1404/01/01"
// Strict whitespace matching.
const strict = JalaliDate.parse('۱۴۰۴\t۱\t۱', 'YYYY\tM\ttD', { preserveWhitespace: true });
console.log(strict.toString()); // "1404/01/01"
// Match day-of-week / quarter / Gregorian fields without validating their values.
const skipValidation =JalaliDate.parse(
'۱۴۰۴/۰۱/۰۱ برابر با ۲۰۲۵/۰۳/۲۲ میلادی',
'YYYY/MM/DD برابر با [gregorian:YYYY/MM/DD] میلادی',
{ skipValidation: true }
);
console.log(skipValidation.toString()); // "1404/01/01"Throws Error if the pattern is malformed, the input string does not match the pattern, required Jalali date components cannot be extracted, or a validated day-of-week, quarter, or Gregorian component does not match the parsed date.
Throws RangeError if the resulting date is invalid or out of supported range.
Static utilities
| Method | Description |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
| JalaliDate.isValidDate(year, month, day) | Returns true if the given date is valid, false otherwise (without throwing). |
| JalaliDate.isLeapYear(year) | Returns whether the given Jalali year is a leap year. |
| JalaliDate.daysInMonth(year, month) | Returns the number of days in a Jalali month. |
| JalaliDate.daysInYear(year) | Returns the number of days in a Jalali year. |
| JalaliDate.vernalEquinox(year) | Returns the UTC time of the vernal equinox used to determine Nowruz for the given Jalali year. |
| JalaliDate.age(birthDate, referenceDate?) | Calculates age in complete years from birth date to reference date (defaults to today). |
JalaliDate.isValidDate(year: number, month: number, day: number): boolean
JalaliDate.isLeapYear(year: number): boolean
JalaliDate.daysInMonth(year: number, month: number): number
JalaliDate.daysInYear(year: number): number
JalaliDate.vernalEquinox(year: number): Date | null
JalaliDate.age(birthDate: JalaliDate, referenceDate?: JalaliDate): numberif (JalaliDate.isValidDate(1403, 12, 30)) {
const date = new JalaliDate(1403, 12, 30);
console.log(date.format('Q YYYY')); // "زمستان ۱۴۰۳"
}
const equinox = JalaliDate.vernalEquinox(1404);
if (equinox !== null) {
console.log(equinox.toISOString()); // e.g., "2025-03-20T09:45:00.000Z"
} else {
console.log('JS Date does not support the year of the equinox calculation.');
}
const birthDate = new JalaliDate(1380, 5, 15);
const currentAge = JalaliDate.age(birthDate); // Age as of today
const ageAtDate = JalaliDate.age(birthDate, new JalaliDate(1403, 5, 15)); // Age at specific dateRange constants
| Constant | Description |
| ------------------------------- | --------------------------------- |
| JalaliDate.MIN_YEAR | Minimum supported Jalali year. |
| JalaliDate.MAX_YEAR | Maximum supported Jalali year. |
| JalaliDate.MIN_GREGORIAN_YEAR | Minimum supported Gregorian year. |
| JalaliDate.MAX_GREGORIAN_YEAR | Maximum supported Gregorian year. |
Instance properties
| Property | Description |
| -------------------- | ------------------------------------------------------------------------------------ |
| date.year | Jalali year. |
| date.month | Jalali month, from 1 to 12. |
| date.day | Jalali day of month. |
| date.quarter | Quarter (season) of the date, from 1 to 4. |
| date.jdn | Julian Day Number for the date. |
| date.weekOfYear | 1-based week number. Week 1 contains 1 Farvardin. |
| date.weekOfMonth | 1-based week number of the month. Week 1 contains day 1. |
| date.dayOfYear | 1-based day of year. |
| date.dayOfWeek | Day of week using the JavaScript Date convention: 0 = Sunday, …, 6 = Saturday. |
| date.monthName | Persian name of the month (e.g., 'فروردین', 'اردیبهشت', etc.). |
| date.dayOfWeekName | Persian name of the day (e.g., 'شنبه', 'یکشنبه', etc.). |
| date.quarterName | Persian name of the quarter (e.g., 'بهار', 'تابستان', etc.). |
| date.isLeapYear | Whether the date's year is a leap year. |
| date.daysInMonth | Number of days in the date's month. |
| date.daysInYear | Number of days in the date's year. |
Conversion
date.toGregorian()
Returns the equivalent proleptic Gregorian date.
const gregorian = date.toGregorian();
// { year: 2025, month: 3, day: 21 }date.toArray()
Returns the Jalali date as an array of [year, month, day].
const arr = date.toArray();
// [1404, 1, 1]date.toObject()
Returns the Jalali date as a plain object.
const obj = date.toObject();
// { year: 1404, month: 1, day: 1 }date.toIsoDateString()
Returns the equivalent Gregorian date as an ISO 8601 date string in "YYYY-MM-DD" format.
const iso = date.toIsoDateString();
// "2025-03-21"Arithmetic
Methods to offset the date by a number of days, months, or years. All return new JalaliDate instances.
| Method | Description |
| ------------------- | ---------------------------------------------------------------------------- |
| date.addDays(n) | Returns a date shifted by n days, where n can be positive or negative. |
| date.addMonths(n) | Returns a date shifted by n months, where n can be positive or negative. |
| date.addYears(n) | Returns a date shifted by n years, where n can be positive or negative. |
When adding months or years, the day is clamped if the target month or year has fewer days.
const tomorrow = today.addDays(1);
const nextMonth = today.addMonths(1);
const lastYear = today.addYears(-1);Derived dates
Methods to obtain related dates from the current date. All return new JalaliDate instances.
| Method | Description |
| ------------------------ | ------------------------------------------------------------------ |
| date.startOfYear() | Returns the first day of this date's year (1 Farvardin). |
| date.endOfYear() | Returns the last day of this date's year (29 or 30 Esfand). |
| date.startOfQuarter() | Returns the first day of this date's quarter. |
| date.endOfQuarter() | Returns the last day of this date's quarter. |
| date.startOfMonth() | Returns the first day of this date's month. |
| date.endOfMonth() | Returns the last day of this date's month. |
| date.startOfWeek() | Returns the first day of the week containing this date (Saturday). |
| date.endOfWeek() | Returns the last day of the week containing this date (Friday). |
Weeks start on Saturday (the traditional first day of the week in Iran) and end on Friday.
const date = new JalaliDate(1403, 6, 15);
// Year boundaries
const yearStart = date.startOfYear(); // 1403/01/01
const yearEnd = date.endOfYear(); // 1403/12/30 (leap year)
// Quarter boundaries
const quarterStart = date.startOfQuarter(); // 1403/04/01 (Q2)
const quarterEnd = date.endOfQuarter(); // 1403/06/31 (Q2)
// Month boundaries
const monthStart = date.startOfMonth(); // 1403/06/01
const monthEnd = date.endOfMonth(); // 1403/06/31
// Week boundaries (Saturday to Friday)
const weekStart = date.startOfWeek(); // Saturday of this week
const weekEnd = date.endOfWeek(); // Friday of this weekImmutability helpers
Methods to create a copy of the date with specific fields changed. All return new JalaliDate instances.
| Method | Description |
| ----------------------- | --------------------------------------------------------- |
| date.withYear(year) | Returns a new date with the specified year. |
| date.withMonth(month) | Returns a new date with the specified month. |
| date.withDay(day) | Returns a new date with the specified day. |
When changing the year or month, the day is clamped if the target month has fewer days.
const date = new JalaliDate(1403, 5, 15);
const sameDate1404 = date.withYear(1404); // 1404/05/15
const differentMonth = date.withMonth(8); // 1403/08/15
const differentDay = date.withDay(20); // 1403/05/20Comparison
| Method | Description |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| date.compareTo(other) | Returns a negative number, zero, or a positive number depending on whether this date is before, equal to, or after other. |
| date.equals(other) | Returns true if both dates represent the same calendar day. |
| date.isBefore(other) | Returns true if this date is strictly earlier than other. |
| date.isAfter(other) | Returns true if this date is strictly later than other. |
| date.isBetween(start, end) | Returns true if this date is between start and end (inclusive). |
| date.isSameYear(other) | Returns true if both dates are in the same Jalali year. |
| date.isSameMonth(other) | Returns true if both dates are in the same year and month. |
| date.isSameWeek(other) | Returns true if both dates are in the same year and week (weeks start on Saturday). |
| date.isSameQuarter(other) | Returns true if both dates are in the same year and quarter. |
const date = new JalaliDate(1403, 5, 15);
const start = new JalaliDate(1403, 5, 1);
const end = new JalaliDate(1403, 5, 31);
date.isBetween(start, end); // true
// Check if dates are in the same period
const date1 = new JalaliDate(1403, 5, 15);
const date2 = new JalaliDate(1403, 6, 20);
date1.isSameYear(date2); // true (both in 1403)
date1.isSameMonth(date2); // false (different months)
date1.isSameQuarter(date2); // true (both in Q2: months 4-6)
// Week comparison (weeks start on Saturday)
const wed = new JalaliDate(1403, 1, 1); // 1 Farvardin 1403 (Wednesday)
const sat = wed.startOfWeek(); // Saturday of the same week
wed.isSameWeek(sat); // trueDate differences
Methods to calculate differences between dates.
| Method | Description |
| -------------------------------- | ------------------------------------------------------------------- |
| date.differenceInDays(other) | Returns the number of days from this date to other. |
| date.differenceInMonths(other) | Returns the approximate number of months from this date to other. |
| date.differenceInYears(other) | Returns the approximate number of years from this date to other. |
The results are whole numbers, and is positive if other is in the future relative to this date, negative if other is in the past.
const date1 = new JalaliDate(1400, 5, 15);
const date2 = new JalaliDate(1403, 8, 20);
date1.differenceInDays(date2); // Positive number (future)
date1.differenceInMonths(date2); // ~39 months
date1.differenceInYears(date2); // 3 years
date2.differenceInDays(date1); // Negative number (past)Formatting and serialization
date.format(pattern, options?)
Formats a JalaliDate using a format pattern.
Format tokens outside a scoped calendar block use the Jalali calendar. The same is true inside an explicit [jalali:...] block. Gregorian components can be included with a [gregorian:...] block and are formatted from the corresponding Gregorian date.
Pattern tokens
| Token | Jalali meaning | Gregorian block meaning |
| ------ | ------------------------------------ | ------------------------------------------------------ |
| YY | 2-digit Jalali year | 2-digit Gregorian year |
| YYYY | Full Jalali year | Full Gregorian year |
| M | Jalali month number | Gregorian month number |
| MM | Zero-padded Jalali month number | Zero-padded Gregorian month number |
| MMMM | Persian Jalali month name | Persian Gregorian month name |
| D | Jalali day of month | Gregorian day of month |
| DD | Zero-padded Jalali day of month | Zero-padded Gregorian day of month |
| DDDD | Persian weekday name | Persian weekday name |
| Q | Persian Jalali quarter (season) name | Literal Q; Gregorian quarter names are not supported |
Pattern syntax
Scoped calendar blocks are defined with square brackets and a calendar name prefix followed by a colon. Nested blocks are not supported.
| Syntax | Meaning |
| ------------------------ | --------------------------------------- |
| YYYY/MM/DD | Implied Jalali calendar block (default) |
| [jalali:YYYY/MM/DD] | Explicit Jalali calendar block |
| [gregorian:YYYY/MM/DD] | Gregorian calendar block |
| 'text' or "text" | Quoted literal text |
The formatter follows these rules:
- Number fields are formatted using Persian-Indic digits.
- Tokens outside scoped calendar blocks are formatted using the Jalali date.
- Tokens inside
[jalali:...]blocks are formatted using the Jalali date. - Tokens inside
[gregorian:...]blocks are formatted using the corresponding Gregorian date. - Quoted text in single or double quotes is output literally, without the quote characters.
Format options
interface FormatOptions {
rlm?: 'never' | 'always' | 'auto';
}| Option | Default | Description |
| ------ | --------- | ------------------------------------------------------------------------------------ |
| rlm | 'never' | Controls whether a Right-to-Left Mark (U+200F) is prepended to the formatted text. |
rlm accepts:
| Value | Meaning |
| ---------- | --------------------------------------------------------------------- |
| 'never' | Never prepend RLM. |
| 'always' | Always prepend RLM. |
| 'auto' | Prepend RLM only when the formatted text starts with a Persian digit. |
date.format('YYYY/MM/DD', 'auto');Examples:
const date = new JalaliDate(1404, 1, 1);
console.log(date.format('YYYY/MM/DD'));
// "۱۴۰۴/۰۱/۰۱"
console.log(date.format('[jalali:DDDD، D MMMM YYYY]'));
// "جمعه، ۱ فروردین ۱۴۰۴"
console.log(date.format('YYYY/MM/DD برابر با [gregorian:YYYY/MM/DD] میلادی'));
// "۱۴۰۴/۰۱/۰۱ برابر با ۲۰۲۵/۰۳/۲۱ میلادی"
console.log(date.format('D MMMM YYYY برابر با [gregorian:D MMMM YYYY]'));
// "۱ فروردین ۱۴۰۴ برابر با ۲۱ مارس ۲۰۲۵"
console.log(date.format('تاریخ: "["YYYY/MM/DD"]"'));
// "تاریخ: [۱۴۰۴/۰۱/۰۱]"
console.log(date.format('"تاریخ:" YYYY/MM/DD'));
// "تاریخ: ۱۴۰۴/۰۱/۰۱"
console.log(date.format('YYYY/MM/DD', { rlm: 'auto' }));
// "۱۴۰۴/۰۱/۰۱"Throws Error if the pattern is malformed: contains an unsupported calendar scope, has an unclosed scoped block, has a nested scoped block, has an unmatched closing bracket, or has an unclosed quoted literal.
date.toString()
Returns the date as "YYYY/MM/DD" with Latin digits.
date.toString(); // "1404/01/01"date.toJSON()
Serializes the date as "YYYY/MM/DD", matching date.toString().
JSON.stringify(date); // "\"1404/01/01\""Testing helpers
JalaliDate.setTestToday(testToday)
Sets a fixed Jalali date for methods that depend on today's date. Pass null to restore the real current date.
JalaliDate.setTestToday(new JalaliDate(1405, 3, 3));
const today = JalaliDate.today(); // 1405/03/03
JalaliDate.setTestToday(null);JalaliDate.getTestToday()
Returns the currently set test date for "today" or null if the real current date is used.
const testToday = JalaliDate.getTestToday();
if (testToday !== null) {
console.log(`Test "today" is set to: ${testToday.toString()}`);
} else {
console.log('Using real current date for "today".');
}License
MIT © Kambiz Khojasteh
