JDK踩坑—— Smart LocalDate

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> JDK踩坑—— Smart LocalDate


点击上方 好好学java ,选择 星标 公众号

重磅资讯,干货,第一时间送达今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W +访问量博客:点击前往,查看更多

转自:Telami,
链接:telami.cn/2020/smart_localdate/

链接:telami.cn/2020/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了一下,发现确实校验通过了。

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

看看源码吧。


/**
 * Obtains an instance of {@code LocalDate} from a text string such as {@code 2007-12-03}.
 * <p>
 * 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.
 * <p>
 * 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.
 * <p>
 * This method will create a formatter based on a simple
 * <a href="#patterns">pattern of letters and symbols</a>
 * as described in the class documentation.
 * For example, {@code d MMM uuuu} will format 2011-12-03 as '3 Dec 2011'.
 * <p>
 * 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.
 * <p>
 * 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.
     * <p>
     * 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.
     * <p>
     * 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.
     * <p>
     * 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.
     * <p>
     * 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.
     * <p>
     * Using lenient resolution will resolve the values in an appropriate
     * lenient manner. Individual fields will interpret this differently.
     * <p>
     * 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与否,还得看使用者啊。


推荐文章


原创电子书历时整整一年总结的 Java面试+ Java入门技术学习指南,这是本人这几年及校招的总结,各种异步面试题已经全部进行总结,按照章节复习即可,已经拿到了了大厂提供。
原创思维导图扫码或者微信搜 程序员的技术圈子 回复 面试 领取原创电子书和思维导图。

原文地址:https://sihai.blog.csdn.net/article/details/113409381

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> JDK踩坑—— Smart LocalDate


 上一篇
Spring 中经典的 9 种设计模式,打死也要记住啊! Spring 中经典的 9 种设计模式,打死也要记住啊!
点击上方 好好学java ,选择 星标 公众号 重磅资讯,干货,第一时间送达今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W +访问量博客:点击前往,查看更多 Spring中
2021-04-04
下一篇 
同事写了一个update,误用一个双引号,生产数据全变0了! 同事写了一个update,误用一个双引号,生产数据全变0了!
点击上方 好好学java ,选择 星标 公众号 重磅资讯,干货,第一时间送达 今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W +访问量博客:点击前往,查看更多 来源:ford
2021-04-04