Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a gregorian date from Hijri date strings

I would like to get a gregorian date from the Hijri date indexs.

e.g

getDate(1436, "SHawwal", 18)

This should return

3rd August 2015

How could I do the conversion ?

like image 375
Prakash Raman Avatar asked Aug 03 '15 16:08

Prakash Raman


People also ask

How do I change my date from Hijri to Gregorian?

On your Android phone or tablet, open your Google app Google Search. Tap More ... > Settings > Language & region > Search region. Tap the region you want to see search results in.

How do I change the date format from Hijri to Gregorian in Excel?

On the Format menu, click Cells, and then click the Number tab. Select Custom from the list of categories.

How do I get both Hijri and Gregorian calendar displayed on my Google calendar?

To add the Hijri Calendar you need to sign in to your Google Account and go to your calendar. Click on Settings and under the General tab, choose Alternate Calendars and select the Hijri calendar of your choice.


1 Answers

I regret that you were ill advised. Many people blindly recommend well-known libraries like Joda-Time without even checking if this can solve your problem and fit your requirements.

Which requirement? You have stated:

value tuple (1436, "SHawwal", 18) => gregorian date as String ("3rd August 2015")

Let's first demonstrate that Joda-Time is not capable of doing this conversion. Shawwal ("شَوّال") is the tenth month of islamic lunar calendar. So we can try this Joda-Time code:

Chronology hijri = 
  IslamicChronology.getInstance(DateTimeZone.UTC, IslamicChronology.LEAP_YEAR_INDIAN);
LocalDate dtHijri = new LocalDate(1436, 10, 18, hijri);

Chronology iso = ISOChronology.getInstanceUTC();
LocalDate dtIso = // Joda does not offer a direct conversion, sorry
  dtHijri.toDateTimeAtStartOfDay(DateTimeZone.UTC)
  .withChronology(iso).toLocalDate(); 
System.out.println("hijri-to-gregorian: " + dtHijri + "=>" + dtIso); 
// hijri-to-gregorian: 1436-10-18=>2015-08-04

All leap year patterns offered by Joda-Time (LEAP_YEAR_15_BASED, LEAP_YEAR_16_BASED, LEAP_YEAR_HABASH_AL_HASIB or LEAP_YEAR_INDIAN) yield the same result "2015-08-04" which is one day later than your expectation. And I have explicitly tested all four leap year patterns.

Is Java-8 an alternative?

HijrahDate hd = HijrahChronology.INSTANCE.date(HijrahEra.AH, 1436, 10, 18);
LocalDate ld = LocalDate.from(hd);
System.out.println("java-8: " + ld); // java-8: 2015-08-03

That is fine, but I assume you need to make this work on Android. And there you would fail because the Java-8-classes are simply not available.

By the way: Why does Java-8 yield the right result here? It uses the data of the Umalqura-calendar in Saudi-Arabia while Joda-Time uses four half-academic approximations of islamic calendar. In fact, the islamic calendar exists in many local variants (and my comment above had targeted this point to ask you which variant you want to use).

So will ThreetenABP do it since it is supposed to be a backport of java.time (JSR-310) on Android?

HijrahDate hd = HijrahChronology.INSTANCE.date(HijrahEra.AH, 1436, 10, 18);
org.threeten.bp.LocalDate ld = org.threeten.bp.LocalDate.from(hd);
System.out.println("threeten-bp: " + ld); // threeten-bp: 2015-08-04

Surprisingly the backport fails. If you watch out the documentations of ThreetenBP and Java-8 regarding the Hijri calendar then you will see a big difference. ThreetenBP does NOT use the Umalqura calendar! It is de facto equivalent to the leap year pattern LEAP_YEAR_16_BASED of Joda-Time. Well, as (awkward) workaround you could configure ThreetenBP such that you tell the deviations by defining a configuration file described here. But I strongly suspect this would only work on Java-platforms because ThreetenABP (the Android migration of ThreetenBP) does not care about defining such a file in the assets-directory of a given Android app.

What about ICU4J?

ICU4J (International components for Unicode) seems to work, at least with three variants: ISLAMIC, ISLAMIC_TBLA and ISLAMIC_UMALQURA. All 3 variants yield "2015-08-03". So you have to carefully decide which one to use because these variants will differ on other days! About the TBLA-variant, that is a half-academic approximation similar to LEAP_YEAR_16_BASED of Joda-Time (but using Thursday epoch - one day difference all the time). The variant called ISLAMIC is a simplified astronomical simulation of moon cycles. My suspicion you rather want the Umalqura-calendar of Saudi-Arabia, right???

Anyway, ICU4J has one big disadvantage. It is not well designed for the Android platform and very big (10.7 MByte). Some people have reported to have not been able to run that software on Android.

My recommendation: Use Time4A

The result of my analysis of current support for Hijri calendar on Android platform has been to develop an alternative library called Time4A to help people using the Hijri calendar on that platform, too. This library (version v3.6-2015f) supports 8 half-academic leap year patterns including the tbla-variant (HijriAlgorithm.WEST_ISLAMIC_ASTRO) , the Umalqura-calendar of Saudi-Arabia and the astronomical simulation of ICU4J. And important: Time4A will also understand Arabic month names (using new Locale("ar")). Example:

HijriCalendar hijri = 
  HijriCalendar.of(HijriCalendar.VARIANT_UMALQURA, 1436, HijriMonth.SHAWWAL, 18);
PlainDate iso = hijri.transform(PlainDate.class);
System.out.println("Time4A: " + iso); // Time4A: 2015-08-03

Now the final code solution handling some oddities in your requirements:

int hijriYear = 1436;
String hijriMonth = "SHawwal";
int hijriDayOfMonth = 18;

ChronoFormatter<HijriCalendar> inputFormat =
        ChronoFormatter.setUp(HijriCalendar.class, Locale.ENGLISH)
        .addPattern("yyyyMMMMd", PatternType.NON_ISO_DATE).build();
HijriCalendar hijriDate = 
        inputFormat
        .with(Attributes.PARSE_CASE_INSENSITIVE, true)
        .withCalendarVariant(HijriCalendar.VARIANT_UMALQURA)
        .parse(hijriYear + hijriMonth + hijriDayOfMonth);

PlainDate iso = hijriDate.transform(PlainDate.class);

ChronoFormatter<PlainDate> outputFormat =
    ChronoFormatter.setUp(PlainDate.class, Locale.ENGLISH)
        .addEnglishOrdinal(PlainDate.DAY_OF_MONTH)
        .addPattern(" MMMM uuuu", PatternType.CLDR).build();
String s = outputFormat.format(iso);
System.out.println(s); // 3rd August 2015

But you still need to think about which variant of Hijri calendar you really want. No intelligent software can decide this for you.

like image 109
Meno Hochschild Avatar answered Sep 17 '22 22:09

Meno Hochschild