Использование нового Java 8 DateTimeFormatter для строгого синтаксического анализа даты
У меня есть простая проблема: я хочу строго разбирать строки Java в формате "yyyyMMdd"
, поэтому "19800229"
является допустимой датой, но "19820229"
- нет. Предположим, что это даты AD из нормального григорианского календаря.
Я пытаюсь использовать новый пакет java.time
от JDK 8 для решения этой проблемы, но он оказывается более сложным, чем можно надеяться. Мой текущий код:
private static final DateTimeFormatter FORMAT = DateTimeFormatter
.ofPattern("yyyyMMdd").withChronology(IsoChronology.INSTANCE)
.withResolverStyle(STRICT);
public static LocalDate parse(String yyyyMMdd) {
return LocalDate.parse(yyyyMMdd, FORMAT);
}
Однако разбор допустимой даты, такой как "19800228", создает то, что для меня является непонятной ошибкой:
java.time.format.DateTimeParseException: текст '19820228' не может быть проанализирован: невозможно получить LocalDate из TemporalAccessor: {MonthOfYear = 2, DayOfMonth = 28, YearOfEra = 1982}, ISO типа java.time.format. Проанализированные
Как использовать java.time.format.DateTimeFormatter
для решения моего простого варианта использования?
Ответы
Ответ 1
Я редактирую, чтобы ограничить, какая строка будет считаться действительной, используя собственный форматировщик, созданный с помощью DateTimeFormatterBuilder.
public class DateFormmaterTest {
static DateTimeFormatter CUSTOM_BASIC_ISO_DATE = new DateTimeFormatterBuilder()
.parseCaseInsensitive().appendValue(YEAR, 4)
.appendValue(MONTH_OF_YEAR, 2).appendValue(DAY_OF_MONTH, 2)
.optionalStart().toFormatter()
.withResolverStyle(ResolverStyle.STRICT)
.withChronology(IsoChronology.INSTANCE);
public static void main(String[] args) {
LocalDate date1 = LocalDate.parse("19800228-5000",
CUSTOM_BASIC_ISO_DATE);
System.out.println(date1);
}
}
2/29/1982 недействителен и будет вызывать следующее:
Caused by: java.time.DateTimeException: Invalid date 'February 29' as '1982' is not a leap year
at java.time.LocalDate.create(LocalDate.java:429)
Дата 19800228-5000 будет работать с BASIC_ISO_DATE, потому что она позволяет дополнительное смещение, которое вы не хотите разрешать. Мой формат CUSTOM_BASIC_ISO_DATE не позволяет этого и выдает следующее:
Exception in thread "main" java.time.format.DateTimeParseException: Text '19800228-5000' could not be parsed, unparsed text found at index 8.
Обратите внимание, что если вы уверены в длине строки, yyyyMMdd, тогда вы всегда можете работать с подстрокой первых 8 символов, чтобы отрицать необходимость в распознавателе. Однако это две разные вещи. Резольвер будет отмечать недопустимые форматы даты на входе, и подстрока, конечно, просто разделит лишние символы.
Ответ 2
В Java 8 используется uuuu
год, а не yyyy
. В Java 8 yyyy
означает "год эры" (BC или AD), и сообщение об ошибке жалуется, что MonthOfYear, DayOfMonth и YearOfEra недостаточно информации для создания даты, потому что эпоха неизвестна.
Чтобы исправить это, используйте uuuu
в строке формата, например. DateTimeFormatter.ofPattern("uuuuMMdd")
Или, если вы хотите продолжать использовать yyyy
, вы можете установить эру по умолчанию, например.
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMdd")
.parseDefaulting(ChronoField.ERA, 1 /* era is AD */)
.toFormatter()
Ответ 3
Попробуйте использовать формат "uuuuMMdd".