Редактировать :
Я открыл ошибку , и это было подтверждено Oracle. Вы можете следить за разрешением здесь: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8216414
Я взаимодействия с хранилищем LDAP который хранит дату рождения человека, с указанием времени и часового пояса, как это:
- Если дата рождения является «27-12-2018», то строка LDAP является «20181227000000 + 0000».
Я не могу найти способ для синтаксического анализа и форматирования даты рождения , используя один и тот же шаблон .
Следующий код работает хорошо для форматирования, но не для разбора:
LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMdd'000000+0000'";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);
// Outputs correctly 20181227000000+0000
date.format(birthdateFormat);
// Throw a DatetimeParseException at index 0
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
А следующий код работает хорошо для синтаксического анализа, но не для форматирования
LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMddkkmmssxx";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);
// Throws a UnsupportedTemporalTypeException for ClockHourOfDay not supported
// Anyway I would have an unwanted string with non zero hour, minute, second, timezone
date.format(birthdateFormat);
// Parse correctly the date to 27-12-2018
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
Какой шаблон может удовлетворить и синтаксический анализ и форматирование?
Могу ли я вынужден использовать 2 различных моделей?
Я спрашиваю потому, что шаблон настроен в файле свойств. Я хочу настроить 1 шаблон только в этом файле свойств. Я хотел бы экстернализацию шаблона, поскольку LDAP не является частью моего проекта, это общий ресурс и у меня нет гарантии, что формат не может измениться.
Я предлагаю:
LocalDate date = LocalDate.of(2018, Month.DECEMBER, 27);
String pattern = "yyyyMMddHHmmssxx";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);
// Outputs 20181227000000+0000
String formatted = date.atStartOfDay(ZoneOffset.UTC).format(birthdateFormat);
System.out.println(formatted);
// Parses to 2018-12-27T00:00Z
OffsetDateTime odt = OffsetDateTime.parse("20181227000000+0000", birthdateFormat);
System.out.println(odt);
// Validate
if (! odt.toLocalTime().equals(LocalTime.MIN)) {
System.out.println("Unexpected time of day: " + odt);
}
if (! odt.getOffset().equals(ZoneOffset.UTC)) {
System.out.println("Unexpected time zone offset: " + odt);
}
// Converts to 2018-12-27
date = odt.toLocalDate();
System.out.println(date);
Строка LDAP представляет собой как дату, время и UTC смещение. Хорошее решение заключается в том , что уважать и создавать все те , при форматировании (время суток настройки до 00:00 и смещение 0) и разбор их всех обратно (в лучшем случае также проверки их улова в случае возникновения каких - либо сюрпризов). Преобразование между LocalDate
и OffsetDateTime
просто , когда вы знаете , как.
Редактирование 3: Разрешение шаблона должны быть настроено
... шаблон настраивается в файле свойств ... Я хочу, чтобы настроить 1 шаблон только в этом файле свойств.
... У меня нет никакой гарантии, что формат не может измениться.
Для того, чтобы принять во внимание возможность, что картина однажды может не содержать время суток и / или нет UTC компенсировали использовать этот форматировщик в приведенном выше коде:
DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()
.appendPattern(pattern)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter()
.withZone(ZoneOffset.UTC);
Это определяет время по умолчанию день (в полночь) и по умолчанию смещения (0). До тех пор, как время и смещение определяется в строке с LDAP, по умолчанию не используются.
Если вы думаете, что становится слишком сложным, с использованием двух настроенных форматов, один для форматирования и один для разбора, может быть лучшим решением (наименее раздражающий раствор) для вас.
Изменить: Как избежать преобразования типов
Я считаю , что выше хорошее решение. Однако, если вы настаиваете расторгающий переход от LocalDate
в ZonedDateTime
использовании atStartOfDay
и с OffsetDateTime
помощью toLocalDate
, что можно с помощью следующей хака:
DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.NEVER)
.appendValue(ChronoField.MONTH_OF_YEAR, 2, 2, SignStyle.NEVER)
.appendValue(ChronoField.DAY_OF_MONTH, 2, 2, SignStyle.NEVER)
.appendLiteral("000000+0000")
.toFormatter();
// Outputs 20181227000000+0000
String formatted = date.format(birthdateFormat);
System.out.println(formatted);
// Parses into 2018-12-27
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
System.out.println(date);
Я указав точную ширину каждого поля так, что верстальщик может знать, где, чтобы отделить их в строке при разборе.
Изменить 2: Является ли это ошибка в анализе?
Я бы сразу ожидал yyyyMMdd'000000+0000'
работать как для форматирования и разбора. Вы можете попробовать подав ошибка с Oracle и видя , что они говорят, хотя я бы не пчелы слишком оптимистично.