javajodatimehijri

Joda time - IllegalFieldValueException: Value 30 for dayOfMonth must be in the range [1,29]


There is a call to some external system which returns a date in hijri. In Production env, we received it as '30-02-1436'.

Consider the snippet which converts the hijri to a gregorian date, but it fails to do so.

    public static String convertHijriToGregorianDate(String hijriDate){
        Chronology iso = ISOChronology.getInstanceUTC(); // get the ISO standard chronology which we follow now
        Chronology hijri = IslamicChronology.getInstanceUTC(); // the hijri based chronology
        //String hijriDate = "19-08-1435"; // example/format which is received as a parameter in the method from response

        if(null != hijriDate && !hijriDate.isEmpty()){
            String[] parts = hijriDate.trim().split("-"); // you will get an array of size 3 by splitting with regex '-'
            String hijriYear = parts[2];
            String hijriMonth = parts[1];
            String hijriDay = parts[0];

            // Construct the Local Date corresponding to the hijri chronology
            LocalDate todayHijri = new LocalDate(Integer.parseInt(hijriYear), Integer.parseInt(hijriMonth), Integer.parseInt(hijriDay), hijri);

            // Construct the Local Date corresponding to the iso chronology based on the hijri date
            LocalDate todayIso = new LocalDate(todayHijri.toDateTimeAtStartOfDay(), iso);
            return String.valueOf(todayIso);
        }
        return null;
    }

Please suggest as to what is wrong with the code. We are getting this issue in stacktrace-

org.joda.time.IllegalFieldValueException: Value 30 for dayOfMonth must be in the range [1,29] at org.joda.time.field.FieldUtils.verifyValueBounds(FieldUtils.java:252) at org.joda.time.chrono.BasicChronology.getDateMidnightMillis(BasicChronology.java:632) at org.joda.time.chrono.BasicChronology.getDateTimeMillis0(BasicChronology.java:186) at org.joda.time.chrono.BasicChronology.getDateTimeMillis(BasicChronology.java:160) at org.joda.time.chrono.LimitChronology.getDateTimeMillis(LimitChronology.java:177) at org.joda.time.chrono.BasicChronology.getDateTimeMillis(BasicChronology.java:155) at org.joda.time.LocalDate.(LocalDate.java:457)

Joda time version used is 2.9.1


Solution

  • I have checked all available leap year patterns (=4) in Joda-Time by the expression

    Chronology hijri = 
        IslamicChronology.getInstance(DateTimeZone.UTC, IslamicChronology.LEAP_YEAR_15_BASED);
    

    Unfortunately nothing works for you. However, the umalqura-calendar of Saudi-Arabia considers '30-02-1436' as valid. It corresponds to the gregorian date 2014-12-22.

    Joda-Time does not support this variant of islamic calendar so you cannot do anything here unless you are willing to migrate to Java-8 which contains the umalqura-variant.

    System.out.println(HijrahDate.of(1436, 2, 30)); // Hijrah-umalqura AH 1436-02-30
    
    DateTimeFormatter fh =
        DateTimeFormatter.ofPattern(
            "dd-MM-yyyy",
            Locale.forLanguageTag("en")
        ).withChronology(HijrahChronology.INSTANCE);
    System.out.println(HijrahDate.from(fh.parse(input))); 
    // Hijrah-umalqura AH 1436-02-30
    System.out.println(LocalDate.from(HijrahDate.from(fh.parse(input)))); 
    // 2014-12-22
    

    Note: The unicode extension Locale.forLanguageTag("en-u-ca-islamic-umalqura") does unfortunately not seem to work in my experiments so specifying the chronology explicitly is necessary.

    Alternative:

    If you cannot migrate to Java-8 or even need more features of islamic calendar (for example other variants like those used in Joda-Time) then you might also try out my library Time4J:

    String input = "30-02-1436";
    ChronoFormatter<HijriCalendar> hf =
        ChronoFormatter.ofPattern(
            "dd-MM-yyyy",
            PatternType.CLDR,
            Locale.ROOT,
            HijriCalendar.family()
        ).withCalendarVariant(HijriCalendar.VARIANT_UMALQURA).with(Leniency.STRICT);
    System.out.println(hf.parse(input)); // AH-1436-02-30[islamic-umalqura]
    System.out.println(hf.parse(input).transform(PlainDate.axis())); // 2014-12-22