TL;DR: I'm getting a strange result when setting a GregorianCalendar
with a custom Julian->Gregorian cutover date. 1 Jan 1800 becomes 12 Jan 1800, where 1800 is before the custom cutover date (31 Jan 1918) but after the default cutover date (15 Oct 1582). (This does not happen before the default cutover date or on the default calendar.)
This is part of a bigger algorithm where I want to use GregorianCalendar
to calculate certain dates in a year, hoping to benefit by having the Julian/Gregorian leap year calculations be transparent to me, since the dates may be either before or after the cutover date.
I'm trying to use the following properties of GregorianCalendar
, quoted 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 base problem, here is a basic Java main()
method. I create 2 calendars, one the default Gregorian, and another one a Gregorian with the cutover date when Russia adopted it, i.e. 31 Jan 1918. Then I set both calendars to 1 Jan 1800. The "Russian" calendar changes this date to 12 Jan 1800, as shown when printed out immediately after the set.
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 outputs:
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 11-day difference looks similar to the days lost when the Julian/Gregorian switchover would be made, but that would only apply during the period in 1918 after 31 Jan, in this instance (31 Jan was followed by 14 Feb in 1918 in Russia, which is a 13-day difference).
I would appreciate any explanation, or help to get the date set to what I intend.
Unfortunately I'm stuck with standard Java 8 libraries, no 3rd party libraries possible at this time. Also, it seems the new java.time
classes won't "automatically" help with the Julian/Gregorian transition (if I'm wrong I welcome pointers), so my Plan B would be to simply do the calculations without using any date classes.
Your code behaves correctly though confusingly, I agree. For formatting your Russian date you need to instruct your formatter to use the Russian Gregorian crossover date.
Setting your Russian calendar to 1 Jan 1800 works. There is no change happening.
Output is:
The date is 1 Jan as it should (
Calendar
confusingly uses 0 for January).When you take out the time into a
Date
, you get a normaljava.util.Date
, there’s nothing Russian nor Julian about it. TheDate
object represents some time of the day that is 1 Jan in Russia and 12 Jan in the Gregorian calendar, in your default time zone. When you further format thisDate
using aDateFormat
with default settings, is uses the standard Gregorian crossover date in 1582 and therefore prints 12 Jan. To print 1 Jan as in the Russian calendar you need to instruct the formatter to use the Russian Gregorian crossover date. You do this by passing it aGregorianCalendar
that uses the desired crossover date:Output in my time zone:
Other options?
Unfortunately you are correct: java.time, the modern Java date and time API, has no support for the Julian calendar out of the box. I read that you could not use any 3rd party libraries. Options to consider include:
For other readers that may use a 3rd party library: Joda-Time and it
GJChronology
. ItFor yourself: You may develop your own Julian-Gregorian chronology to use with java.time. It would require an effort, but I would expect it to give a beautiful result.
Links
GJChronology