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));