Persian Date
A single set of date utilities that works across the Jalali (Solar Hijri) and Gregorian calendars: format and parse dates, convert between calendars, do calendar-aware arithmetic, and validate reservation-style ranges — all built on date-fns and date-fns-jalali.
Last updated August 12, 2026
Overview
Installation
$ npx shadcn@latest add https://ui.persian-labs.ir/r/persian-date.jsonUsage
Every function that reads or manipulates calendar fields (months, years, week boundaries) accepts an optional calendarType — the Jalali calendar is the default, since this library is Iran-first.
import { formatDate, addDays, today } from "@/lib/persian-date"
const checkIn = today()
const checkOut = addDays(checkIn, 3)
formatDate(checkIn, "yyyy/MM/dd") // "1404/06/01" (Jalali by default)
formatDate(checkOut, "d MMMM", { calendarType: "miladi" }) // GregorianExamples
Parsing
parseDate normalizes Persian/Arabic-Indic digits before parsing, and returns null instead of an invalid Date on failure.
Shamsi ↔ Miladi conversion
toParts/fromParts (and their toShamsi/toMiladi/fromShamsi/fromMiladi shorthands) round-trip a Date through calendar fields without losing precision.
Calendar-aware arithmetic
addMonths and addYears respect the active calendar's month lengths — adding a month to the end of Esfand behaves differently than adding one to the Gregorian February.
Start / end of week, month, year
startOfWeek/endOfWeek default to a Saturday-start week for shamsi (Sunday for miladi); startOfMonth/endOfMonth and startOfYear/endOfYear follow the same calendar-aware pattern.
Leap year & days in month
isLeapYear and daysInMonth resolve Esfand's 29 vs. 30 days for the Jalali calendar.
Comparisons
isToday, isPast, and isFuture classify a date relative to the current moment.
Date range
eachDayOfRange lists every day in an inclusive range; isWithinRange checks whether a given date falls inside it.
Range validation (reservation form)
validateRange is built for hospital/hotel-style booking forms: it checks ordering, minimum/maximum stay length, past dates, and specific blocked dates, returning a machine-readable reason you can map to a message.
RTL
formatDate already outputs Persian digits and Persian month/weekday names by default in the shamsi calendar, so the formatted string reads naturally inside right-to-left content with no extra direction handling.