#java #date #gregorian-calendar #java.util.calendar
#java #Дата #григорианский календарь #java.util.calendar
Вопрос:
TL; DR: я получаю странный результат при установке a GregorianCalendar
с пользовательской юлианской-> григорианской датой перехода. 1 января 1800 года становится 12 января 1800 года, где 1800 год предшествует пользовательской дате перехода (31 января 1918 года), но после даты перехода по умолчанию (15 октября 1582 года). (Это не происходит до даты отключения по умолчанию или в календаре по умолчанию.)
Это часть более крупного алгоритма, который я хочу использовать GregorianCalendar
для вычисления определенных дат в году, надеясь извлечь выгоду из того, что вычисления юлианского / григорианского високосного года будут прозрачными для меня, поскольку даты могут быть либо до, либо после даты перехода.
Я пытаюсь использовать следующие свойства GregorianCalendar
, приведенные в документах GregorianCalendar API:
-
GregorianCalendar — это гибридный календарь, который поддерживает как юлианскую, так и григорианскую календарные системы с поддержкой одного разрыва, который по умолчанию соответствует григорианской дате, когда был введен григорианский календарь (15 октября 1582 года в некоторых странах, позже в других).). Вызывающая сторона может изменить дату отключения, вызвав setGregorianChange() .
-
Перед григорианским переходом GregorianCalendar реализует юлианский календарь. Единственное различие между григорианским и юлианским календарями — правило високосного года.
-
До введения григорианского календаря Новый год отмечался 25 марта. Чтобы избежать путаницы, в этом календаре всегда используется 1 января.
Чтобы воспроизвести базовую проблему, вот базовый main()
метод Java. Я создаю 2 календаря, один григорианский по умолчанию, а другой григорианский с датой перехода, когда Россия приняла его, то есть 31 января 1918 года. Затем я установил оба календаря на 1 января 1800 года. «Русский» календарь изменяет эту дату на 12 января 1800 года, как показано при распечатке сразу после набора.
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("=: %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 дней похожа на дни, потерянные при переходе на юлианский / григорианский языки, но в данном случае это будет применяться только в период 1918 года после 31 января (за 31 января последовало 14 февраля 1918 года в России, что составляет 13-дневную разницу).
Я был бы признателен за любое объяснение или помощь, чтобы установить дату на то, что я намереваюсь.
К сожалению, я застрял со стандартными библиотеками Java 8, в настоящее время нет сторонних библиотек. Кроме того, кажется, что новые java.time
классы не будут «автоматически» помогать с переходом в юлианский / григорианский (если я ошибаюсь, я приветствую указатели), поэтому мой план B состоял бы в том, чтобы просто выполнять вычисления без использования каких-либо классов date.
Комментарии:
1. просто идея: возможно, устанавливаемая вами дата находится в «григорианском» формате, а
setGregorianChange
влияет только на отображение даты.
Ответ №1:
Я согласен, ваш код ведет себя правильно, хотя и сбивает с толку. Для форматирования вашей русской даты вам необходимо указать вашему программисту форматирования использовать русско-григорианскую дату перехода.
Установка вашего российского календаря на 1 января 1800 года работает. Никаких изменений не происходит.
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 января ( Calendar
для января ошибочно используется 0).
Когда вы переводите время в a Date
, вы получаете нормальное java.util.Date
, в этом нет ничего русского или юлианского. Date
Объект представляет некоторое время суток, которое составляет 1 января в России и 12 января по григорианскому календарю, в вашем часовом поясе по умолчанию. При дальнейшем форматировании с Date
использованием a DateFormat
с настройками по умолчанию используется стандартная григорианская дата пересечения в 1582 году и, следовательно, выводится 12 января. Чтобы напечатать 1 января, как в русском календаре, вам нужно указать форматировщику использовать дату перехода в русском григорианском формате. Вы делаете это, передавая ему a 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 date and time API, не поддерживает готовый юлианский календарь. Я читал, что вы не можете использовать какие-либо сторонние библиотеки. Варианты, которые следует рассмотреть, включают:
- Для других читателей, которые могут использовать стороннюю библиотеку: Joda-Time и it
GJChronology
. It
Реализует григорианскую / юлианскую календарную систему, которая является календарной системой, используемой в большинстве стран мира. …
- Для себя: вы можете разработать свою собственную юлианско-григорианскую хронологию для использования с java.time. Это потребует усилий, но я ожидаю, что это даст прекрасный результат.
Ссылки
- Учебное пособие по Oracle: дата и время, объясняющее, как использовать java.time.
- Документация по Joda-Time
GJChronology
Комментарии:
1. Вау, я пытался разобраться в этом чрезвычайно странном поведении календаря, тестируя множество вещей … наконец обнаружил, что проблема заключается в форматировании, а не в календаре …. затем хочу вернуться и показать свои выводы; ответил 35 минут назад . Но вы объяснили это лучше, чем я когда-либо мог 🙂
2. Вау, просто вау, это совершенно неочевидно (по крайней мере, с моей точки зрения). Спасибо за усилия!