How to Convert Persian (Jalali) Dates to other 18 Calendar Dates in Javascript without External Libraries or Complex Astronomical Equations

The short Javascript function below does not use external libraries and provides the facilities to convert a Persian (Jalali) Dates (from Persian year -272,442 AP to +275,139 AP) into any of the following 18 Javascript Calendars with options for formatting the resulting output:

"buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic", "gregory", "hebrew", "indian", "islamic", "islamic-umalqura", "islamic-tbla", "islamic-civil", "islamic-rgsa", "iso8601", "japanese", "persian", "roc", "islamicc".

The method, also, does not use complex mathematical or astronomical formulas and relies solely on the Javascript built-in calendar conversion algorithms which are in turn based on the ICU code [https://icu.unicode.org/].

This approach ensures that the output is always accurate and fully compliant with the Javascript engine output.

Syntax

persianToCalendars(year, month, day, [options])

In its simplest form, the function defaults to converting the Persian Date into the Gregorian calendar using the ISO Date Format.

Example: Convert the Persian Date Esfand 19, 1400 (i.e. 12/19/1400) to Gregorian.

persianToCalendars(1400,12,19);

output: 2022-03-10T00:00:00.000Z    // default output Gregorian ISO format

To to convert Persian Date to another calendar (say ‘Islamic’ calendar):

 persianToCalendars(1400,12,19, { toCal: "islamic-umalqura" });

 output: 8/7/1443 AH

To add formatting to the output, use the 'dateStyle' options as in the Javascript Intl.DateTimeFormat() method.

Example: Convert Persian Date to Islamic Date with full dateStyle

 persianToCalendars(1400,12,19, { toCal: "islamic-umalqura", dateStyle: "full" });

 output: Thursday, Shaʻban 7, 1443 AH

Example: Convert a Persian Date into Hebrew with Persian Locale

 persianToCalendars(1400,12,19, { toCal:"hebrew", dateStyle: "full", locale:"fa"})

 output: پنجشنبه ۷ واذار الثانی ۵۷۸۲ تقویم عبری

The above can be done for all other 18 Calendars.

An added feature is the ability to format the Persian Date into any of the available 'dateStyles' and 'locales' without conversion.

To do that specify the 'toCal' to persian

Example: Use Persian Locale to Format a Persian Date

 persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full", locale:"fa"}));

 output:  ۱۴۰۰ اسفند ۱۹, پنجشنبه         // mind the RTL requirements

Example: Format a Persian Date in the Hindi Locale

 persianToCalendars(1400,12,19,{ toCal : "persian", dateStyle : "full", locale : "hi"}));

 output: AP गुरुवार, 19 ईस्फन्द् 1400

You can use all the options available in the Intl.DateTimeFormat() for formatting the output date.

Invalid Persian Dates

If an invalid Islamic Date is passed to the function an error Invalid Persian Date! will be generated.

Invalid Persian Dates are dates that have incorrect days in the month or incorrect days or months.

For example, the Persian Date 1400/12/30 is invalid because month 12 of the Persian Calendar (month “Esfand”) is 29 days in the year 1400.

The future Javascript Temporal API will make this task simpler.

/*********************************************************************
* @function  : persianToCalendars(year, month, day, [options])
*
* @purpose   : Converts Persian/Iranian Date (Jalali Date) to the corresponding Gregorian Date.
*              Handles Persian dates from -272,442 AP to +275,139 AP.
*              Uses the 'JS Calendar Conversion by Target Approximation' Method.
*              No external libraries or complex mathematical/astronautical formulas.
*
* @version   : 1.00
* @author    : Mohsen Alyafei
* @date      : 17 Feb 2022
* @licence   : MIT
* @param     : year  : (numeric) Persian year  (-272442 to 275139)
* @param     : month : (numeric) Persian month (1 to 12) note: months is standard 1 based
* @param     : day   : (numeric) Persian day   (1 to 31)
* @param     : options: Object with the following optional parameters:
*
*              'toCal' : Specifies the the type of output Calendar to convert to with 18 Calendars:
*                        - "gregory" : (default)
*                        - "buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic",
*                          "hebrew", "indian", "islamic", "islamic-umalqura", "islamic-tbla",
*                          "islamic-civil", "islamic-rgsa", "iso8601", "japanese", "persian", "roc".
*
*               'dateStyle' Same as used in the Intl.DateTimeFormat() constructor.
*                           If not stated, default output is in Gregorian ISO Format: YYYY:MM:DDTHH:mm:ss.sssZ
*
*               'locale' The BCP 47 language tag for formatting (default is 'en'). If the 'locale'
*                        is given then no date conversion happens and the Persian date is formatted
*                        based on the specified 'dateStyle' and 'locale'.
*
*               Other options: As used in the Intl.DateTimeFormat() constructor.
*
* @returns   : Return the date in the calendar and format of the specified 'options'
**********************************************************************/




