Using new Java 8 DateTimeFormatter to do strict da

2019-06-15 16:22发布

I have a simple problem : I want to parse Java strings in format "yyyyMMdd" strictly, so that "19800229" is a valid date, but "19820229" is not. Assume these are AD dates from the normal Gregorian calendar.

I am trying to use the new java.time package from JDK 8 to solve this problem, but it is proving more complicated than hoped. My current code is:

private static final DateTimeFormatter FORMAT = DateTimeFormatter
        .ofPattern("yyyyMMdd").withChronology(IsoChronology.INSTANCE)
        .withResolverStyle(STRICT);

public static LocalDate parse(String yyyyMMdd) {
    return LocalDate.parse(yyyyMMdd, FORMAT);
}

However, parsing a valid date such as "19800228" produces what to me is an incomprehensible error:

java.time.format.DateTimeParseException: Text '19820228' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {MonthOfYear=2, DayOfMonth=28, YearOfEra=1982},ISO of type java.time.format.Parsed

How do I use java.time.format.DateTimeFormatter to solve my simple use case?

3条回答
姐就是有狂的资本
2楼-- · 2019-06-15 16:49

I'm editing to limit what kind of string will be considered valid by using a custom formatter created with a 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 is invalid and would throw the following:

Caused by: java.time.DateTimeException: Invalid date 'February 29' as '1982' is not a leap year
    at java.time.LocalDate.create(LocalDate.java:429)

A date of 19800228-5000 would work with BASIC_ISO_DATE because it allows the optional offset which you don't want allowed. My CUSTOM_BASIC_ISO_DATE formatter will not allow that and throw the following:

Exception in thread "main" java.time.format.DateTimeParseException: Text '19800228-5000' could not be parsed, unparsed text found at index 8. 

Note, if you are sure of the string length, yyyyMMdd then you could always work with the substring of first 8 chars to negate the need for the resolver. However that is two different things. The resolver will flag invalid date formats on input and the substring would of course just strip the extra chars out.

查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-06-15 16:51

Java 8 uses uuuu for year, not yyyy. In Java 8, yyyy means "year of era" (BC or AD) and the error message complains that MonthOfYear, DayOfMonth and YearOfEra is not enough information to construct the date because era is not known.

To fix that, use uuuu in your format string, e.g. DateTimeFormatter.ofPattern("uuuuMMdd")

Or, if you want to keep using yyyy, you can set the default era, e.g.

private static final DateTimeFormatter FORMAT = new DateTimeFormatterBuilder()
            .appendPattern("yyyyMMdd")
            .parseDefaulting(ChronoField.ERA, 1 /* era is AD */)
            .toFormatter()
            .withChronology(IsoChronology.INSTANCE)
            .withResolverStyle(ResolverStyle.STRICT);
查看更多
走好不送
4楼-- · 2019-06-15 16:53

Try using format "uuuuMMdd" instead.

查看更多
登录 后发表回答