Smart LocalDate ?

前两天线上出了个小问题,有个统计页面报错了。

简单一看,原来是前端传了个无效日期,2020-06-31

代码抛异常在这一行

LocalDate.parse(param.getEndDate())

错误信息如下:

java.time.format.DateTimeParseException: Text '2020-06-31' could not be parsed: Invalid date 'JUNE 31'

先不管为啥前端传了个0631,为啥我这转换日期会报错呢?已经加了校验了啊。

public static final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");

public static boolean isDateTimeFormat2(String date) {
        String regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}";
        Pattern pattern = Pattern.compile(regex);
        Matcher m = pattern.matcher(date);
        boolean dateFlag = m.matches();
        if (!dateFlag) {
            return false;
        } else {
            try {
                LocalDate.parse(date, dateTimeFormat);
                return true;
            } catch (DateTimeParseException var6) {
                return false;
            }
        }
    }

上面就是校验代码,用了好久了,debug了一下,发现确实校验通过了。
Smart LocalDate ?_第1张图片

嗯?等等,我明明传的【2020-06-31】,怎么变成【2020-06-30】了,咋回事?

看看源码吧,

/**
 * Obtains an instance of {@code LocalDate} from a text string such as {@code 2007-12-03}.
 * 

* The string must represent a valid date and is parsed using * {@link java.time.format.DateTimeFormatter#ISO_LOCAL_DATE}. * * @param text the text to parse such as "2007-12-03", not null * @return the parsed local date, not null * @throws DateTimeParseException if the text cannot be parsed */ public static LocalDate parse(CharSequence text) { return parse(text, DateTimeFormatter.ISO_LOCAL_DATE); } /** * Obtains an instance of {@code LocalDate} from a text string using a specific formatter. *

* The text is parsed using the formatter, returning a date. * * @param text the text to parse, not null * @param formatter the formatter to use, not null * @return the parsed local date, not null * @throws DateTimeParseException if the text cannot be parsed */ public static LocalDate parse(CharSequence text, DateTimeFormatter formatter) { Objects.requireNonNull(formatter, "formatter"); return formatter.parse(text, LocalDate::from); }

卖关子好累,不卖了。

LocalDate.parse方法有两个,区别就是指没指定DateTimeFormatter。

很明显上面的没指定,下面那个指定了

/**
 * Creates a formatter using the specified pattern.
 * 

* This method will create a formatter based on a simple * pattern of letters and symbols * as described in the class documentation. * For example, {@code d MMM uuuu} will format 2011-12-03 as '3 Dec 2011'. *

* The formatter will use the {@link Locale#getDefault(Locale.Category) default FORMAT locale}. * This can be changed using {@link DateTimeFormatter#withLocale(Locale)} on the returned formatter * Alternatively use the {@link #ofPattern(String, Locale)} variant of this method. *

* The returned formatter has no override chronology or zone. * It uses {@link ResolverStyle#SMART SMART} resolver style. * * @param pattern the pattern to use, not null * @return the formatter based on the pattern, not null * @throws IllegalArgumentException if the pattern is invalid * @see DateTimeFormatterBuilder#appendPattern(String) */ public static DateTimeFormatter ofPattern(String pattern) { return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(); }

画重点:

It uses {@link ResolverStyle#SMART SMART} resolver style.

SMART(聪明的,智能的),话说我经历好几个叫SMART的项目了。。。

DateTimeFormatter.ofPattern,使用了智能解析模式

public enum ResolverStyle {

    /**
     * Style to resolve dates and times strictly.
     * 

* Using strict resolution will ensure that all parsed values are within * the outer range of valid values for the field. Individual fields may * be further processed for strictness. *

* For example, resolving year-month and day-of-month in the ISO calendar * system using strict mode will ensure that the day-of-month is valid * for the year-month, rejecting invalid values. */ STRICT, /** * Style to resolve dates and times in a smart, or intelligent, manner. *

* Using smart resolution will perform the sensible default for each * field, which may be the same as strict, the same as lenient, or a third * behavior. Individual fields will interpret this differently. *

* For example, resolving year-month and day-of-month in the ISO calendar * system using smart mode will ensure that the day-of-month is from * 1 to 31, converting any value beyond the last valid day-of-month to be * the last valid day-of-month. */ SMART, /** * Style to resolve dates and times leniently. *

* Using lenient resolution will resolve the values in an appropriate * lenient manner. Individual fields will interpret this differently. *

* For example, lenient mode allows the month in the ISO calendar system * to be outside the range 1 to 12. * For example, month 15 is treated as being 3 months after month 12. */ LENIENT; }

怎么个智能法呢:

1 to 31, converting any value beyond the last valid day-of-month to be the last valid day-of-month.

超出这个月的最后有效日,会被转化为这个月的最后有效日。

就是说31就变成30了,但是32不会,因为不在1~31之间。

现在我们知道了,为啥会开篇所提的2020-06-31会通过了校验,因为它是SMART模式。。

public static final DateTimeFormatter ISO_LOCAL_DATE;
static {
    ISO_LOCAL_DATE = new DateTimeFormatterBuilder()
            .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
            .appendLiteral('-')
            .appendValue(MONTH_OF_YEAR, 2)
            .appendLiteral('-')
            .appendValue(DAY_OF_MONTH, 2)
            .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
}

而没指定DateTimeFormatter的,则使用了默认的ISO_LOCAL_DATE,可以看出,它使用了ResolverStyle.STRICT,严格模式。

到这里,就是全部真相了,看来jdk smart与否,还得看使用者啊

更多精彩内容欢迎点击我的博客 Telami

你可能感兴趣的:(java,jdk8)