美文网首页就事论事
就事论事系列-时间格式的处理

就事论事系列-时间格式的处理

作者: 大师艾小伦 | 来源:发表于2020-03-01 14:31 被阅读0次

    在日常工作生产中,我们会经常和时间格式打交道。
    2020年2月29日,是一个特殊的日期,我们代码在进行一个日期校验的时候出现了一个bug,因此,咱们就来聊一聊,时间的格式验证的问题。

    日期格式校验我们常常遇到的情况是,给定一个格式,根据这个格式校验输入的字符串是否符合条件。
    解决方案有很多,根据工作经验,不同的小伙伴会有很多不同的解决方案,有些通过拆分字符串,有些会选择正则表达式。

    我们这里我们给一个正则表达式的案例

    //时间格式
    private static final String RULE_YYYYMMDDHHMMSS 
        = "^\\d{4}(((0[13578]|1[02])(0[1-9]|[12][0-9]|3[01]))" +
    "|((0[469]|11)(0[1-9]|[12][0-9]|30))|(02(0[1-9]|[1][0-9]|2[0-8])))"
         +"([0-1][0-9]|2[0-3])([0-5][0-9])([0-5][0-9])";
         
    public boolean validDateTypeYYYYMMDDHHMMSS(String str) {
            return StringUtils.isNotBlank(str)
             && str.matches(RULE_YYYYMMDDHHMMSS);
    }
    

    这里我们能够看到,上面要校验的格式是yyyyMMddhhmmss格式的日期,该方法在传入的日期字符串不为空的情况下,通过正则表达式,能够迅速地进行格式校验;

    public static void main(String[] args) {
        ValidDataUtil v = new ValidDataUtil();
        //正常的日期
        System.out.println(
        v.validDateTypeYYYYMMDDHHMMSS("20211221010101"));
        //正常日期后面多个数字
        System.out.println(
        v.validDateTypeYYYYMMDDHHMMSS("202112210101011"));
    }
    
    output:
    true
    false
    

    大体上还是符合预期的。
    但是这个正则表达式忽略了一个问题,就是闰年的问题。
    当我们使用这样的一个日期格式进行校验的时候就就发现验证错误了。

    System.out.println(  v.validDateTypeYYYYMMDDHHMMSS("20200229010101"));
    
    output:
    false
    

    从上面正则表达式进行分析,其实也可以得到这样的结果。

    这也就是我们进行格式校验的时候无法避免的一个问题,闰年如何处理。

    要么宽松,将每个月的日期都允许输入31日,要么就需要非常复杂的正则表达式去完成。

    Java为了支持多语言,没有固定的日期格式。你需要根据自己的需要指定日期格式,然后用DateFormat类或者SimpleDateFormat类来判断是否是正确的日期格式
    DateFormat是SimpleDateFormat的一个超类,验证格式的功能我们需要使用 SimpleDateFormat

    上述方法我们可以改造成

    
    public boolean validDateTypeYYYYMMDDHHMMSS(String str) {
        if(StringUtils.isBlank(str)){
            return false;
        }
        try{
            SimpleDateFormat sdf 
              = new SimpleDateFormat("yyyyMMddhhmmss");
            sdf.parse(str);
        }catch (ParseException e){
                 return false;
        }
        return true;
    }
    

    还是上面的逻辑,这个方法的原理就是,通过指定格式,将字符串转化为一个日期对象,转化的方法如果在执行时没有抛出异常就认为校验通过,反之就说明日期字符串有问题。我们把校验的过程交给jdk去处理。
    经过验证"20200229010101"被正确识别了
    但是,问题接着就来了。我们发现使用一些特别的数据进行测试的时候,发现这些“日期”居然也能够通过校验

    比如:
    正常日期后面多个数字: "202112210101011"
    一个不存在的日子:"20200230010101"

    这样的“日期”在校验过程中也返回了true。是jdk的bug吗?
    其实SimpleDateFormat 类中有个方法

    public void setLenient(boolean lenient)
    

    Specify whether or not date/time parsing is to be lenient. With lenient parsing, the parser may use heuristics to interpret inputs that do not precisely match this object's format. With strict parsing, inputs must match this object's format.

    指定日期/时间分析是否宽松。对于宽松的解析,解析器可以使用启发式来解释与此对象的格式不完全匹配的输入。通过严格的解析,输入必须与此对象的格式匹配

    如果不指定的话,将默认使用宽松的模式进行验证。这就解释了上面的结果,我们这里只需要对上面的方法改成

    
    public boolean validDateTypeYYYYMMDDHHMMSS(String str) {
        if(StringUtils.isBlank(str)){
            return false;
        }
        try{
            SimpleDateFormat sdf 
              = new SimpleDateFormat("yyyyMMddhhmmss");
            sdf.setLenient(false);
            sdf.parse(str);
        }catch (ParseException e){
                 return false;
        }
        return true;
    }
    

    再次执行时方法就能得到我们想要的结果了。
    对于这种方式在大多数项目中可能正在被使用,一个更为普遍的做法应该是,将该其改造成一个通用的方法

    /**
     * 验证日期格式数据是否符合条件
     * 如果日期为空或者空字符串这直接返回false
     * 根据格式将字符串转化为日期对象不抛异常则返回true,否则返回false
     * @param dateStr 日期
     * @param format 格式
     * @param lenient 是否宽松的格式,
     *   如果是宽松的验证的情况 20200230这样的日期会被允许认为是正确的 
     *   否则就是错误的
     * @return 验证通过则返回true 否则返回false
     */
    public static boolean validateDate(String dateStr,
         String format,boolean lenient){
        if(null == dateStr || dateStr.trim().length() == 0){
            return false;
        }
        try {
            SimpleDateFormat simpleDateFormat 
                = new SimpleDateFormat(format);
            simpleDateFormat.setLenient(lenient);
            simpleDateFormat.parse(dateStr);
        } catch (ParseException e) {
            return false;
        }
        return true;
    }
    

    今天就聊到这,就事论事,就看我们的代码里面的小事。

    相关文章

      网友评论

        本文标题:就事论事系列-时间格式的处理

        本文链接:https://www.haomeiwen.com/subject/ocuqkhtx.html