Search code examples
datejava-8calendarlocaldatehijri

How to subtract Hijrah year from a Hijrah Date in Java 8 Date API


I want to display the Ramadan 2017 start and end dates. I tried writing code using the HijrahChronology built into Java 8 and later, with HijrahDate class.

import java.time.LocalDate;
import java.time.chrono.HijrahDate;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAdjusters;

public class Ramdan {

    public static void main(String[] args) {
        
        HijrahDate ramdanDate = HijrahDate.now().with(ChronoField.DAY_OF_MONTH, 1).with(ChronoField.MONTH_OF_YEAR, 9);
        
        LocalDate ramdanStart = LocalDate.from(ramdanDate);
        LocalDate ramdanEnd = LocalDate.from(ramdanDate.with(TemporalAdjusters.lastDayOfMonth()));
        
        System.out.println("Ramdan 2017");
        System.out.println(ramdanStart);
        System.out.println(ramdanEnd);
    }

}

But it obviously prints out the dates for current year, i.e., 2018.

Output

Ramdan 2017

2018-05-16

2018-06-14

I tried many things like minus years, or doing temporal adjustments but nothing helped. Can someone suggest a cool way of achieving it?


Solution

  • I’m not sure it’s the perfect way to do it, but this works for me and doesn’t seem all too complicated:

        ramdanDate = ramdanDate.with(ChronoField.YEAR, ramdanDate.get(ChronoField.YEAR) - 1);
    

    With this line inserted before you convert to LocalDate your code now prints:

    Ramdan 2017
    2017-05-27
    2017-06-24
    

    Edit:

    Suppose even if (in case you got a Gregorian year that overlaps with two instances of Ramadan, finding both) is desired...I cannot think of a way of achieving it. Can you suggest?

    Keep your tongue straight in your mouth:

    public static void printRamdanDates(int gregorianYear) {
        LocalDate gregDate = LocalDate.ofYearDay(gregorianYear, 1);
        HijrahDate ramdanDate = HijrahDate.from(gregDate)
                .with(ChronoField.DAY_OF_MONTH, 1)
                .with(ChronoField.MONTH_OF_YEAR, 9);
        LocalDate ramdanStart = LocalDate.from(ramdanDate);
        LocalDate ramdanEnd = LocalDate.from(ramdanDate.with(TemporalAdjusters.lastDayOfMonth()));
        // if in previous Gregorian year, skip
        while (ramdanEnd.getYear() < gregorianYear) {
            ramdanDate = ramdanDate.with(ChronoField.YEAR, ramdanDate.get(ChronoField.YEAR) + 1);
            ramdanStart = LocalDate.from(ramdanEnd);
            ramdanEnd = LocalDate.from(ramdanDate.with(TemporalAdjusters.lastDayOfMonth()));
        }
        if (ramdanStart.getYear() > gregorianYear) { // in following Gregorian year
            System.out.println("No Ramdan in " + gregorianYear);
        } else {
            System.out.println("Ramdan " + gregorianYear);
            do {
                System.out.println(ramdanStart);
                System.out.println(ramdanEnd);
                ramdanDate = ramdanDate.with(ChronoField.YEAR, ramdanDate.get(ChronoField.YEAR) + 1);
                ramdanStart = LocalDate.from(ramdanDate);
                ramdanEnd = LocalDate.from(ramdanDate.with(TemporalAdjusters.lastDayOfMonth()));
            } while (ramdanStart.getYear() == gregorianYear);
        }
    }
    

    If I feed 2017 to the above method, it gives the same result as before:

        printRamdanDates(2017);
    

    Output:

    Ramdan 2017
    2017-05-27
    2017-06-24
    

    But try, for example, 2000 or 2030 when Ramadan happens twice in the Gregorian/ISO year.

        printRamdanDates(2000);
        printRamdanDates(2030);
    

    Output:

    Ramdan 2000
    1999-12-09
    2000-01-07
    2000-11-27
    2000-12-26
    Ramdan 2030
    2030-01-05
    2030-02-03
    2030-12-26
    2031-01-23