Использование нового Java 8 DateTimeFormatter для строгого анализа даты

у меня простая проблема: я хочу разобрать строки Java в формате "yyyyMMdd" строго, так что "19800229" является допустимой датой, но "19820229" нет. Предположим, это даты объявления из обычного григорианского календаря.

Я пытаюсь использовать новый тег 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.время.формат.DateTimeParseException: не удалось проанализировать текст '19820228': невозможно получить LocalDate от TemporalAccessor: {MonthOfYear=2, DayOfMonth=28, YearOfEra=1982},ISO типа java.время.формат.Разобран

как использовать 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, вы можете установить эру по умолчанию, например

private static final DateTimeFormatter FORMAT = new DateTimeFormatterBuilder()
            .appendPattern("yyyyMMdd")
            .parseDefaulting(ChronoField.ERA, 1 /* era is AD */)
            .toFormatter()
            .withChronology(IsoChronology.INSTANCE)
            .withResolverStyle(ResolverStyle.STRICT);

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