configure a custom variant in HijrahChronology for date correction jdk 8
Asked Answered
J

2

3

I have used DatePicker in javafx - JDK 8 and used HijrahChronology.INSTANCE - so that the date picker shows both the calendar - everything work good enough but I am having a difference of 1 day between gregorian calendar and hijri calendar. Hijri Calendar is 1 day backward.

I am trying to change the variant as per the following link https://bugs.openjdk.java.net/browse/JDK-8187987

but unable to succeed in it. Kindly explain or refer a better or a clear solution to this issue.

Code:

HijrahChronology hijriChronology = HijrahChronology.INSTANCE;
    dateOfTransaction.setChronology(hijriChronology);

dateOfTransaction is the instance of DatePicker in JavaFx I have not used Joda Time, neither I wish to, unless that is the only solution.

Jeaninejeanlouis answered 25/6, 2018 at 4:48 Comment(1)
I think you'll need to show us your code if you want any help with fixing it. My clairvoyance is currently running at a low ebb.Bort
A
1

Unfortunately, Java (8 to 10) does not support providing custom Hijrah variant at runtime (here is the related bug report I submitted few months ago: https://bugs.openjdk.java.net/browse/JDK-8187987).

However, there are 2 workarounds:

1- Manipulate the default hijrah variant shipped with the JRE. In Java 8, you can find it at this location:

C:\Program Files\Java\jdk1.8.0_162\jre\lib\hijrah-config-umalqura.properties

In Java 9 and 10, it is bundled at this location:

/java/time/chrono/hijrah-config-islamic-umalqura.properties

2- You can inject custom Hijrah variant at runtime using Reflection API:

public static void injectCustomHijriVariant(Map<Integer, int[]> yearMonthsMap, long isoStartDate)
        throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
{
    int minYear = Integer.MAX_VALUE;
    int maxYear = Integer.MIN_VALUE;

    for(int year : yearMonthsMap.keySet())
    {
        maxYear = Math.max(maxYear, year);
        minYear = Math.min(minYear, year);
    }

    int isoStart = (int) LocalDateTime.ofInstant(Instant.ofEpochMilli(isoStartDate),
                                                 ZoneId.systemDefault()).toLocalDate().toEpochDay();

    Field initCompleteField = HijrahChronology.class.getDeclaredField("initComplete");
    initCompleteField.setAccessible(true);
    initCompleteField.set(HijrahChronology.INSTANCE, true);

    Field hijrahStartEpochMonthField = HijrahChronology.class.getDeclaredField("hijrahStartEpochMonth");
    hijrahStartEpochMonthField.setAccessible(true);
    hijrahStartEpochMonthField.set(HijrahChronology.INSTANCE, minYear * 12);

    Field minEpochDayField = HijrahChronology.class.getDeclaredField("minEpochDay");
    minEpochDayField.setAccessible(true);
    minEpochDayField.set(HijrahChronology.INSTANCE, isoStart);

    Method createEpochMonthsMethod = HijrahChronology.class.getDeclaredMethod("createEpochMonths", int.class,
                                                                              int.class, int.class, Map.class);
    createEpochMonthsMethod.setAccessible(true);
    int[] hijrahEpochMonthStartDays = (int[]) createEpochMonthsMethod.invoke(HijrahChronology.INSTANCE, isoStart, minYear, maxYear, years);

    Field hijrahEpochMonthStartDaysField =
                                        HijrahChronology.class.getDeclaredField("hijrahEpochMonthStartDays");
    hijrahEpochMonthStartDaysField.setAccessible(true);
    hijrahEpochMonthStartDaysField.set(HijrahChronology.INSTANCE, hijrahEpochMonthStartDays);

    Field maxEpochDayField = HijrahChronology.class.getDeclaredField("maxEpochDay");
    maxEpochDayField.setAccessible(true);
    maxEpochDayField.set(HijrahChronology.INSTANCE, hijrahEpochMonthStartDays[hijrahEpochMonthStartDays.length - 1]);

    Method getYearLengthMethod = HijrahChronology.class.getDeclaredMethod("getYearLength", int.class);
    getYearLengthMethod.setAccessible(true);

    Field minYearLengthField = HijrahChronology.class.getDeclaredField("minYearLength");
    minYearLengthField.setAccessible(true);

    Field maxYearLengthField = HijrahChronology.class.getDeclaredField("maxYearLength");
    maxYearLengthField.setAccessible(true);

    for(int year = minYear; year < maxYear; year++)
    {
        int length = (int) getYearLengthMethod.invoke(HijrahChronology.INSTANCE, year);
        int minYearLength = (int) minYearLengthField.get(HijrahChronology.INSTANCE);
        int maxYearLength = (int) maxYearLengthField.get(HijrahChronology.INSTANCE);
        minYearLengthField.set(HijrahChronology.INSTANCE, Math.min(minYearLength, length));
        maxYearLengthField.set(HijrahChronology.INSTANCE, Math.max(maxYearLength, length));
    }
}
Antilog answered 25/6, 2018 at 9:42 Comment(2)
replace the "years" variable error with "yearMonthsMap" in the line int[] hijrahEpochMonthStartDays = (int[]) createEpochMonthsMethod.invoke(HijrahChronology.INSTANCE, isoStart, minYear, maxYear, years); Thanks FouadJeaninejeanlouis
Call it once (at program initialization, maybe) before using any of the date/time apis.Antilog
L
1

I am using JDK 15, and I think one can do:

  1. Copy hijrah-config-Hijrah-umalqura_islamic-umalqura.properties from java/time/chrono inside the jdk.
  2. Manually fix the issues in his copy - mainly for past years (according to http://www.ummulqura.org.sa/Index.aspx)
  3. Load the copy using the mechanisms described in the source code for HijrahChronology (The JVM looks for Hijrah chronology variant properties files in <JAVA_HOME>/conf/chronology directory)
  4. Try to set the default chronology using Chronology.ofLocaleas described in javadoc for HijrahChronology

Note, the steps above are based on understanding, One needs to test first.

Loveless answered 25/7, 2021 at 18:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.