//==========================================================
function persianToCalendars(year, month, day, op={}) {
const formatOut= gD=> "toCal"in op?(op.calendar=op.toCal,new Intl.DateTimeFormat(op.locale??"en",op).format(gD)):gD,
      dFormat  = new Intl.DateTimeFormat('en-u-ca-persian',{dateStyle:'short',timeZone:"UTC"});
let   gD       = new Date(Date.UTC(2000,month,day));
      gD       = new Date(gD.setUTCDate(gD.getUTCDate() + 226867));
const gY       = gD.getUTCFullYear()-2000+year;
      gD       = new Date(((gY<0)?"-":"+")+("00000"+Math.abs(gY)).slice(-6)+"-"+("0"+(gD.getUTCMonth()+1)).slice(-2)+"-"+("0"+(gD.getUTCDate())).slice(-2));
let [pM,pD,pY] = [...dFormat.format(gD).split("https://stackoverflow.com/")], i=0;
      gD       = new Date(gD.setUTCDate(gD.getUTCDate() +
                 ~~(year*365.25+month*30.44+day-(pY.split(" ")[0]*365.25+pM*30.44+pD*1))-2));
while (i < 4) {
    [pM,pD,pY]=[...dFormat.format(gD).split("https://stackoverflow.com/")];
    if (pD==day && pM==month && pY.split(" ")[0]==year) return formatOut(gD);
    gD = new Date(gD.setUTCDate(gD.getUTCDate()+1));i++;
}
throw new Error('Invalid Persian Date!');
}
//==========================================================










//==========================================================
// Test Units
//==========================================================
console.log("-".repeat(55));
console.log("Convert the Persian Date '1400-12-19' to other calendars:");
console.log("input to function: persianToCalendars(1400,12,19, options)");
console.log("-".repeat(55));

console.log("Default (Gregory) ISO format   : ",persianToCalendars(1400,12,19)); // convert to default gregorian date
console.log("Gregory 'full' format          : ",persianToCalendars(1400,12,19,{toCal:"gregory",dateStyle:"full"}));
console.log("Islamic 'full' format          : ",persianToCalendars(1400,12,19,{toCal:"islamic",dateStyle:"full"}));
console.log("Islamic-Umaalqura 'short'format: ",persianToCalendars(1400,12,19,{toCal:"islamic-umalqura"}));
console.log("Islamic-Umaalqura 'full' format: ",persianToCalendars(1400,12,19,{toCal:"islamic-umalqura",dateStyle:"full"}));
console.log("Islamic-civil 'full' format    : ",persianToCalendars(1400,12,19,{toCal:"islamic-civil",dateStyle:"full"}));
console.log("Islamic-tbla 'full' format     : ",persianToCalendars(1400,12,19,{toCal:"islamic-tbla",dateStyle:"full"}));
console.log("Islamic-rgsa 'full' format     : ",persianToCalendars(1400,12,19,{toCal:"islamic-rgsa",dateStyle:"full"}));
console.log("Hebrew 'full' format           : ",persianToCalendars(1400,12,19,{toCal:"hebrew",dateStyle:"full"}));
console.log("Indian 'full' format           : ",persianToCalendars(1400,12,19,{toCal:"indian",dateStyle:"full"}));
console.log("Buddhist 'full' format         : ",persianToCalendars(1400,12,19,{toCal:"buddhist",dateStyle:"full"}));
console.log("Chinese 'full' format          : ",persianToCalendars(1400,12,19,{toCal:"chinese",dateStyle:"full"}));
console.log("Dangi (Korean) 'full' format   : ",persianToCalendars(1400,12,19,{toCal:"dangi",dateStyle:"full"}));
console.log("R.O.C. (Minguo) 'full' format  : ",persianToCalendars(1400,12,19,{toCal:"roc",dateStyle:"full"}));
console.log("Japanese 'full' format         : ",persianToCalendars(1400,12,19,{toCal:"japanese",dateStyle:"full"}));
console.log("Coptic 'full' format           : ",persianToCalendars(1400,12,19,{toCal:"coptic",dateStyle:"full"}));
console.log("Ethioaa 'full' format          : ",persianToCalendars(1400,12,19,{toCal:"ethioaa",dateStyle:"full"}));
console.log("Ethiopic 'full' format         : ",persianToCalendars(1400,12,19,{toCal:"ethiopic",dateStyle:"full"}));
console.log("-".repeat(55));
console.log("Format the input Persian Date without conversion:");
console.log("-".repeat(55));
console.log("Persian 'full' format        : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full"}));
console.log("Persian 'medium' format      : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"medium"}));
console.log("Persian 'short' format       : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"short"}));
console.log("Persian 'ar' locale          : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full", locale:"ar"}));
console.log("Persian 'fa' locale          : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full", locale:"fa"}));
console.log("Persian 'hi' locale          : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full", locale:"hi"}));
console.log("Persian 'ur' locale          : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full", locale:"ur"}));
console.log("Persian 'ps-AF' locale       : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full", locale:"ps-AF"}));
console.log("Persian 'id' locale          : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full", locale:"id"}));
console.log("Persian 'pa' locale          : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full", locale:"pa"}));
console.log("Persian 'ma' locale          : ",persianToCalendars(1400,12,19,{toCal:"persian",dateStyle:"full", locale:"ma"}));

console.log("-".repeat(55));
console.log("Convert Max Negative and Max Positive Persian Dates to Gregorian");
console.log("-".repeat(55));
console.log(persianToCalendars(-272442,12,29)); // max negative Persian date
console.log(persianToCalendars(275139,6,23));   // max positive Persian date

console.log("-".repeat(55));

Leave a Comment