使用新的Java 8 DateTimeFormatter进行严格的日期解析

2022-09-02 20:14:32

我有一个简单的问题:我想严格地解析格式的Java字符串,所以这是一个有效的日期,但不是。假设这些是正常公历的 AD 日期。"yyyyMMdd""19800229""19820229"

我正在尝试使用JDK 8中的新软件包来解决这个问题,但它被证明比预期的要复杂得多。我目前的代码是:java.time

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': 无法从 TemporalAccessor 获取 LocalDate: {MonthOfYear=2, DayOfMonth=28, YearOfEra=1982},java.time.format.Parsed 类型的 ISO

我如何用来解决我的简单用例?java.time.format.DateTimeFormatter


答案 1

Java 8 使用年份,而不是 .在Java 8中,意思是“时代年份”(BC或AD),错误消息抱怨MonthOfYear,DayOfMonth和YearOfEra没有足够的信息来构造日期,因为era是未知的。uuuuyyyyyyyy

要解决此问题,请在格式字符串中使用,例如uuuuDateTimeFormatter.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);

答案 2

我正在编辑以通过使用使用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);

    }
}

1982 年 2 月 29 日无效,将引发以下内容:

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个字符的子字符串来否定对解析器的需求。然而,这是两回事。解析器将在输入时标记无效的日期格式,子字符串当然只会去除多余的字符。