Introduction

The Gregorian calendar is the most widely used civil calendar in the world, while the Jalali calendar, also known as the Persian or Solar Hijri calendar, is primarily used in Iran and Afghanistan. In this blog post, we will demonstrate how to convert dates between these two calendars using the Time4J library in a Java Maven project.

Setting Up the Maven Project

First, let’s create a new Maven project and add the Time4J library as a dependency in the pom.xml file:

<dependencies>
    <dependency>
        <groupId>net.time4j</groupId>
        <artifactId>time4j-calendar</artifactId>
        <version>5.5</version>
    </dependency>
</dependencies>

Converting Gregorian Date to Jalali Date

Now, let’s write a method to convert a Gregorian date to a Jalali date:

import net.time4j.PlainDate;
import net.time4j.calendar.PersianCalendar;

public class CalendarConverter {

    public static PersianCalendar gregorianToJalali(PlainDate gregorianDate){
        return PersianCalendar.from(gregorianDate);
    }
}

Converting Jalali Date to Gregorian Date

Similarly, let’s write a method to convert a Jalali date to a Gregorian date:

public static PlainDate jalaliToGregorian(PersianCalendar jalaliDate) {
    return jalaliDate.toGregorian();
}

Example Usage

Here’s an example of how to use the conversion methods in a Java application:

import net.time4j.PlainDate;
import net.time4j.calendar.PersianCalendar;

public class Main {
    public static void main(String[] args) {
        // Converting Gregorian date to Jalali date
        PlainDate gregorianDate = PlainDate.of(2023, 4, 12);
        PersianCalendar jalaliDate = CalendarConverter.gregorianToJalali(gregorianDate);
        System.out.println("Gregorian date: " + gregorianDate);
        System.out.println("Jalali date: " + jalaliDate);

        // Converting Jalali date to Gregorian date
        PersianCalendar jalaliDate2 = PersianCalendar.of(1402, 1, 23);
        PlainDate gregorianDate2 = CalendarConverter.jalaliToGregorian(jalaliDate2);
        System.out.println("Jalali date: " + jalaliDate2);
        System.out.println("Gregorian date: " + gregorianDate2);
    }
}

Output:

Gregorian date: 2023-04-12
Jalali date: 1402-01-23(Persian)
Jalali date: 1402-01-23(Persian)
Gregorian date: 2023-04-12

Unittests

Adding a JUnit test with edge cases for the calendar conversion methods ensures their correctness and robustness. Let’s create a test class, CalendarConverterTest, and add the JUnit and AssertJ dependencies to the pom.xml file:

<dependencies>
    <!-- Other dependencies -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.8.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>3.21.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Now, let’s create the CalendarConverterTest class and add test methods with edge cases:

import net.time4j.PlainDate;
import net.time4j.calendar.PersianCalendar;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

public class CalendarConverterTest {

    @Test
    public void testGregorianToJalali() {
        // Test minimum supported Gregorian date
        PlainDate minGregorianDate = PlainDate.of(-999999999, 1, 1);
        PersianCalendar minJalaliDate = CalendarConverter.gregorianToJalali(minGregorianDate);
        assertThat(minJalaliDate).isEqualTo(PersianCalendar.of(-1000000004, 10, 11));

        // Test maximum supported Gregorian date
        PlainDate maxGregorianDate = PlainDate.of(999999999, 12, 31);
        PersianCalendar maxJalaliDate = CalendarConverter.gregorianToJalali(maxGregorianDate);
        assertThat(maxJalaliDate).isEqualTo(PersianCalendar.of(1000000004, 10, 10));
    }

    @Test
    public void testJalaliToGregorian() {
        // Test minimum supported Jalali date
        PersianCalendar minJalaliDate = PersianCalendar.of(-1000000004, 10, 11);
        PlainDate minGregorianDate = CalendarConverter.jalaliToGregorian(minJalaliDate);
        assertThat(minGregorianDate).isEqualTo(PlainDate.of(-999999999, 1, 1));

        // Test maximum supported Jalali date
        PersianCalendar maxJalaliDate = PersianCalendar.of(1000000004, 10, 10);
        PlainDate maxGregorianDate = CalendarConverter.jalaliToGregorian(maxJalaliDate);
        assertThat(maxGregorianDate).isEqualTo(PlainDate.of(999999999, 12, 31));
    }
}

In these test methods, we have checked the correctness of the conversion methods using the minimum and maximum supported dates for both the Gregorian and Jalali calendars. Running the tests will help ensure that the calendar conversion methods are working correctly, even for edge cases.

Conclusion

In this blog post, we have demonstrated how to convert dates between the Gregorian and Jalali calendars using the Time4J library in a Java Maven project. This can be useful for applications that require support for both calendar systems.

The conversion between Gregorian and Jalali dates involves mathematical calculations based on the properties of both calendar systems. Here, we’ll outline the algorithms for converting dates between these two calendars.

Gregorian to Jalali:

  1. Calculate the number of days passed since a reference date (e.g., the Gregorian date 15 October 1582, which is equivalent to the Jalali date 1 Farvardin 962).
  2. Calculate the number of Jalali years passed since the reference date by dividing the total number of days by the average length of a Jalali year (approximately 365.2425 days).
  3. Estimate the number of Jalali leap years passed since the reference date. A Jalali leap year occurs if the remainder of the year number when divided by 33 is one of the following numbers: {1, 5, 9, 13, 17, 22, 26, 30}.
  4. Calculate the number of days in the current Jalali year by subtracting the total number of days in the passed Jalali years (including leap years) from the initial number of days since the reference date.
  5. Determine the current Jalali month by finding the month in which the remaining days fall, considering the number of days in each month (the first six months have 31 days, and the next five months have 30 days; the last month has 29 days in common years and 30 days in leap years).
  6. Finally, the remaining days represent the current day in the Jalali calendar.

Jalali to Gregorian:

  1. Calculate the number of days passed since a reference date (e.g., the Jalali date 1 Farvardin 962, which is equivalent to the Gregorian date 15 October 1582).
  2. Calculate the number of Gregorian years passed since the reference date by dividing the total number of days by the average length of a Gregorian year (approximately 365.2425 days).
  3. Estimate the number of Gregorian leap years passed since the reference date. A Gregorian leap year occurs if the year is divisible by 4, but not divisible by 100, or if the year is divisible by 400.
  4. Calculate the number of days in the current Gregorian year by subtracting the total number of days in the passed Gregorian years (including leap years) from the initial number of days since the reference date.
  5. Determine the current Gregorian month by finding the month in which the remaining days fall, considering the number of days in each month (January, March, May, July, August, October, and December have 31 days; April, June, September, and November have 30 days; February has 28 days in common years and 29 days in leap years).
  6. Finally, the remaining days represent the current day in the Gregorian calendar.

Note that the algorithms described above are simplifications of the actual algorithms used in the Time4J library, which employs additional optimizations and mathematical techniques to improve the accuracy and efficiency of the conversion process.

Categories: Hands-on