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

在日常工作生产中,我们会经常和时间格式打交道。
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;
}

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容