LocalDateTime.parse ()
Если вы можете получить строку из XML без особых проблем, для предсказуемого результата используйте
LocalDateTime.parse("1899-12-30T07:20:00")
Редактировать: Без прямого доступа к строке я полагаю, что решение состоит в том, чтобы установить смещение от GMT / UTC на вашем XMLGregorianCalendar
, чтобы избежать любой зависимости от часового пояса JVM по умолчанию:
XMLGregorianCalendar xgc = DatatypeFactory.newInstance()
.newXMLGregorianCalendar("1899-12-30T07:20:00");
xgc.setTimezone(0);
LocalTime time = xgc.toGregorianCalendar()
.toZonedDateTime()
.toLocalTime();
System.out.println(time);
Поскольку так называемый «часовой пояс» для XMLGregorianCalendar
на самом деле не что иное, как фиксированное смещение, не имеет значения, какое значение мы устанавливаем. Выходные данные из этого фрагмента постоянно:
07: 20
Я проверил с девятью часовыми поясами по умолчанию, включая Европу / Варшаву.
Поскольку вы сказали, что вас интересует только время суток, а не дата, я перевел на LocalTime
. Если вы хотите LocalDateTime
, как в вашем вопросе, просто используйте toLocalDateTime
вместо toLocalTime
,
Кроме того, это было ваше простое решение из вашего комментария:
LocalDateTime.parse(xmllGregoriaCalendar.toXMLFormat())
toXMLFormat()
воссоздает строку из XML, из которой был создан объект XMLGregorianCalendar
(документы гарантируют, что вы получите ту же строку обратно). Таким образом, этот способ также избегает всех проблем с часовыми поясами.
Редактировать: разногласия между старым и новым классами
Мне кажется, что суть проблемы заключается в старом и устаревшем TimeZone
классе и современном ZoneId
классе, не согласном с историческими смещениями из GMT / UTC.
Я провел пару экспериментов. Давайте сначала попробуем часовой пояс, который, кажется, работает правильно, Берлин. Берлин был по смещению +01: 00 с 1894 по 1915 год. Ява знает, что:
LocalDate baseDate = LocalDate.of(1899, Month.DECEMBER, 30);
ZoneId berlin = ZoneId.of("Europe/Berlin");
TimeZone tzb = TimeZone.getTimeZone(berlin);
GregorianCalendar gcb = new GregorianCalendar(tzb);
gcb.set(1899, Calendar.DECEMBER, 30);
ZonedDateTime zdtb = baseDate.atStartOfDay(berlin);
System.out.println("" + berlin + ' ' + tzb.getOffset(gcb.getTimeInMillis())
+ ' ' + berlin.getRules().getOffset(zdtb.toInstant())
+ ' ' + berlin.getRules().getOffset(zdtb.toInstant()).getTotalSeconds());
Вывод этого фрагмента:
Европа / Берлин 3600000 +01: 00 3600
Смещение на 30 декабря 1899 года дается правильно как +01: 00. Класс TimeZone
говорит 3 600 000 миллисекунд, ZoneId
говорит 3600 секунд, поэтому они согласны.
Беда с Варшавой. Варшава все время находилась в смещении по Гринвичу +01: 24 до 1915 года. Посмотрим, сможет ли Java узнать:
ZoneId warsaw = ZoneId.of("Europe/Warsaw");
TimeZone tzw = TimeZone.getTimeZone(warsaw);
GregorianCalendar gcw = new GregorianCalendar(tzw);
gcw.set(1899, Calendar.DECEMBER, 30);
ZonedDateTime zdtw = baseDate.atStartOfDay(warsaw);
System.out.println("" + warsaw + ' ' + tzw.getOffset(gcw.getTimeInMillis())
+ ' ' + warsaw.getRules().getOffset(zdtw.toInstant())
+ ' ' + warsaw.getRules().getOffset(zdtw.toInstant()).getTotalSeconds());
Европа / Варшава 3600000 +01: 24 5040
ZoneId
правильно говорит +01: 24 или 5040 секунд, но здесь TimeZone
говорит 3 600 000 миллисекунд, так же, как в случае с Берлином. Это неверно.
Старый класс GregorianCalendar
опирается на старый класс TimeZone
и поэтому дает неверные результаты при использовании часового пояса Европы / Варшавы (либо явно, либо по умолчанию). В частности вы получаете неправильный Instant
от Calendar.toInstant()
. И именно потому, что LocalDateTime.ofInstant
использует современный ZoneId
, ошибка переносится в ваш LocalDateTime
.
Также из часовых поясов Европы / Дублина, Европы / Парижа, Европы / Москвы и Азии / Калькутты я получаю противоречивые результаты.
Я запускал свои фрагменты на Java 1.8.0_131, Java 9.0.4 и Java 11. Результаты были одинаковыми для всех версий.
Ссылки