Java – GregorianCalendar with custom switch date set an unexpected date before switchover

GregorianCalendar with custom switch date set an unexpected date before switchover… here is a solution to the problem.

GregorianCalendar with custom switch date set an unexpected date before switchover

TL;DR: I get a strange result when setting up GregorianCalendar with a custom Julian-> Gregorian conversion date. January 1, 1800 becomes January 12, 1800, where 1800 is the previous custom conversion date (January 31, 1918) but then the default conversion date (October 15, 1582). (This does not happen before the default conversion date or on the default calendar.) )

This is part of a larger algorithm, and I want to use GregorianCalendar to calculate certain dates of the year, hoping to benefit by making the Julian/Gregorian leap year calculation transparent to me, as dates may be before or after switching dates.

I’m trying to use the following property of GregorianCalendar, referenced from GregorianCalendar API docs :

  • >

    GregorianCalendar is a hybrid calendar that supports both the Julian and Gregorian calendar systems with the support of a single discontinuity, which corresponds by default to the Gregorian date when the Gregorian calendar was instituted (October 15, 1582 in some countries,
    later in others). The cutover date may be changed by the caller by calling setGregorianChange().

  • >

    Before the Gregorian cutover, GregorianCalendar implements the Julian calendar. The only difference between the Gregorian and the Julian calendar is the leap year rule.

  • >

    Prior to the institution of the Gregorian calendar, New Year’s Day was March 25. To avoid confusion, this calendar always uses January 1.

To replicate the basic problem, here is a basic Java main() method. I created 2 calendars, one for the default Gregorian calendar and the other for the Gregorian calendar with the converted date when Russia adopted it, that is, January 31, 1918. Then I set both calendars to January 1, 1800. The “Russian” calendar changed this date to January 12, 1800, as shown when it was printed immediately after setup.

public static void main(String[] args) {
  DateFormat DF = DateFormat.getDateInstance(DateFormat.SHORT);

System.out.printf("Generic Gregorian Calendar (after cutover)%n");
  GregorianCalendar genericCal = new GregorianCalendar();
  System.out.printf("  Changeover=%s%n", DF.format(genericCal.getGregorianChange()));
  genericCal.set(1800, Calendar.JANUARY, 1);
  System.out.printf("%3s  %s%n", "", DF.format(genericCal.getTime()));

System.out.printf("Russian Gregorian Calendar (before cutover)%n");
  GregorianCalendar russianCal = new GregorianCalendar();
  russianCal.setGregorianChange(new GregorianCalendar(1918, Calendar.JANUARY, 31).getTime());
  System.out.printf("  Changeover=%s%n", DF.format(russianCal.getGregorianChange()));
  russianCal.set(1800, Calendar.JANUARY, 1);
  System.out.printf("%3s  %s%n", "", DF.format(russianCal.getTime()));
  for (int i = 1; i < 15; i++) {
    russianCal.add(Calendar.DAY_OF_YEAR, -1);
    System.out.printf("%3d: %s %n", -i, DF.format(russianCal.getTime()));
  }
}

This output:

Generic Gregorian Calendar (after cutover)
  Changeover=1582/10/15
     1800/01/01
Russian Gregorian Calendar (before cutover)
  Changeover=1918/01/31
     1800/01/12
 -1: 1800/01/11 
 -2: 1800/01/10 
 -3: 1800/01/09 
 -4: 1800/01/08 
 -5: 1800/01/07 
 -6: 1800/01/06 
 -7: 1800/01/05 
 -8: 1800/01/04 
 -9: 1800/01/03 
-10: 1800/01/02 
-11: 1800/01/01 
-12: 1799/12/31 
-13: 1799/12/30 
-14: 1799/12/29 

The difference of these 11 days

looks similar to the number of days lost when making the Julian / Gregorian conversion, but this applies only to the period of 1918 after January 31, in this case (February 31 after January 14 in Russia, a difference of 13 days).

I

would appreciate any explanation, or help setting the date to what I want.

Unfortunately, I’m stuck with the standard Java 8 library and can’t use the 3rd-party library at the moment. Also, the new java.time class doesn’t seem to “automatically” help Julian/Gregorian conversions (welcome to point out if I’m wrong), so my plan B is to simply do calculations without using any date classes.

Solution

Your code, while confusing, behaves correctly, I agree. To format your Russian date, you need to instruct your formatter to use the Russian Gregorian calendar cross date.

Set the Russian calendar to January 1, 1800. Nothing has changed.

    GregorianCalendar russianCal = new GregorianCalendar();
    russianCal.setGregorianChange(
            new GregorianCalendar(1918, Calendar.JANUARY, 31).getTime());
    russianCal.set(1800, Calendar.JANUARY, 1);
    
System.out.println("Russian month (0-based):      "
            + russianCal.get(Calendar.MONTH));
    System.out.println("Russian day of month:         "
            + russianCal.get(Calendar.DATE));

The output is:

Russian month (0-based):      0
Russian day of month:         1

The date should be January 1 (Calendar uses 0 confusingly for January).

When you put the time into Date, you get a plain java.util.Date, which has neither the Russian nor Julian meaning. The Date object represents a time of day, January 1 in Russia and January 12 in the Gregorian calendar, in your default time zone. When you further format this Date with a DateFormat with default settings , the standard Gregorian cross date of 1582 is used, so 12 Jan is printed. To print 1 Jan, like a Russian calendar, you need to instruct the formatter to cross dates using the Russian Gregorian calendar. To do this, you can pass it a GregorianCalendar: that uses the desired cross date

    Date dateCorrespondingToRussianCal = russianCal.getTime();
    System.out.println("Corresponding java.util.Date: "
            + dateCorrespondingToRussianCal);
    
DateFormat russianDateFormatter
            = DateFormat.getDateInstance(DateFormat.SHORT);
    russianDateFormatter.setCalendar(russianCal);
    System.out.println("Formatted Russian date:       "
            + russianDateFormatter.format(dateCorrespondingToRussianCal));

Output in my time zone:

Corresponding java.util.Date: Sun Jan 12 13:10:30 CET 1800
Formatted Russian date:       1/1/00

Other options?

Unfortunately, you’re right: the modern Java date and time API java.time doesn’t support Julian calendars out of the box. I read that you can’t use any 3rd party libraries. Options to consider include:

  • For other readers who might use 3rd-party libraries: Joda-Time and its GJChronology. It

    Implements the Gregorian/Julian calendar system which is the calendar system used in most of the world. …

  • For yourself: You can develop your own Julian-Gregorian chronology to use with java.time. It takes effort, but I hope it will lead to good results.

Link

Related Problems and Solutions