Использование нового 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 для решения моего простого варианта использования?

3 ответа

Я редактирую, чтобы ограничить, какая строка будет считаться действительной, используя собственный форматировщик, созданный с помощью 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 символов, чтобы отрицать необходимость в распознавателе. Однако это две разные вещи. Резольвер будет отмечать недопустимые форматы даты на входе, и подстрока, конечно, просто разделит лишние символы.


В 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()


Попробуйте использовать формат "uuuuMMdd".

licensed under cc by-sa 3.0 with attribution.