具有自定义切换日期的 GregorianCalendar 在切换前设置了意外日期

GregorianCalendar with a custom cutover date sets unexpected date before cutover

TL;DR:使用自定义 Julian->Gregorian 转换日期设置 GregorianCalendar 时,我得到了一个奇怪的结果。 1800 年 1 月 1 日变为 1800 年 1 月 12 日,其中 1800 是之前自定义转换日期(1918 年 1 月 31 日),但 之后 默认转换日期(10 月 15 日) 1582)。 (这不会发生在默认转换日期之前或默认日历上。)

这是一个更大的算法的一部分,我想在其中使用 GregorianCalendar 来计算一年中的某些日期,希望通过让 Julian/Gregorian 闰年计算对我透明来受益,因为日期可能在切换日期之前或之后。

我正在尝试使用 GregorianCalendar 的以下属性,引用自 GregorianCalendar API docs

要复制基本问题,这里有一个基本的 Java main() 方法。我创建了 2 个日历,一个是默认的公历,另一个是公历,在俄罗斯采用它时有转换日期,即 1918 年 1 月 31 日。然后我将两个日历都设置为 1800 年 1 月 1 日。“俄罗斯”日历发生变化这个日期到 1800 年 1 月 12 日,如设置后立即打印时所示。

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()));
  }
}

这输出:

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 

11 天的差异看起来类似于 Julian/Gregorian 转换时损失的天数,但这仅适用于 1 月 31 日之后的 1918 年期间,在这种情况下(随后是 1 月 31 日)到 1918 年 2 月 14 日在俄罗斯,相差 13 天)。

我将不胜感激任何解释,或帮助将日期设置为我想要的。

不幸的是,我受困于标准 Java 8 个库,目前无法使用第 3 方库。此外,新的 java.time 类 似乎不会“自动”帮助 Julian/Gregorian 过渡(如果我错了欢迎指点),所以我的 B 计划只是简单地不使用任何日期进行计算 类.

您的代码虽然令人困惑,但表现正确,我同意。要格式化您的俄语日期,您需要指示您的格式化程序使用俄语公历交叉日期。

将俄罗斯日历设置为 1800 年 1 月 1 日有效。没有发生任何变化。

    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));

输出为:

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

日期应该是 1 月 1 日(Calendar 混淆地使用 0 表示 1 月)。

当你把时间拿出来变成 Date 时,你会得到一个正常的 java.util.Date,没有任何俄罗斯和 Julian 的东西。 Date 对象表示一天中的某个时间,即俄罗斯的 1 月 1 日和公历的 1 月 12 日,在您的默认时区中。当您使用具有默认设置的 DateFormat 进一步格式化此 Date 时,将使用 1582 年的标准公历交叉日期,因此打印 1 月 12 日。要像俄罗斯日历一样打印 1 月 1 日,您需要指示 格式化程序 使用俄罗斯公历交叉日期。您可以通过向其传递一个使用所需交叉日期的 GregorianCalendar 来执行此操作:

    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));

我所在时区的输出:

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

其他选项?

很遗憾,您是对的:java.time,现代 Java 日期和时间 API,不支持开箱即用的儒略历。我读到你不能使用任何第 3 方库。要考虑的选项包括:

  • 对于可能使用第 3 方库的其他读者:Joda-Time 及其 GJChronology。它

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

  • 对于您自己:您可以开发自己的儒略-格里高利年表以与 java.time 一起使用。这需要付出努力,但我希望它能给出一个漂亮的结果。

链接