Calendars تقویمها
Convert Jalali to Gregorian in code (JS & Python)
On this pageدر این صفحه
If you’re building anything that touches Iranian dates, you’ll eventually need to convert Jalali to Gregorian in code. The good news: you should not write the algorithm yourself. The arithmetic is subtle, and well-tested libraries already handle it.
To sanity-check any single date against a reference, use the live converter (it runs the jalaali-js cycle):
JavaScript
jalaali-js is the de-facto standard and is what this site’s converter uses under the hood (source: jalaali-js on GitHub). The core call converts a Jalali year/month/day to Gregorian:
import { toGregorian } from 'jalaali-js'
// 1 Farvardin 1404 -> Gregorian
const g = toGregorian(1404, 1, 1)
// { gy: 2025, gm: 3, gd: 21 }
And back the other way with toJalaali(gy, gm, gd). The library works on plain year/month/day numbers, which is deliberate: it keeps you away from Date objects and their timezone behaviour for the conversion itself.
Python
In Python, jdatetime mirrors the standard datetime API, so it’s familiar:
import jdatetime
# Jalali date -> Gregorian
g = jdatetime.date(1404, 1, 1).togregorian()
# datetime.date(2025, 3, 21)
It also parses and formats Jalali dates, which is handy for I/O.
The edge cases you must test
Why you need a library (and shouldn’t hand-roll it)
These libraries exist instead of a one-line formula because the Persian leap rule is a 33-year cycle, not a modulo-4 check, so a naive conversion drifts. For example, assuming “every 4th year is leap” places 30 Esfand in the wrong year within a single cycle. Whichever language you use, the bugs cluster in the same places. Write tests for these:
- The Nowruz boundary. 30 Esfand of a leap year and the following 1 Farvardin (e.g. 30 Esfand 1403 → 20 March 2025, then 1 Farvardin 1404 → 21 March 2025). Off-by-one here is the classic failure.
- 30 Esfand validity. 30 Esfand exists only in leap years. Decide what your code does with an invalid one, reject, don’t silently roll forward. See 30 Esfand explained.
- The leap cycle. Persian leap years follow a 33-year cycle, not “every four years,” so don’t assume modulo 4. See Persian (Jalali) leap years, explained.
The timezone trap
The most common production bug isn’t the calendar math at all, it’s mixing in timestamps. If you convert a Date/datetime that carries a time and zone, a value near midnight can land on the wrong calendar day. Convert on date-only values (year, month, day) and keep time zones out of the conversion path. This site’s converter is built date-only for exactly that reason; you can read more in days between dates, which hits the same issue.
Frequently asked questions
How do I convert Jalali to Gregorian in JavaScript?
Use the jalaali-js library: toGregorian(jy, jm, jd) returns the Gregorian year, month, and day. Convert back with toJalaali(gy, gm, gd).
What library converts Jalali dates in Python?
jdatetime mirrors Python’s datetime API: jdatetime.date(y, m, d).togregorian() returns a standard date. It also parses and formats Jalali dates.
Should I write the conversion algorithm myself?
No. The 33-year leap cycle and month lengths are easy to get subtly wrong. Use a maintained library and test the boundaries.
What is the most common bug?
Timezone leakage: converting a timestamp instead of a date-only value, which can shift the result by a day near midnight. Convert on year/month/day only.
In short: pick jalaali-js or jdatetime, convert on date-only values, and test the Nowruz and 30 Esfand boundaries. To verify a result by hand, the converter is your reference.