Lunar Hijri dates and Shahanshahi years
Convert Jalali and Gregorian dates to the lunar Hijri calendar as Iran's official calendar counts it, and write or parse years in the Shahanshahi numbering, in Dart.
Hijri calendar
final ashura = PersianDate(1405, 4, 4).toHijri();
print(ashura); // 10 محرم 1448
print(ashura.format('EEEE d MMMM yyyy')); // پنجشنبه 10 محرم 1448
print(HijriDate(1447, 10, 1).toPersianDate()); // 1405/01/01
print(DateTime(2026, 6, 25).toHijriDate().format('yyyy/MM/dd')); // 1448/01/10
print(HijriDate(1447, 12, 1).monthLength); // 29HijriDate is the lunar Hijri (قمری) date as Iran's official calendar counts it.
A month starts the day after the evening its new crescent can be seen in Iran.
The start is calculated from the positions of the Sun and the Moon with Yallop's visibility criterion at eight Iranian cities, and a built-in table corrects the months where the published calendar differs.
Checked against the calendar published on time.ir for every month with a holiday from Jalali 1387 to 1406, the result matches all of them.
What it cannot do:
A month officially starts when the crescent is actually seen, which is announced only days before. The calculation can be one day off from that announcement. Pass the announced start to an
IranianHijriof your own:dartfinal announced = IranianHijri( // 1 Muharram 1448 announced for 1405/03/27. monthStarts: {(1448, 1): PersianDate(1405, 3, 27)}, ); print(announced.fromPersianDate(PersianDate(1405, 4, 5))); // 10 محرم 1448Only Iran's calendar is implemented; Umm al-Qura and the tabular Islamic calendar are not.
Far from today the dates are a calculation only, with nothing to check them against.
| Member | Description |
|---|---|
HijriDate(int year, int month, int day) | From Hijri values; throws ArgumentError for a day that does not exist |
HijriDate.fromPersianDate(PersianDate), PersianDate.toHijri() | From a Jalali date |
HijriDate.fromDateTime(DateTime), DateTime.toHijriDate() | From the calendar day of a DateTime |
year, month, day | Hijri year, month (1 is Muharram), and day |
monthName, monthNames | محرم to ذوالحجه |
monthLength | 29 or 30 |
weekday, weekdayName, weekdayNameShort | ISO weekday and Persian names, like PersianDate |
toPersianDate(), toDateTime() | Jalali date, and local midnight of the day |
==, compareTo, <, <=, >, >=, isBefore, isAfter | By year, month, and day |
format(String pattern, {persianDigits}) | Tokens yyyy, yy, MMMM, MM, M, dd, d, EEEE, E, quoting like PersianDate.format |
toString() | day monthName year, for example 10 محرم 1448 |
HijriDate.min, HijriDate.max | The Hijri dates of PersianDate.min and PersianDate.max |
IranianHijri.official | The calendar every HijriDate conversion uses |
IranianHijri({monthStarts}) | A calendar with announced month starts; fromPersianDate, fromDateTime, date, monthStart, monthLength |
A HijriDate created by a calendar with announced starts keeps that calendar's day in toPersianDate, toDateTime, and weekday.
Equality and ordering compare year, month, and day only.
Shahanshahi years
final date = PersianDate(1405, 1, 4);
print(date.shahanshahiYear); // 2585
print(date.format('d MMMM yyyy', era: JalaliEra.shahanshahi)); // 4 فروردین 2585
print(date.toString(era: JalaliEra.shahanshahi)); // 2585/01/04
print(PersianDate.parse('2585/01/04', era: JalaliEra.shahanshahi)); // 1405/01/04The Shahanshahi (شاهنشاهی, Imperial) numbering is the Solar Hijri year plus 1180.
Pass era: JalaliEra.shahanshahi to format, toString, parse, or tryParse to write or read years that way.
Only the year tokens yyyy and yy change; months, days, and names are the same in both eras.
year always holds the Solar Hijri year, and toJson always writes it.
No era name is printed; add one as quoted text, for example format("yyyy 'شاهنشاهی'", era: JalaliEra.shahanshahi).
parse with the Shahanshahi era accepts years 1119 to 4357 and throws FormatException outside them.