在做单表动态增删改查功能时,有这样一个需求:判断传入的字符串是否符合字段要求的日期格式。

页面提交的字段值统一用 Map<String, String> 接收,后端再根据字段配置判断类型。比如日期字段约定使用 yyyy-MM-dd HH:mm:ss,就需要在保存之前检查一下。

首先想到的是用 SimpleDateFormat 来 parse 字符串,如果抛出异常则不符合,反之则符合,代码如下:

private static boolean isValidFormat(String format, String value) {
    boolean ok = true;
    DateFormat df = new SimpleDateFormat(format);
    try {
        df.parse(value);
    } catch (ParseException e) {
        ok = false;
    }
    return ok;
}

第一次测试

使用 yyyy-MM-dd HH:mm:ss 和 2019-02-31 13:12:20 进行测试:

@Test
public void test_1() {
    String format = "yyyy-MM-dd HH:mm:ss";
    String value = "2019-02-31 13:12:20";
    Assert.assertFalse(isValidFormat(format, value));
}

测试没有通过,2 月 31 日也被当成了正确的日期。

查看 DateFormat 的说明,默认情况下解析是宽松的,超出的日期会向后计算。这里的 2 月 31 日会被解析成 3 月 3 日。

在 parse 之前关闭宽松模式:

df.setLenient(false);

再次执行,测试通过。

第二次测试

把秒数改成一位:

@Test
public void test_2() {
    String format = "yyyy-MM-dd HH:mm:ss";
    String value = "2019-02-18 13:12:2";
    Assert.assertFalse(isValidFormat(format, value));
}

测试还是失败。虽然设置了非宽松模式,2019-02-18 13:12:2 仍然可以被解析。

setLenient(false) 检查的是日期和时间的取值是否合法,并没有要求秒数一定写成两位。对于这里用分隔符隔开的数字字段,解析时不会按格式字母的数量限制位数。2 秒是有效值,所以 ss 也能把它解析出来。

再加一个长度判断

这里使用的格式固定为 yyyy-MM-dd HH:mm:ss,长度是 19。既然少一位也能解析,就在解析之前先判断字符串长度:

private static boolean isValidDate(String str) {
    boolean convertSuccess = true;
    String pattern = "yyyy-MM-dd HH:mm:ss";
    if (str.length() != pattern.length()) {
        return false;
    }
    SimpleDateFormat format = new SimpleDateFormat(pattern);
    try {
        // 设置lenient为false.
        // 否则SimpleDateFormat会宽松验证
        format.setLenient(false);
        format.parse(str);
    } catch (ParseException e) {
        convertSuccess = false;
    }
    return convertSuccess;
}

长度不一致直接返回 false,长度一致再解析。这样,前面秒数少一位的字符串就不会通过了。

再试一下:

System.out.println(isValidDate("2019-02-18 13:12:20")); // true
System.out.println(isValidDate("2019-02-31 13:12:20")); // false
System.out.println(isValidDate("2019-02-18 13:12:2"));  // false
System.out.println(isValidDate("2016-02-29 13:12:20")); // true
System.out.println(isValidDate("2019-02-29 13:12:20")); // false

不过,长度判断只限制了总长度,各部分的位数并没有逐一检查。比如 2019-2-018 13:12:20 也是 19 位,仍然能通过这个方法。

单表校验时,先跳过空值,再根据字段类型调用日期校验:

for (FieldMetaInfo fieldMetaInfo : fieldMetaInfoList) {
    String value = paramMap.get(fieldMetaInfo.getField());
    if (StringUtils.isBlank(value)) {
        continue;
    }
    String title = "【" + fieldMetaInfo.getTitle() + "】";
    if (DbDataTypesUtils.isDate(fieldMetaInfo.getJavaType()) && !isValidDate(value)) {
        return new HttpProtocol(ProtocolStatus.FAILURE, "请检查日期格式是否正确" + title);
    }
}

isValidDate 本身没有处理 null,这里由调用处先过滤。日期格式不对时,把字段名称一起返回,页面上就能知道是哪一项有问题。

最后修改:2019 年 03 月 18 日
如果觉得我的文章对你有用,请随意赞赏