Commonly, the Arabic date is written in the following formats:
الحادي والثلاثون من شهر يناير سنة ١٩٢٠ ميلادية
غرة آذار سنة ١٩٤٥ ميلادية
السابع عشر من شهر ربيع الأول سنة 1364 هجرية
السابع من شهر جوان سنة 2021 ميلادية
الحادي عشر من شهر تموز سنة ٢٠٢٠ ميلادية
Javascript is limited to providing the following formats:
Intl.DateTimeFormat("ar-LB",{day: 'numeric', month: 'long',year:'numeric', era: "long"})
"٢٨ كانون الثاني ٢٠٢٢ ميلادي"
Intl.DateTimeFormat("ar-u-ca-islamic",{day: 'numeric', month: 'long',year:'numeric', era: "long"})
"26 جمادى الآخرة 1443 هـ"
The Javascript Intl.DateTimeFormat()
constructor provides various options to specify the output format. One of these options is the era
.
The era
has the following possible values:
"long" (e.g., Anno Domini)
"short" (e.g., AD)
"narrow" (e.g., A)
The above is in accordance with the ISO 639 Language specifications.
However, in the Arabic language "ar", the "era" option output (in "long" format) provides the word "ميلادي" which means "gregorian year"; again this is in accordance with ISO 639. However, the Arabic word "ميلادي" is a "Masculine" word and would not work because the Arabic word "سنة" (i.e. "year") is Feminine and it should be "ميلادية".
In Arabic a gregorian date will be written as:
الثامن من شهر نوفمبر سنة ١٩٨٨ ميلادية
Notice the use of the word "ميلادية" not "ميلادي".
Another problem is with the "Islamic" calendar. The "era" option output (both in "short" and "long" format) give the same output "هــ". The correct output for "era" in the "long" format is "هجرية".
In Arabic a Hijri date will be written as:
التاسع عشر من شهر محرم سنة 1406 هجرية
Notice the use of the word "هجرية" not "هــ".
My Solution:
My solution to fix this issue and also to provide an Arabic Date with a 'Long Ordinal Date Format' is the following function.
The function will output a long ordinal date format and takes any date() object as an input with 3 additional options:
locale : A valid Javascript locale format string or array.
gora : flag "y" to use the word "غرة" instead of "الأول".
calendarName: "long", "short" ,"narrow" used for era. The "long" format corrects the ISO error in representation of the era for both the Gregorian and Hijri years.
I have provided various test cases below.
Are there any alternative methods to do this fix?
Update 29 Jan 2022
The following line has been added to ensure that the correction of the Islamic year name continues to work even if the Unicode Common Locale Data Repository (CLDR) would correct the "long" name of the Islamic era string to "هجري" instead of the current letters "هـ".
else if (result.split(" ").slice(-1)=="هجري") result=result.split(" ").slice(0,-1).join(" ") + " هجرية";
/*********************************************************************
* @function : dateToArabicString(date [, options])
* @purpose : Converts Dates to Arabic Ordinal and corrects the
* name of calendar year type "ميلادية" "هجرية"
* and permits the use of the word "غرة" instead of "الأول"
* @version : 1.00
* @author : Mohsen Alyafei
* @date : 28 Jan 2022
* @Licence : MIT
* @param : {date} a date object
* [options] are:
* - locale : Javascript standard locale format string. Default: "ar".
* - gora : flag "y" to use the word "غرة" instead of "الأول". Default: "no"
* - calendarName: "long", "short" ,"narrow" used for era. Default: "long".
* "long" corrects the ISO error in representation of the era
* @returns : {string} The Date in Arabic ordinal.
**********************************************************************/
function dateToArabicString(date,{locale, gora, calendarName}={}) {
locale ??= "ar"; // default locale "ar" if undefined
gora ??= ""; // default to using "الأول" for 1st day of month
calendarName ||= "long"; // default to corrected calendar name "هجرية" or "ميلادية"
let day = +(date.toLocaleDateString(locale , {day:'numeric'} )).replace(/[٠-٩]/g,d=>"٠١٢٣٤٥٦٧٨٩".indexOf(d)),
year = date.toLocaleDateString(locale , {year:'numeric', era:calendarName} ),
result = (day === 1 && gora.toLowerCase() === "y" ? "غرة " : daysToOrdinals() + " من شهر ") +
date.toLocaleDateString(locale, {month:'long'} ) + " سنة " + year;
if (calendarName==="long") { // correct the name of the 'era' (i.e. calendar)
if (year.slice(-2) === "هـ") result = result.slice(0,-2) + "هجرية";
else if (result.split(" ").slice(-1)=="هجري") result=result.split(" ").slice(0,-1).join(" ") + " هجرية";
else result = result.split(" ").slice(0,-1).join(" ") + " ميلادية";
}
return result;
// Convert day number to Arabic ordinal string
function daysToOrdinals() {
let unit = (day %= 100) % 10,
ordinal = "ال" + [,"أول","ثاني","ثالث","رابع","خامس","سادس","سابع","ثامن","تاسع","عاشر"][day==10?day:unit],
hadi = unit === 1 ? "الحادي" : ordinal;
return day < 11 ? ordinal : day < 20 ? hadi + " عشر" : (unit ? hadi + " و" : "") + "ال" +
[,,"عشر","ثلاث"][~~(day/10)] + "ون";
}
}
//=======================================================================
// Tests for the default locale "ar" with Gregorian months English
//=======================================================================
console.log("-".repeat(50));
console.log("Default locale 'ar' with Gregorian months English");
console.log("-".repeat(50));
console.log(dateToArabicString(new Date(1920, 0, 31)));
console.log(dateToArabicString(new Date(1967, 1, 10)));
console.log(dateToArabicString(new Date(1945, 2, 1)));
console.log(dateToArabicString(new Date(2001, 3, 22)));
console.log(dateToArabicString(new Date(2012, 4, 9)));
console.log(dateToArabicString(new Date(2021, 5, 9)));
console.log(dateToArabicString(new Date(2020, 6, 15)));
console.log(dateToArabicString(new Date(2019, 7, 14)));
console.log(dateToArabicString(new Date(2022, 8, 11)));
console.log(dateToArabicString(new Date(1985, 9, 21)));
console.log(dateToArabicString(new Date(1988, 10, 8)));
console.log(dateToArabicString(new Date(1950, 11, 7)));
console.log("اليوم هو " + dateToArabicString(new Date()));
//=======================================================================
// Tests for Assyrian Months using Lebanon Locale
//=======================================================================
console.log("-".repeat(50));
console.log("Assyrian Months using Lebanon Locale");
console.log("-".repeat(50));
options={locale:"ar-LB",gora:"y", calendarName:"long"};
console.log(dateToArabicString(new Date(1920, 0, 31),options));
console.log(dateToArabicString(new Date(1967, 1, 10),options));
console.log(dateToArabicString(new Date(1945, 2, 1),options));
console.log(dateToArabicString(new Date(2001, 3, 22),options));
console.log(dateToArabicString(new Date(2012, 4, 15),options));
console.log(dateToArabicString(new Date(2021, 5, 7),options));
console.log(dateToArabicString(new Date(2020, 6, 11),options));
console.log(dateToArabicString(new Date(2019, 7, 21),options));
console.log(dateToArabicString(new Date(2022, 8, 12),options));
console.log(dateToArabicString(new Date(1985, 9, 3),options));
console.log(dateToArabicString(new Date(1988, 10, 23),options));
console.log(dateToArabicString(new Date(1950, 11, 11),options));
console.log("اليوم هو " + dateToArabicString(new Date(),options));
//=======================================================================
// Tests for Islamic Calendar using Western-Arabic (latin) Numbers
// with option 'gora' 'غرة' for the 1st day of the month
//=======================================================================
console.log("-".repeat(50));
console.log("Islamic Hijri Months with English Numbers");
console.log("-".repeat(50));
options={locale:"ar-u-ca-islamic-nu-latn",gora:"y", calendarName:"long"};
console.log(dateToArabicString(new Date(1920, 0, 31),options));
console.log(dateToArabicString(new Date(1967, 1, 10),options));
console.log(dateToArabicString(new Date(1945, 2, 1),options));
console.log(dateToArabicString(new Date(2001, 3, 22),options));
console.log(dateToArabicString(new Date(2012, 4, 15),options));
console.log(dateToArabicString(new Date(2021, 5, 7),options));
console.log(dateToArabicString(new Date(2020, 6, 11),options));
console.log(dateToArabicString(new Date(2019, 7, 21),options));
console.log(dateToArabicString(new Date(2022, 8, 12),options));
console.log(dateToArabicString(new Date(1985, 9, 3),options));
console.log(dateToArabicString(new Date(1988, 10, 23),options));
console.log(dateToArabicString(new Date(1950, 11, 11),options));
console.log("اليوم هو " + dateToArabicString(new Date(),options));
console.log("-".repeat(50));
The following table shows the differences between the various "ar" locales for the display of the output months and the numbering systems in Javascript as the default: